Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use javascript functions to parse CSSStyle values and remove regex #115

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions examples/CSSPlugin_square.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<html>

<head>
<script type="text/javascript" src="../lib/tweenjs-NEXT.js"></script>
<script type="text/javascript" src="../src/tweenjs/plugins/CSSPlugin.js"></script>
</head>

<body>
</body>

<script>
timeMs = 0
createjs.Ticker.timingMode = createjs.Ticker.RAF;
createjs.Ticker.init();
createjs.CSSPlugin.install();
var box = document.createElement('div');
box.style.setProperty('background-color', '#008800');
box.style.setProperty('width', '100px');
box.style.setProperty('height', '100px');
box.style.setProperty('opacity', '0.4');
box.style.setProperty('margin-left', '0');
box.style.setProperty('transform', 'skew(30deg, 20deg) scale(0.5, 0.5) rotate(0.2turn)');
document.body.appendChild(box);

createjs.Tween.get(box)
.to({ "margin-left": 240, "top": 0 }, 1000, createjs.Ease["sineIn"]) // Move to (300, 200) in 1 second.
setTimeout(() => {createjs.Tween.get(box).to({ "opacity": 1.0 }, 1000, createjs.Ease["sineIn"])}, 1000) // Move to (300, 200) in 1 second.
setTimeout(() => {createjs.Tween.get(box).to({ "margin-left": 0, "top": 0 }, 1000, createjs.Ease["sineIn"])}, 1000) // Move to (300, 200) in 1 second.
setTimeout(() => {createjs.Tween.get(box).to({ "margin-left": 240, "top": 0 }, 1000, createjs.Ease["sineIn"])}, 3000) // Move to (300, 200) in 1 second.
setTimeout(() => {createjs.Tween.get(box).to({ "margin-left": 0, "top": 0 }, 1000, createjs.Ease["sineIn"])}, 5000) // Move to (300, 200) in 1 second.
setTimeout(() => {createjs.Tween.get(box).to({ "margin-left": 240, "top": 0 }, 1000, createjs.Ease["sineIn"])}, 7000) // Move to (300, 200) in 1 second.
setTimeout(() => {createjs.Tween.get(box).to({ "margin-left": 0, "top": 0 }, 1000, createjs.Ease["sineIn"])}, 9000) // Move to (300, 200) in 1 second.
setTimeout(() => {createjs.Tween.get(box).to({ "transform": "skew(1deg, 1deg) scale(1, 1) rotate(0turn)" }, 1000, createjs.Ease["sineIn"])}, 1000) // Move to (300, 200) in 1 second.
</script>

</html>
201 changes: 78 additions & 123 deletions src/tweenjs/plugins/CSSPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
* @module TweenJS
*/

this.createjs = this.createjs||{};
this.createjs = this.createjs || {};

(function() {
(function () {
"use strict";

/**
Expand Down Expand Up @@ -74,18 +74,18 @@ this.createjs = this.createjs||{};
* @constructor
**/
function CSSPlugin() {
throw("CSSPlugin cannot be instantiated.")
throw ("CSSPlugin cannot be instantiated.")
}
var s = CSSPlugin;

// static properties
// static properties
/**
* @property priority
* @protected
* @static
**/
s.priority = 100; // high priority, should read first and write last

/**
* READ-ONLY. A unique identifying string for this plugin. Used by TweenJS to ensure duplicate plugins are not installed on a tween.
* @property ID
Expand All @@ -94,22 +94,8 @@ this.createjs = this.createjs||{};
* @readonly
**/
s.ID = "CSS";

/**
* READ-ONLY.
* @property VALUE_RE
* @type {RegExp}
* @static
* @readonly
*/
s.VALUE_RE = /^(-?[\d.]+)([a-z%]*)$/; // extracts the numeric value and suffix from a single CSS value

s.TRANSFORM_VALUE_RE = /(?:^| |,)(-?[\d.]+)([a-z%]*)/g; // extracts the numeric value and suffix from comma delimited lists

s.TRANSFORM_RE = /(\w+?)\(([^)]+)\)|(?:^| )(\*)(?:$| )/g; // extracts the components of a transform





/**
* By default, CSSPlugin uses only inline styles on the target element (ie. set via the style attribute, `style` property, or `cssText`)
* to determine which properties should be tweened via CSS, and what units to use.
Expand Down Expand Up @@ -139,13 +125,13 @@ this.createjs = this.createjs||{};
s.compute = false;


// static methods
// static methods
/**
* Installs this plugin for use with TweenJS. Call this once after TweenJS is loaded to enable this plugin.
* @method install
* @static
**/
s.install = function() {
s.install = function () {
createjs.Tween._installPlugin(CSSPlugin);
};

Expand All @@ -159,31 +145,40 @@ this.createjs = this.createjs||{};
* @return {any}
* @static
**/
s.init = function(tween, prop, value) {
s.init = function (tween, prop, value) {
var data = tween.pluginData;
if (data.CSS_disabled || !(tween.target instanceof HTMLElement)) { return; }
var initVal = value||getStyle(tween.target, prop, data.CSS_compute);
if (initVal === undefined) { return; }

var initVal = value || getStyle(tween.target, prop, data.CSS_compute);


if (initVal === undefined) { return; }

tween._addPlugin(CSSPlugin);
var cssData = data.CSS || (data.CSS = {});
if (prop === "transform") {
cssData[prop] = "_t";
return parseTransform(initVal);
}

var result = s.VALUE_RE.exec(initVal);

let result = null;
if (initVal)
result = CSSNumericValue.parse(initVal);

if (result === null) {
// a string we can't handle numerically, so add it to the CSSData without a suffix.
cssData[prop] = "";
return initVal;
} else {
cssData[prop] = result[2];
return parseFloat(result[1]);
if (result.unit == 'number')
cssData[prop] = '';
else
cssData[prop] = result.unit;

return result.value;
}
};


/**
* Called when a new step is added to a tween (ie. a new "to" action is added to a tween).
* See {{#crossLink "SamplePlugin/step"}}{{/crossLink}} for more info.
Expand All @@ -193,7 +188,7 @@ this.createjs = this.createjs||{};
* @param {Object} props
* @static
**/
s.step = function(tween, step, props) {
s.step = function (tween, step, props) {
if (props.transform) {
step.props.transform = parseTransform(step.props.transform, step.prev.props.transform);
}
Expand All @@ -212,7 +207,7 @@ this.createjs = this.createjs||{};
* @return {any}
* @static
**/
s.change = function(tween, step, prop, value, ratio, end) {
s.change = function (tween, step, prop, value, ratio, end) {
var sfx = tween.pluginData.CSS[prop];
if (sfx === undefined) { return; }
if (prop === "transform") {
Expand All @@ -223,9 +218,9 @@ this.createjs = this.createjs||{};
tween.target.style[prop] = value;
return createjs.Tween.IGNORE;
};
// private helper methods:


// private helper methods:
function getStyle(target, prop, compute) {
if (compute || (compute == null && s.compute)) {
return window.getComputedStyle(target)[prop];
Expand All @@ -234,104 +229,64 @@ this.createjs = this.createjs||{};
}
}

function parseTransform(str, compare) {
var result, list = [false, str];
do {
// pull out the next "component" of the transform (ex. "translate(10px, 20px)")
result = s.TRANSFORM_RE.exec(str);
if (!result) { break; }
if (result[3] === "*") {
// reuse previous value:
list.push(compare[list.length]);
continue;
}
var component = [result[1]], compareComp = compare && compare[list.length];

// check that the operation type matches (ex. "translate" vs "rotate"):
if (compare && (!compareComp || component[0] !== compareComp[0])) {
console.log("transforms don't match: ", component[0], compareComp&&compareComp[0]);
compare=null;
} // component doesn't match

parseMulti(result[2], compareComp, component);
const parseTransform = (str, compare) => {
let list = [false];
let result = CSSStyleValue.parse("transform", str);
list.push(result);

list.push(component);
} while(true);
if (compare && compare.length === result.length) {
for (let i = 0; i < result.length; i++) {
if (compare[1][i].constructor !== result[i].constructor) {
console.log("transforms don't match: ", result[0].constructor.name, compare[1][i].constructor.name);
compare = null;
break;
} // component doesn't match
}
}

// Return false when compare is undefined/null
list[0] = !!compare;
return list;
}

// this was separated so that it can be used for other multi element styles in the future
// ex. transform-origin, border, etc.
function parseMulti(str, compare, arr) {
// TODO: add logic to deal with "0" values? Troublesome because the browser automatically appends a unit for some 0 values.
do {
// pull out the next value (ex. "20px", "12.4rad"):
var result = s.TRANSFORM_VALUE_RE.exec(str);
if (!result) { return arr; }
if (!arr) { arr = []; }
arr.push(+result[1], result[2]);

// check that the units match (ex. "px" vs "em"):
if (compare && (compare[arr.length-1] !== result[2])) { console.log("transform units don't match: ",arr[0], compare[arr.length-1], result[2]); compare=null; } // unit doesn't match
} while(true);
return (list);
}

function writeTransform(list0, list1, ratio) {
// check if we should just use the original transform strings:
if (ratio === 1) { return list1[1]; }
if (ratio === 0 || !list1[0]) { return list0[1]; }
if (ratio === 1) { return list1[1].toString(); }
if (ratio === 0 || !list1[0]) { return list0[1].toString(); }

// they match, we want to apply the ratio:
var str = "", l=list0.length, i, j, jl;
for (i=2; i<l; i++) {
var component0 = list0[i], component1 = list1[i];
str += component0[0]+"(";
for (j=1, jl=component0.length; j<jl; j+=2) {
str += component0[j]+(component1[j]-component0[j])*ratio; // value
str += component1[j+1] || component0[j+1]; // unit
if (j < jl-2) { str += ", "; }
}
str += ")";
if (i < l-1) { str += " "; }
}
return str;
}

/*

// this was part of an attempt to handle multi element css values, ex. margin="10px 10px 20px 30px"
// discarded because the browser likes to collapse values, which makes a generic solution infeasible.
// for example, margin="10px 10px 10px 10px" will collapse to just "10px"
// requires custom logic to handle each scenario.
s.MULTI_RE = /((?:^| )-?[\d.]+[a-z%]*){2,}/; // matches CSS values that consist of two or more values with suffixes
function writeMulti(arr0, arr1, ratio) {
var str = "", l=arr0.length, i;
for (i=0; i<l; i+=2) {
str += arr0[i]+(arr1[i]-arr0[i])*ratio+arr0[i+1];
if (i < l-2) { str += " "; }
}
return str;
}

// this method is really only needed for roundtrip tests.
function writeSingleTransform(list) {
var str = "", l=list.length, i, j, jl, component;
for (i=2; i<l; i++) {
component = list[i];
str += component[0]+"(";
for (j=1, jl=component.length; j<jl; j+=2) {
str += component[j]+component[j+1];
if (j < jl-2) { str += ", "; }
let startValue = list0[1];
let newValue = CSSStyleValue.parse("transform", startValue.toString());
let endValue = list1[1];

// Only works for rotate, scale and translate, skew or metrix do not have x,y,z
for (let i = 0; i < startValue.length; i++) {
switch (startValue[i].constructor) {
case CSSRotate:
newValue[i].angle.value += (endValue[i].angle.value - startValue[i].angle.value) * ratio;
// Fall down to CSSScale to set x,y,z
case CSSTranslate:
// Fall down to CSSScale to set x,y,z
case CSSScale:
newValue[i].x.value += (endValue[i].x.value - startValue[i].x.value) * ratio;
newValue[i].y.value += (endValue[i].y.value - startValue[i].y.value) * ratio;
newValue[i].z.value += (endValue[i].z.value - startValue[i].z.value) * ratio;
break;
case CSSSkew:
newValue[i].ax.value += (endValue[i].ax.value - startValue[i].ax.value) * ratio;
newValue[i].ay.value += (endValue[i].ay.value - startValue[i].ay.value) * ratio;
break;
case CSSSkewX:
newValue[i].ax.value += (endValue[i].ax.value - startValue[i].ax.value) * ratio;
break;
case CSSSkewY:
newValue[i].ay.value += (endValue[i].ay.value - startValue[i].ay.value) * ratio;
break;
}
str += ")";
if (i < l-1) { str += " "; }
}
return str;
return newValue.toString();
}
*/


createjs.CSSPlugin = s;

}());