Skip to content

Commit

Permalink
add StyleValue allowing to have multiple values and also just text (l…
Browse files Browse the repository at this point in the history
…ike none or block)

improve transforms adding more props
  • Loading branch information
soerenmeier committed Jan 17, 2024
1 parent 3c277fc commit 9fc5f5e
Show file tree
Hide file tree
Showing 12 changed files with 631 additions and 197 deletions.
227 changes: 190 additions & 37 deletions src/animation/property.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Value from '../utils/value.js';
import Value from '../values/value.js';
import StyleValue from '../values/stylevalue.js';

export default class Property {
constructor(name) {
Expand Down Expand Up @@ -68,60 +69,52 @@ export default class Property {
}

removeValue(target) {
// todo remove
return target.removeValue(this.name);
}
}

const TRANSFORM_PROPS = ['x', 'y', 'scale', 'scaleX', 'scaleY', 'rotate'];
const TRANSFORM_PROPS = {
'x': [0, 'px'],
'y': [0, 'px'],
'z': [0, 'px'],
'scale': [1, null],
'scaleX': [1, null],
'scaleY': [1, null],
'scaleZ': [1, null],
'rotate': [0, 'deg'],
'rotateX': [0, 'deg'],
'rotateY': [0, 'deg'],
'rotateZ': [0, 'deg'],
'skew': [0, 'deg'],
'skewX': [0, 'deg'],
'skewY': [0, 'deg']
};

export class Transform extends Property {
constructor(name) {
super(name);

if (name === 'x' || name === 'y') {
this._defUnit = 'px';
this._defVal = 0;
this._transformFunction = 'translate' + name.toUpperCase();
} else if (name.startsWith('scale')) {
this._defUnit = null;
this._defVal = 1;
this._transformFunction = name;
} else if (name === 'rotate') {
this._defUnit = 'deg';
this._defVal = 0;
this._transformFunction = name;
} else {
throw new Error('unknown prop ' + name);
}
this._defaultValue = new Value(...TRANSFORM_PROPS[name]);
}

parseValue(val) {
return Value.parse(val);
}

defaultValue() {
return new Value(this._defVal, this._defUnit);
return this._defaultValue.clone();
}

getValue(target) {
const val = target.getTransformValue(this._transformFunction);
if (typeof val === 'undefined' || val === null)
return this.defaultValue();

return val;
return target.getTransformValue(this.name) ?? this.defaultValue();
}

setValue(target, val) {
return target.setTransformValue(
this._transformFunction,
val.withDefaultUnit(this._defUnit)
);
target.setTransformValue(this.name, val);
}

removeValue(target) {
// todo remove
return target.removeTransformValue(this._transformFunction);
target.removeTransformValue(this.name);
}
}

Expand Down Expand Up @@ -173,6 +166,9 @@ export class TransformXY extends Property {
}

const STYLE_PROPS = {
'position': 'static',
'display': 'block',
'visibility': 'visible',
'minWidth': 'px',
'width': 'px',
'maxWidth': 'px',
Expand All @@ -183,22 +179,109 @@ const STYLE_PROPS = {
'left': 'px',
'right': 'px',
'bottom': 'px',
'opacity': null,
'opacity': [1, null],
'padding': 'px',
'margin': 'px'
}

export class StyleProp extends Property {
constructor(name) {
super(name);

this.unit = STYLE_PROPS[name];
const def = STYLE_PROPS[name];
if (Array.isArray(def)) {
this._defaultValue = new StyleValue([new Value(def[0], def[1])]);
this.unit = def[1];
} else if (def === 'px') {
this._defaultValue = new StyleValue([new Value(0, def)]);
this.unit = def;
} else {
this._defaultValue = new StyleValue(def);
this.unit = null;
}

// text|values
this.kind = null;
}

init(target, from, to) {
// this check is probably not necessary?
if (!from && !to)
throw new Error('either from or to need to be defined');

/*
if the user inputed a text value both need to be treated as text
and interpolated as classes or in reverse if they are negative values
if the user inputed a value both need to be treated as values and need
to match in length
*/

if (from?.kind === 'text' || to?.kind === 'text') {
if (from && from.kind !== 'text')
throw new Error('from is expected to be a text because to is');

if (to && to.kind !== 'text')
throw new Error('to is expected to be a text because from is');

this.kind = 'text';
this.from = from;
this.to = to;
return;
}

// get the current value
if (!from)
from = this.getValue(target);

// get the current value
if (!to)
to = this.getValue(target);

// check that both are values
if (from.kind === 'text')
from = this.defaultValue();

if (to.kind === 'text')
to = this.defaultValue();

if (from.length === 0 || to.length === 0)
throw new Error('from or to has zero values');

// either from or to needs to be 1 to work

if (from.length !== to.length && from.length !== 1 && to.length !== 1)
throw new Error('from or to need to match in length or be one');

const newLength = Math.max(from.length, to.length);
from.extendToLength(newLength);
to.extendToLength(newLength);

// now let's try to unify each value
const fromVals = [];
const toVals = [];

// now we need to make sure the values are the same
for (let i = 0; i < newLength; i++) {
const f = from.values[i];
const t = to.values[i];

const [nFrom, nTo] = target.unifyValues(this.name, f, t);
fromVals.push(nFrom);
toVals.push(nTo);
}

this.kind = 'values';
this.from = new StyleValue(fromVals);
this.to = new StyleValue(toVals);
}

parseValue(val) {
return Value.parse(val);
return StyleValue.parse(val);
}

defaultValue() {
return new Value(0, this.unit);
return this._defaultValue.clone();
}

getValue(target) {
Expand All @@ -209,7 +292,78 @@ export class StyleProp extends Property {
return val;
}

interpolate(pos) {
if (this.kind === 'text') {
return this._interpolateText(pos);
} else {
return this._interpolateValues(pos);
}
}

_interpolateText(pos) {
/*
## if both values are set
<=0 the from value will be set
>0 the to value will be set
## if only one text is set
if the to value is
the text in the to value will be set >0
the text in the to value will be removed in <=0
the text in the from value will be set <1
the text in the from value will be removed in >=1
*/
if (this.from && this.to) {
if (pos <= 0)
return this.from.clone();

return this.to.clone();
}

if (this.to) {
if (pos <= 0)
return null;

return this.to.clone();
}

if (this.from) {
if (pos >= 1)
return null;

return this.from.clone();
}

throw new Error('expected from or to');
}

_interpolateValues(pos) {
// both values should be the same length

const vals = [];

for (let i = 0; i < this.from.values.length; i++) {
const f = this.from.values[i];
const t = this.to.values[i];

const dif = t.num - f.num;

vals.push(f.cloneAdd(pos * dif));
}

return new StyleValue(vals);
}

setValue(target, val) {
if (!val)
return target.removeStyleValue(this.name);

return target.setStyleValue(this.name, val.withDefaultUnit(this.unit));
}

Expand Down Expand Up @@ -261,9 +415,8 @@ export class ClassNameProp extends Property {
});
}

if (typeof val === 'string') {
if (typeof val === 'string')
return [val];
}

throw new Error('cls only accepts strings and strings[]');
}
Expand Down Expand Up @@ -347,7 +500,7 @@ export function newProperty(prop, targetType = '') {
if (targetType !== 'dom')
throw new Error('only dom is supported as target');

if (TRANSFORM_PROPS.includes(prop))
if (prop in TRANSFORM_PROPS)
return new Transform(prop);
if (prop === 'xy')
return new TransformXY(prop);
Expand Down
Loading

0 comments on commit 9fc5f5e

Please sign in to comment.