import { mergeDiscard, mergeOver, Ωempty } from '../helper/utilities.js';This mixin defines additional attributes and functions for an artefact that uses delta functionality
import { mergeDiscard, mergeOver, Ωempty } from '../helper/utilities.js';Shared constants
import { _isArray, _keys, MULTIPLY, PC, ZERO_STR } from '../helper/shared-vars.js'Local constants
const ADD = 'add',
LONGCHECK = ['startX', 'startY', 'handleX', 'handleY', 'offsetX', 'offsetY', 'width', 'height'],
LOOP = 'loop',
NEWNUMBER = 'newNumber',
NEWSTRING = 'newString',
REMOVE = 'remove',
REVERSE = 'reverse',
SEPARATOR = ':',
SHORTCHECK = ['startY', 'handleY', 'offsetY', 'height'],
UPDATE = 'update';export default function (P = Ωempty) { const defaultAttributes = {delta - a Javascript object containing {key:value, key:value, etc} attributes.
delta object which we use to set bounds on the delta values, and instructions on what to do when those bounds are crossed.artefact.updateByDelta, artefact.reverseByDelta.artefact.set, we can also update the delta object values using artefact.setDeltaValues.// This Block artefact will animate itself across the <canvas> element
// - it will move to the right and upwards until the `delta` values are updated
// - animation will stop when the `noDeltaUpdates` flag is set
let myBlock = scrawl.makeBlock({
start: ['left', 500],
delta: {
startX: 0.5,
startY: '-0.3',
},
noDeltaUpdates: false,
});
Note that the delta and deltaConstraints objects are set up in the mixin/position.js module, not here
delta: null,
noDeltaUpdates: false,
deltaConstraints: null,
checkDeltaConstraints: false,We do not want to perform any delta checks on any uninitalized attributes - in other words: no checks on the first iteration of the Display cycle
performDeltaChecks: false,
};
P.defs = mergeOver(P.defs, defaultAttributes); const S = P.setters;delta
S.delta = function (items = Ωempty) {
if (items) this.delta = mergeDiscard(this.delta, items);
};
S.deltaConstraints = function (items = Ωempty) {
if (items) this.deltaConstraints = mergeDiscard(this.deltaConstraints, items);
};updateByDelta - this function gets called as part of every display cycle iteration, meaning that if an attribute is set to a non-zero value in the delta attribute object then those delta animations will start playing immediately.
P.updateByDelta = function () {
this.setDelta(this.delta);
if (this.checkDeltaConstraints) this.performDeltaConstraintsChecks();
return this;
};reverseByDelta - The opposite action to ‘updateByDelta’; values in the delta attribute object will be subtracted from the current value for that Scrawl-canvas object.
P.reverseByDelta = function () {
const temp = {},
delta = this.delta,
deltaKeys = _keys(delta),
deltaLen = deltaKeys.length;
let i, key, val;
for (i = 0; i < deltaLen; i++) {
key = deltaKeys[i];
val = delta[key];
if (val.substring) val = -(parseFloat(val)) + PC;
else val = -val;
temp[key] = val;
}
this.setDelta(temp);
if (this.checkDeltaConstraints) this.performDeltaConstraintsChecks();
return this;
};
P.performDeltaConstraintsChecks = function () {
const {delta, deltaConstraints} = this;
if (this.performDeltaChecks) {
const keys = _keys(deltaConstraints),
keysLen = keys.length;
let key, keyIndex, item, min, max, action, act, loopAct, arr, valIndex, val, i, fMin, fMax, fVal;
for (i = 0; i < keysLen; i++) {
key = keys[i];
item = deltaConstraints[key];
if (_isArray(item) && item.length === 3) {
[min, max, action] = deltaConstraints[key];
if (min.substring) {
keyIndex = LONGCHECK.indexOf(key);
arr = false;
valIndex = 0;
if (keyIndex >= 0) {
if (keyIndex < 2) arr = this.start;
else if (keyIndex < 4) arr = this.handle;
else if (keyIndex < 6) arr = this.offset;
else if (keyIndex < 8) arr = this.dimensions;
if (SHORTCHECK.includes(key)) valIndex = 1;
val = arr[valIndex];
}
else val = this.get(key);
fMin = parseFloat(min);
fMax = parseFloat(max);
fVal = parseFloat(val);
act = ZERO_STR;
if (fVal < fMin) {
act = action;
loopAct = 0;
}
else if (fVal > fMax) {
act = action;
loopAct = 1;
}
if (act) {
switch (act) {
case REVERSE :
delta[key] = -parseFloat(delta[key]) + PC;
this.set({
[key]: fVal + parseFloat(delta[key]) + PC,
});
break;
case LOOP :
if (loopAct) {
this.set({
[key]: fVal - (fMax - fMin) + PC,
});
}
else {
this.set({
[key]: fVal + (fMax - fMin) + PC,
});
}
break;
}
}
}
else {
val = this.get(key);
act = ZERO_STR;
if (val < min) {
act = action;
loopAct = 0;
}
else if (val > max) {
act = action;
loopAct = 1;
}
if (act) {
switch (act) {
case REVERSE :
delta[key] = -delta[key];
this.set({
[key]: val + delta[key],
});
break;
case LOOP :
if (loopAct) {
this.set({
[key]: val - (max - min),
});
}
else {
this.set({
[key]: val + (max - min),
});
}
break;
}
}
}
}
}
}
else this.performDeltaChecks = true;
};setDeltaValues - warning: EXPERIMENTAL and not currently tested!
Update the artefact’s delta object in a more intelligent way. The function accepts an object argument containing a set of instructions which will be interpreted and applied to the delat object.
// Original delta object:
artefact.delta: {
width: 50,
height: '20%',
scale: 2,
}
// Function argument object to be applied to the delta object:
{
width: 'add:20',
height: 'multiply:0.8',
scale: 'remove',
roll: 'newNumber:45',
}
// Result of applying the argument to the delta object:
artefact.delta: {
width: 70,
height: '16%',
roll: 45,
}
P.setDeltaValues = function (items = Ωempty) {
const delta = this.delta,
keys = _keys(items),
keysLen = keys.length;
let i, key, item, action, val, old
for (i = 0; i < keysLen; i++) {
key = keys[i];
item = items[key];
old = delta[key];
if (!item.includes(SEPARATOR)) {
action = item;
val = false;
}
else {
[action, val] = item.split(SEPARATOR);
}
switch (action) {
case NEWSTRING :
if (val != null) delta[key] = val;
break;
case NEWNUMBER :
if (val != null) delta[key] = parseFloat(val);
break;
case REMOVE :
delete delta[key];
break;
case UPDATE :
if (val != null) delta[key] = (old.substring) ? val : parseFloat(val);
break;
case REVERSE :
if (old.substring) val = -(parseFloat(old)) + PC;
else val = -old;
delta[key] = val;
break;
case ADD :
if (old.substring) val = (parseFloat(old) + parseFloat(val)) + PC;
else val += old;
delta[key] = val;
break;
case MULTIPLY :
if (old.substring) val = (parseFloat(old) * parseFloat(val)) + PC;
else val *= old;
delta[key] = val;
break;
}
}
return this;
};
}