Created
June 24, 2016 18:52
-
-
Save mzgoddard/11b53c781a064842ed1cf8db0ce4aaca to your computer and use it in GitHub Desktop.
BoxArt Animated Grammar WIP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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