// Copyright (c) 2017 Adobe Systems Incorporated. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import eve from './eve.js' import { Snap } from './svg.js' import { uuid } from './utils.js' let animations = {}, requestAnimFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame, requestID, isArray = Array.isArray, diff = function (a, b, A, B) { if (isArray(a)) { res = [] for (let i = 0, ii = a.length; i < ii; i++) { res[i] = diff(a[i], b, A[i], B) } return res } let dif = (A - a) / (B - b) return function (bb) { return a + dif * (bb - b) } }, timer = Date.now, sta = function (val) { let a = this if (val == null) { return a.s } let ds = a.s - val a.b += a.dur * ds a.B += a.dur * ds a.s = val }, speed = function (val) { if (val == null) { return this.spd } this.spd = val }, duration = function (val) { if (val == null) { return this.dur } this.s = (this.s * val) / this.dur this.dur = val }, stopit = function () { delete animations[this.id] this.update() eve('mina.stop.' + this.id, a) }, pause = function () { let a = this if (a.pdif) { return } delete animations[a.id] a.update() a.pdif = a.get() - a.b }, resume = function () { let a = this if (!a.pdif) { return } a.b = a.get() - a.pdif delete a.pdif animations[a.id] = a frame() }, update = function () { let a = this, res if (isArray(a.start)) { res = [] for (let j = 0, jj = a.start.length; j < jj; j++) { res[j] = +a.start[j] + (a.end[j] - a.start[j]) * a.easing(a.s) } } else { res = +a.start + (a.end - a.start) * a.easing(a.s) } a.set(res) }, frame = function (timeStamp) { // Manual invokation? if (!timeStamp) { // Frame loop stopped? if (!requestID) { // Start frame loop... requestID = requestAnimFrame(frame) } return } let len = 0 for (let i in animations) if (animations.hasOwnProperty(i)) { let a = animations[i], b = a.get(), res len++ a.s = (b - a.b) / (a.dur / a.spd) if (a.s >= 1) { delete animations[i] a.s = 1 len-- ;(function (a) { setTimeout(function () { eve('mina.finish.' + a.id, a) }) })(a) } a.update() } requestID = len ? requestAnimFrame(frame) : false }, /*\ * mina [ method ] ** * Generic animation of numbers ** - a (number) start _slave_ number - A (number) end _slave_ number - b (number) start _master_ number (start time in general case) - B (number) end _master_ number (end time in general case) - get (function) getter of _master_ number (see @mina.time) - set (function) setter of _slave_ number - easing (function) #optional easing function, default is @mina.linear = (object) animation descriptor o { o id (string) animation id, o start (number) start _slave_ number, o end (number) end _slave_ number, o b (number) start _master_ number, o s (number) animation status (0..1), o dur (number) animation duration, o spd (number) animation speed, o get (function) getter of _master_ number (see @mina.time), o set (function) setter of _slave_ number, o easing (function) easing function, default is @mina.linear, o status (function) status getter/setter, o speed (function) speed getter/setter, o duration (function) duration getter/setter, o stop (function) animation stopper o pause (function) pauses the animation o resume (function) resumes the animation o update (function) calles setter with the right value of the animation o } \*/ mina = function (a, A, b, B, get, set, easing) { let anim = { id: uuid('M'), start: a, end: A, b: b, s: 0, dur: B - b, spd: 1, get: get, set: set, easing: easing || mina.linear, status: sta, speed: speed, duration: duration, stop: stopit, pause: pause, resume: resume, update: update } animations[anim.id] = anim let len = 0, i for (i in animations) if (animations.hasOwnProperty(i)) { len++ if (len == 2) { break } } len == 1 && frame() return anim } /*\ * mina.time [ method ] ** * Returns the current time. Equivalent to: | function () { | return (new Date).getTime(); | } \*/ mina.time = timer /*\ * mina.getById [ method ] ** * Returns an animation by its id - id (string) animation's id = (object) See @mina \*/ mina.getById = function (id) { return animations[id] || null } /*\ * mina.linear [ method ] ** * Default linear easing - n (number) input 0..1 = (number) output 0..1 \*/ mina.linear = function (n) { return n } /*\ * mina.easeout [ method ] ** * Easeout easing - n (number) input 0..1 = (number) output 0..1 \*/ mina.easeout = function (n) { return Math.pow(n, 1.7) } /*\ * mina.easein [ method ] ** * Easein easing - n (number) input 0..1 = (number) output 0..1 \*/ mina.easein = function (n) { return Math.pow(n, 0.48) } /*\ * mina.easeinout [ method ] ** * Easeinout easing - n (number) input 0..1 = (number) output 0..1 \*/ mina.easeinout = function (n) { if (n == 1) { return 1 } if (n == 0) { return 0 } let q = 0.48 - n / 1.04, Q = Math.sqrt(0.1734 + q * q), x = Q - q, X = Math.pow(Math.abs(x), 1 / 3) * (x < 0 ? -1 : 1), y = -Q - q, Y = Math.pow(Math.abs(y), 1 / 3) * (y < 0 ? -1 : 1), t = X + Y + 0.5 return (1 - t) * 3 * t * t + t * t * t } /*\ * mina.backin [ method ] ** * Backin easing - n (number) input 0..1 = (number) output 0..1 \*/ mina.backin = function (n) { if (n == 1) { return 1 } let s = 1.70158 return n * n * ((s + 1) * n - s) } /*\ * mina.backout [ method ] ** * Backout easing - n (number) input 0..1 = (number) output 0..1 \*/ mina.backout = function (n) { if (n == 0) { return 0 } n = n - 1 let s = 1.70158 return n * n * ((s + 1) * n + s) + 1 } /*\ * mina.elastic [ method ] ** * Elastic easing - n (number) input 0..1 = (number) output 0..1 \*/ mina.elastic = function (n) { if (n == !!n) { return n } return ( Math.pow(2, -10 * n) * Math.sin(((n - 0.075) * (2 * Math.PI)) / 0.3) + 1 ) } /*\ * mina.bounce [ method ] ** * Bounce easing - n (number) input 0..1 = (number) output 0..1 \*/ mina.bounce = function (n) { let s = 7.5625, p = 2.75, l if (n < 1 / p) { l = s * n * n } else { if (n < 2 / p) { n -= 1.5 / p l = s * n * n + 0.75 } else { if (n < 2.5 / p) { n -= 2.25 / p l = s * n * n + 0.9375 } else { n -= 2.625 / p l = s * n * n + 0.984375 } } } return l } export default mina