Skip to content

Instantly share code, notes, and snippets.

@mzgoddard
Created June 24, 2016 18:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mzgoddard/11b53c781a064842ed1cf8db0ce4aaca to your computer and use it in GitHub Desktop.
Save mzgoddard/11b53c781a064842ed1cf8db0ce4aaca to your computer and use it in GitHub Desktop.
BoxArt Animated Grammar WIP
function buildConcat(setBuild, space) {
if (setBuild.length === 0) {
return function(t) {
return '';
};
}
else if (setBuild.length === 1) {
var a = setBuild[0];
return function(t) {
return a(t);
};
}
else if (setBuild.length === 2) {
var a = setBuild[0];
var b = setBuild[1];
return function(t) {
return a(t) + space + b(t);
};
}
else if (setBuild.length === 3) {
var a = setBuild[0];
var b = setBuild[1];
var c = setBuild[2];
return function(t) {
return a(t) + space + b(t) + space + c(t);
};
}
else if (setBuild.length === 4) {
var a = setBuild[0];
var b = setBuild[1];
var c = setBuild[2];
var d = setBuild[3];
return function(t) {
return a(t) + space + b(t) + space + c(t) + space + d(t);
};
}
return function(t) {
var value = '';
for (let i = 0; i < setBuild.length - 1; i++) {
value += setBuild[i](t) + space;
}
if (setBuild.length > 0) {
value += setBuild[setBuild.length - 1](t);
}
return value;
};
}
function buildSet(set, next, input) {
var build = [];
if (next) {
for (let i = 0; i < set.length; i++) {
var item = set[i];
var itemNext = null;
if (item.setKey) {
for (let j = 0; j < next.length; j++) {
if (next[j].setKey === item.setKey) {
itemNext = next[j];
break;
}
}
}
else {
itemNext = next[i];
}
build[i] = item(itemNext, input);
}
}
else {
for (let i = 0; i < set.length; i++) {
build[i] = set[i](null, input);
}
}
var lerp = function(t, target, state) {
for (let i = 0; i < build.length; i++) {
build[i](t, target, state);
}
};
lerp.replace = function(target, state) {
for (let i = 0; i < build.length; i++) {
build[i].replace(target, state);
}
return state;
};
lerp.restore = function(target, state) {
for (let i = 0; i < build.length; i++) {
build[i].restore(target, state);
}
return target;
};
lerp.setBuild = build;
return lerp;
}
function buildObjectItem(key, action, next, input) {
var itemNext;
if (next) {
itemNext = next[key];
}
var lerpItem = action(next, input);
var lerp = function(t, target, state) {
var itemTarget = target[key];
var itemState = state[key];
target[key] = lerpItem(t, itemTarget, itemState);
};
lerp.replace = function(target, state) {
return state[key] = lerpItem.replace(target[key], state[key]);
};
lerp.restore = function(target, state) {
return target[key] = lerpItem.restore(target[key], state[key]);
};
return lerp;
}
function buildObject(obj, itemBuilder, next, input) {
var keys = Object.keys(obj);
var build = [];
if (next) {
for (let i = 0; i < keys.length; i++) {
var key = keys[i];
build[i] = itemBuilder(key, obj[key], next[key], input);
}
}
else {
for (let i = 0; i < keys.length; i++) {
var key = keys[i];
build[i] = itemBuilder(key, obj[key], null, input);
}
}
var lerp = function(t, target, state) {
for (let i = 0; i < keys.length; i++) {
build[i](t, target, state);
}
};
lerp.replace = function(target, state) {
for (let i = 0; i < keys.length; i++) {
build[i].replace(target, state);
}
return state;
};
lerp.restore = function(target, state) {
for (let i = 0; i < keys.length; i++) {
build[i].restore(target, state);
}
return target;
};
return lerp;
}
function buildState(key, defaultConstructor, valueLerp) {
var lerp = function(t, target, state) {
if (!state[key]) {
state[key] = defaultConstructor();
}
var keyState = state[key];
return valueLerp(t, target, keyState);
};
lerp.replace = function(target, state) {
if (!state[key]) {
state[key] = defaultConstructor();
}
var keyState = state[key];
valueLerp.replace(target, keyState);
return state;
};
lerp.restore = function(target, state) {
if (!state[key]) {
state[key] = defaultConstructor();
}
var keyState = state[key];
valueLerp.restore(target, keyState);
return target;
};
return lerp;
}
function buildDeref(key, defaultConstructor, valueConstructor) {
var constructor = function(next, input) {
// var keyNext;
// if (next && next[key]) {
// keyNext = next[key];
// }
var valueLerp = valueConstructor(next, input);
var lerp = function(t, target, state) {
var keyTarget = target[key];
if (!state[key]) {
state[key] = defaultConstructor();
}
var keyState = state[key];
return valueLerp(t, keyTarget, keyState);
};
lerp.replace = function(target, state) {
var keyTarget = target[key];
if (!state[key]) {
state[key] = defaultConstructor();
}
var keyState = state[key];
valueLerp.replace(keyTarget, keyState);
return state;
};
lerp.restore = function(target, state) {
var keyTarget = target[key];
if (!state[key]) {
state[key] = defaultConstructor();
}
var keyState = state[key];
valueLerp.restore(keyTarget, keyState);
return target;
};
return lerp;
};
constructor.setKey = key;
constructor.valueConstructor = valueConstructor;
return constructor;
}
function nextValue(next, input) {
if (typeof next.value === 'number') {
return next.value;
}
if (next.compileInput) {
var inputNext = next.action(input);
if (inputNext.value) {
return inputNext.value;
}
if (inputNext.compileNext) {
return inputNext(inputNext)(0);
}
return inputNext(0);
}
if (next.compileNext) {
return next(next)(0);
}
return next(0);
}
function value(start, suffix) {
if (suffix) {
var lerpConstructor = function(next, input) {
var end = next ? nextValue(next, input) : start;
var lerp = function(t) {
return (end - start) * t + start + suffix;
};
lerp.replace = function(target, state) {
return target;
};
lerp.restore = function(target, state) {
return state;
};
return lerp;
};
lerpConstructor.compileNext = true;
lerpConstructor.value = start;
return lerpConstructor;
}
else {
var lerpConstructor = function(next, input) {
var end = next ? nextValue(next, input) : start;
var lerp = function(t) {
return (end - start) * t + start;
};
lerp.replace = function(target, state) {
return target;
};
lerp.restore = function(target, state) {
return state;
};
return lerp;
};
lerpConstructor.compileNext = true;
lerpConstructor.value = start;
return lerpConstructor;
}
}
function px(v) {
return value(v, 'px');
}
function em(v) {
return value(v, 'em');
}
function percent(v) {
return value(v, '%');
}
function rad(v) {
return value(v, 'rad');
}
function deg(v) {
return value(v, 'deg');
}
value.px = px;
value.em = em;
value.percent = percent;
value.rad = rad;
var a = value(0, 'px')(function() {return 100;});
var b = value(0, 'px')(value(100, 'px'));
function ratio(start) {
var constructor = function(next, input) {
var end = next ? nextValue(next, input) : start;
var lerp = function(t) {
return ((end - start) * t + start) / end;
};
lerp.replace = function(target, state) {
return target;
};
lerp.restore = function(target, state) {
return state;
};
return lerp;
};
constructor.compileNext = true;
constructor.value = start;
return constructor;
}
function ease(lerpConstructor, tLerp) {
var constructor = function(next, input) {
var lerpValue = lerpConstructor(next, input);
var lerp = function(t, target, state) {
return lerpValue(tLerp(t), target, state);
};
lerp.replace = function(target, state) {
return target;
};
lerp.restore = function(target, state) {
return state;
};
return lerp;
};
constructor.compileNext = lerpConstructor.compileNext;
constructor.compileInput = lerpConstructor.compileInput;
constructor.value = lerpConstructor.value;
constructor.action = lerpConstructor.action;
return constructor;
}
function input(fn) {
var constructor = function(next, input) {
return fn(input)(next);
};
constructor.compileInput = true;
constructor.action = fn;
return constructor;
}
function func(name, args) {
var properName = name.replace(/#.*$/, '');
var constructor = function(next, input) {
var setLerp = buildSet(args, next && next.set, input);
var setBuild = setLerp.setBuild;
var innerConcat = buildConcat(setBuild, ', ');
var lerp = function(t) {
return properName + '(' + innerConcat(t) + ')';
};
lerp.replace = function(target, state) {
return target;
};
lerp.restore = function(target, state) {
return state;
};
return lerp;
};
constructor.setKey = name;
constructor.set = args;
return constructor;
}
var t1 = func('translate', [value(0, 'px'), value(1, 'px')]);
var t2 = func('translate', [value(1, 'px'), value(2, 'px')]);
var t3 = func('translate3d', [px(0), px(0), px(0)]);
function bench(n, fn) {
var start = Date.now();
for (let i = 0; i < n; i++) {
fn();
}
return [(Date.now() - start) / 1000, n / ((Date.now() - start) / 1000)];
}
function concat(set) {
var constructor = function(next, input) {
var setLerp = buildSet(set, next && next.set, input);
var setBuild = setLerp.setBuild;
var lerp = buildConcat(setBuild, ' ');
lerp.replace = function(target, state) {
return target;
};
lerp.restore = function(target, state) {
return state;
};
return lerp;
};
constructor.set = set;
return constructor;
}
var c1 = concat([t1]);
var c2 = concat([t2]);
var c = c1(c2);
var t = t1(t2);
function keyframes(duration, frames) {
if (Array.isArray(duration)) {
frames = duration;
if (frames.length) {
duration = frames[frames.length - 1].position;
}
else {
duration = 0;
}
}
var constructor = function(next, input) {
var build = [];
if (frames.length > 0) {
var frame = frames[frames.length - 1];
build.push(frame(next, input));
build[0].position = frame.position / duration;
build[0].positionDiff = duration - frame.position;
}
for (let i = frames.length - 2; i >= 0; i--) {
var frame = frames[i];
var nextFrame = frames[i + 1];
var builtFrame = frame(nextFrame, input);
builtFrame.position = frame.position / duration;
builtFrame.nextPosition = nextFrame.position / duration;
builtFrame.positionDiff = builtFrame.nextPosition - builtFrame.position;
build.push(builtFrame);
}
var lerp = function(t, target, state) {
if (build[0].position < t) {
var frame = build[0];
return frame(0, target, state);
}
for (let i = 1; i < build.length; i++) {
var frame = build[i];
if (frame.position < t) {
var frameT = (t - frame.position) * frame.positionDiff;
return frame(frameT, target, state);
}
}
var frame = build[build.length - 1];
return frame(0, target, state);
};
lerp.replace = function(target, state) {
return build[0].replace(target, state);
};
lerp.restore = function(target, state) {
return build[0].restore(target, state);
};
return lerp;
};
return constructor;
}
function frame(pos, action) {
var constructor = function(next, input) {
return action(next ? next.action : null, input);
};
constructor.position = pos;
constructor.action = action;
return constructor;
}
var af = frame(0, value(0, 'px'));
var bf = frame(1, value(100, 'px'));
var k = keyframes([af, bf])();
function effect(name, fn) {
var constructor = function(next, input) {
return fn(next, input);
};
constructor.setKey = name;
constructor.set = fn.set;
constructor.object = fn.object;
constructor.effectName = name;
return constructor;
}
function effectSet(effects) {
var constructor = function(next, input) {
return buildSet(effects, next && next.set, input);
};
constructor.set = effects;
return constructor;
}
function styles(obj) {
var constructor = buildDeref('style', function() {
return {};
}, function(next, input) {
return buildObject(obj, buildObjectItem, next && next.object, input);
});
constructor.style = obj;
constructor.object = obj;
return constructor;
}
function buildObjectAttribute(key, action, next, input) {
var itemNext;
if (next) {
itemNext = next[key];
}
var lerpItem = action(next, input);
var lerp = function(t, target, state) {
var itemTarget = target.getAttribute(key);
var itemState = state[key];
target.setAttribute(key, lerpItem(t, itemTarget, itemState));
};
lerp.replace = function(target, state) {
return state[key] = lerpItem.replace(target.getAttribute(key), state[key]);
};
lerp.restore = function(target, state) {
return target.setAttribute(key, lerpItem.restore(target.getAttribute(key), state[key]));
};
return lerp;
}
function attributes(obj) {
var constructor = function(next, input) {
var attributesNext;
if (next && next.attributes) {
attributesNext = next.attributes;
}
var lerp = buildObject(obj, buildObjectAttribute, attributesNext, input);
return buildState('attributes', function() {return {};}, lerp);
};
constructor.setKey = 'attributes';
constructor.attributes = obj;
return constructor;
}
function properties(obj) {
var constructor = function(next, input) {
var propertiesNext;
if (next && next.properties) {
propertiesNext = next.properties;
}
var lerp = buildObject(obj, buildObjectItem, propertiesNext, input);
return buildState('properties', function() {return {};}, lerp);
};
constructor.setKey = 'properties';
constructor.properties = obj;
return constructor;
}
function transform(set) {
var cc = concat(set);
return effect('transform', styles({
MozTransform: cc,
MsTransform: cc,
webkitTransform: cc,
transform: cc,
}));
}
function rect(obj) {
return effect('rect', transform([
func('translate3d', [px(obj.left), px(obj.top), value(0)]),
func('rotateZ', [rad(obj.angle)]),
func('scale', [ratio(obj.width), ratio(obj.height)]),
]));
}
var r1 = rect({left: 0, top: 0, width: 10, height: 10, angle: 0});
var r2 = rect({left: 10, top: 0, width: 10, height: 10, angle: 0});
var r = r1(r2);
var sWire = {style: {}};
var s1 = styles({
left: keyframes([
frame(0, value(0, 'px')),
frame(1, value(100, 'px')),
]),
})()
var s2 = keyframes([
frame(0, styles({left: value(0, 'px')})),
frame(1, styles({left: value(100, 'px')}))
])()
var esWire = {style: {}};
var es = effectSet([styles({left: keyframes([
frame(0, value(0, 'px')),
frame(1, value(100, 'px')),
])})])(null);
var es2 = keyframes([
frame(0, effectSet([styles({left: value(0, 'px')})])),
frame(1, effectSet([styles({left: value(100, 'px')})])),
])();
var es3 = effectSet([
keyframes([
frame(0, styles({left: value(0, 'px')})),
frame(1, styles({left: value(100, 'px')})),
]),
])();
function wire(name, effects) {
var constructor = buildDeref(name, function() {
return [];
}, function(next, input) {
return buildSet(effects, next && next.set, input);
});
constructor.wireName = name;
constructor.set = effects;
constructor.effects = effects;
return constructor;
}
var wWire = {box: {style: {}}};
var translate = func.bind(null, 'translate');
var w1 = wire('box', [
keyframes([
frame(0, styles({
transform: translate([value(0, 'px'), value(0, 'px')]),
})),
frame(1, styles({
transform: translate([value(100, 'px'), value(100, 'px')]),
})),
]),
])();
var w2 = keyframes([
frame(0, wire('box', [
styles({
transform: translate([value(0, 'px'), value(0, 'px')]),
}),
])),
frame(1, wire('box', [
styles({
transform: translate([value(100, 'px'), value(100, 'px')]),
}),
])),
])();
var w3 = wire('box', [
styles({
transform: keyframes([
frame(0, translate([value(0, 'px'), value(0, 'px')])),
frame(1, translate([value(100, 'px'), value(100, 'px')])),
]),
}),
])();
var w4 = wire('box', [
styles({
transform: translate([
keyframes([
frame(0, value(0, 'px')),
frame(1, value(100, 'px')),
]),
keyframes([
frame(0, value(0, 'px')),
frame(1, value(100, 'px')),
]),
]),
}),
])();
function metadata(meta) {
var constructor = function(next, input) {
return function() {};
};
constructor.type = 'metadata';
constructor.data = meta;
}
function clip(defs) {
var metadata;
defs = defs.reduce(function(carry, def) {
if (def.type === 'metadata') {
metadata = def;
return carry;
}
carry.push(def);
return carry;
}, []);
var constructor = function(next, input) {
return buildSet(defs, next && next.set, input);
};
clip.metadata = metadata ? metadata.data : {};
clip.set = defs;
return constructor;
}
export default {
input,
value,
ease,
func,
concat,
keyframes,
frame,
styles,
attributes,
properties,
effect,
effectSet,
meta: metadata,
clip,
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment