2a5bac2bb3
* major JSDoc write up * removed ESLint `no-bitwise` exception, it only applies to specific functions and not the entire code * the `SVGCubicMorph` component will remove un-necessary `Z` path commands when is the case for better out of the box animation * fixed a minor disambiguation with `filterEffects` and `drop-shadow` property and its `dropshadow` interpolation function * TypeScript strong: all files are modules, easy to implement in any third party app * updated `CubicBezier` and SVGPathCommander * code cleanup
132 lines
4.7 KiB
JavaScript
132 lines
4.7 KiB
JavaScript
import defaultValues from '../objects/defaultValues';
|
|
import onStart from '../objects/onStart';
|
|
import trueColor from '../util/trueColor';
|
|
import trueDimension from '../util/trueDimension';
|
|
import numbers from '../interpolation/numbers';
|
|
import colors from '../interpolation/colors';
|
|
import { attributes, onStartAttr } from './htmlAttributesBase';
|
|
|
|
// Component Name
|
|
const ComponentName = 'htmlAttributes';
|
|
|
|
// Component Properties
|
|
const svgColors = ['fill', 'stroke', 'stop-color'];
|
|
|
|
// Component Util
|
|
/**
|
|
* Returns non-camelcase property name.
|
|
* @param {string} a the camelcase property name
|
|
* @returns {string} the non-camelcase property name
|
|
*/
|
|
function replaceUppercase(a) { return a.replace(/[A-Z]/g, '-$&').toLowerCase(); }
|
|
|
|
// Component Functions
|
|
/**
|
|
* Returns the current attribute value.
|
|
* @param {string} _ the property name
|
|
* @param {string} value the property value
|
|
* @returns {{[x:string]: string}} attribute value
|
|
*/
|
|
export function getAttr(/* tweenProp, */_, value) {
|
|
const attrStartValues = {};
|
|
Object.keys(value).forEach((attr) => {
|
|
// get the value for 'fill-opacity' not fillOpacity
|
|
// also 'width' not the internal 'width_px'
|
|
const attribute = replaceUppercase(attr).replace(/_+[a-z]+/, '');
|
|
const currentValue = this.element.getAttribute(attribute);
|
|
attrStartValues[attribute] = svgColors.includes(attribute)
|
|
? (currentValue || 'rgba(0,0,0,0)')
|
|
: (currentValue || (/opacity/i.test(attr) ? 1 : 0));
|
|
});
|
|
|
|
return attrStartValues;
|
|
}
|
|
|
|
/**
|
|
* Returns the property tween object.
|
|
* @param {string} tweenProp the property name
|
|
* @param {string} attrObj the property value
|
|
* @returns {number} the property tween object
|
|
*/
|
|
export function prepareAttr(tweenProp, attrObj) { // attr (string),attrObj (object)
|
|
const attributesObject = {};
|
|
|
|
Object.keys(attrObj).forEach((p) => {
|
|
const prop = replaceUppercase(p);
|
|
const regex = /(%|[a-z]+)$/;
|
|
const currentValue = this.element.getAttribute(prop.replace(/_+[a-z]+/, ''));
|
|
|
|
if (!svgColors.includes(prop)) {
|
|
// attributes set with unit suffixes
|
|
if (currentValue !== null && regex.test(currentValue)) {
|
|
const unit = trueDimension(currentValue).u || trueDimension(attrObj[p]).u;
|
|
const suffix = /%/.test(unit) ? '_percent' : `_${unit}`;
|
|
|
|
// most "unknown" attributes cannot register into onStart, so we manually add them
|
|
onStart[ComponentName][prop + suffix] = (tp) => {
|
|
if (this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes)) {
|
|
attributes[tp] = (elem, oneAttr, a, b, v) => {
|
|
const _p = oneAttr.replace(suffix, '');
|
|
/* eslint no-bitwise: ["error", { "allow": [">>"] }] */
|
|
elem.setAttribute(_p, ((numbers(a.v, b.v, v) * 1000 >> 0) / 1000) + b.u);
|
|
};
|
|
}
|
|
};
|
|
attributesObject[prop + suffix] = trueDimension(attrObj[p]);
|
|
} else if (!regex.test(attrObj[p]) || currentValue === null
|
|
|| (currentValue !== null && !regex.test(currentValue))) {
|
|
// most "unknown" attributes cannot register into onStart, so we manually add them
|
|
onStart[ComponentName][prop] = (tp) => {
|
|
if (this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes)) {
|
|
attributes[tp] = (elem, oneAttr, a, b, v) => {
|
|
elem.setAttribute(oneAttr, (numbers(a, b, v) * 1000 >> 0) / 1000);
|
|
};
|
|
}
|
|
};
|
|
attributesObject[prop] = parseFloat(attrObj[p]);
|
|
}
|
|
} else { // colors
|
|
// most "unknown" attributes cannot register into onStart, so we manually add them
|
|
onStart[ComponentName][prop] = (tp) => {
|
|
if (this.valuesEnd[tweenProp] && this.valuesEnd[tweenProp][tp] && !(tp in attributes)) {
|
|
attributes[tp] = (elem, oneAttr, a, b, v) => {
|
|
elem.setAttribute(oneAttr, colors(a, b, v));
|
|
};
|
|
}
|
|
};
|
|
attributesObject[prop] = trueColor(attrObj[p]) || defaultValues.htmlAttributes[p];
|
|
}
|
|
});
|
|
|
|
return attributesObject;
|
|
}
|
|
|
|
// All Component Functions
|
|
const attrFunctions = {
|
|
prepareStart: getAttr,
|
|
prepareProperty: prepareAttr,
|
|
onStart: onStartAttr,
|
|
};
|
|
|
|
// Component Full
|
|
const htmlAttributes = {
|
|
component: ComponentName,
|
|
property: 'attr',
|
|
// the Animation class will need some values to validate this Object attribute
|
|
subProperties: ['fill', 'stroke', 'stop-color', 'fill-opacity', 'stroke-opacity'],
|
|
defaultValue: {
|
|
fill: 'rgb(0,0,0)',
|
|
stroke: 'rgb(0,0,0)',
|
|
'stop-color': 'rgb(0,0,0)',
|
|
opacity: 1,
|
|
'stroke-opacity': 1,
|
|
'fill-opacity': 1, // same here
|
|
},
|
|
Interpolate: { numbers, colors },
|
|
functions: attrFunctions,
|
|
// export to global for faster execution
|
|
Util: { replaceUppercase, trueColor, trueDimension },
|
|
};
|
|
|
|
export default htmlAttributes;
|