mm.mfx.js (mass effect) minimum version
mofmof.js の mm.mfx.js を単体で動作可能に切り出してみました。
280行です。ES5対応のブラウザ, Titanium, NGCore, node.js などで動作します。
// mfx.js (minimum version) // string.js --- (function(global) { // @param GlobalObject: // @see http://code.google.com/p/mofmof-js/wiki/API String.prototype.f || (String.prototype.f = String_f); // --- local vars --- var _StringForamt = /@@/g; // String#f - placeholder( "@@" ) replacement function String_f(/* var_args */) { // @param Mix: values // @return String: "formatted string" // @see: String#sprintf var i = 0, args = arguments; return this.replace(_StringForamt, function() { return args[i++]; }); } })(this); // math.easing.js --- // Math.easing - easing functions Math.easing = { linear: "(c*t/d+b)", // linear(t,b,c,d) // t:Number - current time (from 0) // b:Number - beginning value // c:Number - change in value(delta)(end - begin) // d:Number - duration(unit: ms) // Quad --- inquad: "(q1=t/d,c*q1*q1+b)", outquad: "(q1=t/d,-c*q1*(q1-2)+b)", inoutquad: "(q1=t/(d*0.5),q1<1?c*0.5*q1*q1+b:-c*0.5*((--q1)*(q1-2)-1)+b)", // Cubic --- incubic: "(q1=t/d,c*q1*q1*q1+b)", outcubic: "(q1=t/d-1,c*(q1*q1*q1+1)+b)", inoutcubic: "(q1=t/(d*0.5),q1<1?c*0.5*q1*q1*q1+b:c*0.5*((q1-=2)*q1*q1+2)+b)", outincubic: "(q1=t*2,q2=c*0.5,t<d*0.5?(q3=q1/d-1,q2*(q3*q3*q3+1)+b)" + ":(q3=(q1-d)/d,q2*q3*q3*q3+b+q2))", // Quart --- inquart: "(q1=t/d,c*q1*q1*q1*q1+b)", outquart: "(q1=t/d-1,-c*(q1*q1*q1*q1-1)+b)", inoutquart: "(q1=t/(d*0.5),q1<1?c*0.5*q1*q1*q1*q1+b" + ":-c*0.5*((q1-=2)*q1*q1*q1-2)+b)", outinquart: "(q1=t*2,q2=c*0.5,t<d*0.5?(q3=q1/d-1,-q2*(q3*q3*q3*q3-1)+b)" + ":(q4=q1-d,q3=q4/d,q2*q3*q3*q3*q3+b+q2))", // Back --- inback: "(q1=t/d,q2=1.70158,c*q1*q1*((q2+1)*q1-q2)+b)", outback: "(q1=t/d-1,q2=1.70158,c*(q1*q1*((q2+1)*q1+q2)+1)+b)", inoutback: "(q1=t/(d*0.5),q2=1.525,q3=1.70158," + "q1<1?(c*0.5*(q1*q1*(((q3*=q2)+1)*q1-q3))+b)" + ":(c*0.5*((q1-=2)*q1*(((q3*=q2)+1)*q1+q3)+2)+b))", outinback: "(q1=t*2,q2=c*0.5," + "t<d*0.5?(q3=q1/d-1,q4=1.70158,q2*(q3*q3*((q4+1)*q3+q4)+1)+b)" + ":(q3=(q1-d)/d,q4=1.70158,q2*q3*q3*((q4+1)*q3-q4)+b+q2))", // Bounce --- inbounce: "(q1=(d-t)/d,q2=7.5625,q3=2.75,c-(q1<(1/q3)?(c*(q2*q1*q1)+0)" + ":(q1<(2/q3))?(c*(q2*(q1-=(1.5/q3))*q1+.75)+0):q1<(2.5/q3)" + "?(c*(q2*(q1-=(2.25/q3))*q1+.9375)+0)" + ":(c*(q2*(q1-=(2.625/q3))*q1+.984375)+0))+b)", outbounce: "(q1=t/d,q2=7.5625,q3=2.75,q1<(1/q3)?(c*(q2*q1*q1)+b)" + ":(q1<(2/q3))?(c*(q2*(q1-=(1.5/q3))*q1+.75)+b):q1<(2.5/q3)" + "?(c*(q2*(q1-=(2.25/q3))*q1+.9375)+b)" + ":(c*(q2*(q1-=(2.625/q3))*q1+.984375)+b))" }; // create easing functions (function() { for (var key in Math.easing) { Math[key] = new Function("t,b,c,d, q1,q2,q3,q4", // q1~q4 is tmp args "return " + Math.easing[key]); } })(); // core.js --- window.mm || (window.mm = {}); // mm.fx.js --- (function(global, mm) { mm.fx = {}; mm.fx.requestAnimationFrame = mm_fx_requestAnimationFrame; mm.mfx = mm_mfx; mm.mfx.kill = mm_mfx_kill; var _uniqueCount = 0, _mfxTicket = [], _mfxKillingTicket = [], _requestAnimationFrame = global.requestAnimationFrame || global.oRequestAnimationFrame || global.msRequestAnimationFrame || global.mozRequestAnimationFrame || global.webkitRequestAnimationFrame; // mm.fx.requestAnimationFrame function mm_fx_requestAnimationFrame(tick, // @param Function: delay) { // @param Number(= 4): setTimeout delay, msec // @return Mix: return _requestAnimationFrame ? _requestAnimationFrame(tick) : setTimeout(tick, delay == null ? 4 : delay); } // mm.mfx.kill - killing animation function mm_mfx_kill(killingTicket) { // @param Number: mm.mfx() result killingTicket // addif if (_mfxKillingTicket.indexOf(killingTicket) < 0) { _mfxKillingTicket.push(killingTicket); } } // mm.mfx - mass effect function mm_mfx(param, // @param Hash: { id: spec, ... } tickCallback, // @param Function: tick callback(result, param) stateCallback) { // @param Function(= null): change state callback(build, param) // @return Number: killingTicket for killing animation function tick() { // @lookup: param, ticket, past, mfxdb, // tickCallback, stateCallback var now = Date.now(), curt, spec, result = {}, update = false, i = 0, iz = mfxdb.length, j, jz, remain = iz; if (_mfxKillingTicket.length && _mfxKillingTicket.indexOf(ticket) >= 0) { kill(); } for (; i < iz; ++i) { curt = null; spec = mfxdb[i]; switch (spec.state) { case COMPLETED: --remain; break; case WAIT: spec.past || (spec.past = now); if (now >= spec.past + spec.delay) { spec.state = RUNNING; curt = spec.a.concat(); update = true; } break; case RUNNING: update = true; if (now >= spec.past + spec.delay + spec.time) { // timeout? spec.state = COMPLETED; // change state(RUNNING -> COMPLETED) curt = spec.b.concat(); --remain; } else { for (curt = [], j = 0, jz = spec.a.length; j < jz; ++j) { curt.push(spec.easing(now - spec.past - spec.delay, // -> current time spec.a[j], spec.b[j] - spec.a[j], spec.time)); } spec.c = curt.concat(); } break; case FREEZE: update = true; spec.state = COMPLETED; // change state(FREEZE -> COMPLETED) curt = (spec.freeze ? spec.c : spec.b).concat(); --remain; } if (curt !== null) { if (spec.isArray) { result[spec.id] = []; for (j = 0, jz = spec.a.length; j < jz; ++j) { result[spec.id].push(curt[j]); } } else { result[spec.id] = curt[0]; } } } if (update) { // tickCallback() -> false is killing animation if (tickCallback(result, now - spec.past, param) === false) { kill(); } } if (remain > 0) { _requestAnimationFrame ? _requestAnimationFrame(tick) : setTimeout(tick, 4); } else { // removeif var index = _mfxTicket.indexOf(ticket); if (index >= 0) { _mfxTicket.splice(index, 1); } stateCallback && stateCallback(false, param); // destruct } } function kill() { // @lookup: _mfxKillingTicket, ticket, mfxdb var i = 0, iz = mfxdb.length; // removeif var index = _mfxKillingTicket.indexOf(ticket); if (index >= 0) { _mfxKillingTicket.splice(index, 1); } for (i = 0; i < iz; ++i) { if (mfxdb[i].state !== COMPLETED) { mfxdb[i].state = FREEZE; } } } function _buildMassEffectDB(param) { // @lookup: function ora() { var undef, args = arguments, i = 0, iz = args.length; for (; i < iz; ++i) { if (args[i] !== undef) { return args[i]; } } return args[iz - 1]; // last } var rv = [], id, spec, remain = {}, defs = { _time: 200, _delay: 0, _easing: Math.inoutquad, _freeze: false }; // --- pickup reserved words --- for (id in param) { id in defs ? (defs[id] = param[id]) : (remain[id] = param[id]); } // --- build mfx db --- // mfxdb = [spec, ...] // spec = { id, a, b, c, time, delay, easing, freeze, isArray, state, past } for (id in remain) { spec = remain[id]; if (spec.a === void 0 || spec.b === void 0) { ; // ignore id } else { rv.push({ id: id, // id. eg: "x", "y" a: Array.isArray(spec.a) ? spec.a : [spec.a], // point A b: Array.isArray(spec.b) ? spec.b : [spec.b], // point B c: Array.isArray(spec.a) ? spec.a : [spec.a], // Current value time: ora(spec.time, spec.t, defs._time), delay: ora(spec.delay, spec.d, defs._delay), easing: ora(spec.easing, spec.e, defs._easing), freeze: ora(spec.freeze, spec.f, defs._freeze), isArray: Array.isArray(spec.a), state: WAIT, past: 0 }); } } return rv; } var ticket = ++_uniqueCount, mfxdb, WAIT = 0, RUNNING = 1, FREEZE = 2, COMPLETED = 4; mfxdb = _buildMassEffectDB(param); // addif if (_mfxTicket.indexOf(ticket) < 0) { _mfxTicket.push(ticket); } stateCallback && stateCallback(true, param); // build _requestAnimationFrame ? _requestAnimationFrame(tick) : setTimeout(tick, 4); return ticket; } })(this, this.mm || this);