264 lines
7.9 KiB
JavaScript
264 lines
7.9 KiB
JavaScript
|
|
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.amator = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
|||
|
|
var BezierEasing = require('bezier-easing')
|
|||
|
|
|
|||
|
|
// Predefined set of animations. Similar to CSS easing functions
|
|||
|
|
var animations = {
|
|||
|
|
ease: BezierEasing(0.25, 0.1, 0.25, 1),
|
|||
|
|
easeIn: BezierEasing(0.42, 0, 1, 1),
|
|||
|
|
easeOut: BezierEasing(0, 0, 0.58, 1),
|
|||
|
|
easeInOut: BezierEasing(0.42, 0, 0.58, 1),
|
|||
|
|
linear: BezierEasing(0, 0, 1, 1)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
module.exports = animate;
|
|||
|
|
module.exports.makeAggregateRaf = makeAggregateRaf;
|
|||
|
|
module.exports.sharedScheduler = makeAggregateRaf();
|
|||
|
|
|
|||
|
|
|
|||
|
|
function animate(source, target, options) {
|
|||
|
|
var start = Object.create(null)
|
|||
|
|
var diff = Object.create(null)
|
|||
|
|
options = options || {}
|
|||
|
|
// We let clients specify their own easing function
|
|||
|
|
var easing = (typeof options.easing === 'function') ? options.easing : animations[options.easing]
|
|||
|
|
|
|||
|
|
// if nothing is specified, default to ease (similar to CSS animations)
|
|||
|
|
if (!easing) {
|
|||
|
|
if (options.easing) {
|
|||
|
|
console.warn('Unknown easing function in amator: ' + options.easing);
|
|||
|
|
}
|
|||
|
|
easing = animations.ease
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var step = typeof options.step === 'function' ? options.step : noop
|
|||
|
|
var done = typeof options.done === 'function' ? options.done : noop
|
|||
|
|
|
|||
|
|
var scheduler = getScheduler(options.scheduler)
|
|||
|
|
|
|||
|
|
var keys = Object.keys(target)
|
|||
|
|
keys.forEach(function(key) {
|
|||
|
|
start[key] = source[key]
|
|||
|
|
diff[key] = target[key] - source[key]
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
var durationInMs = typeof options.duration === 'number' ? options.duration : 400
|
|||
|
|
var durationInFrames = Math.max(1, durationInMs * 0.06) // 0.06 because 60 frames pers 1,000 ms
|
|||
|
|
var previousAnimationId
|
|||
|
|
var frame = 0
|
|||
|
|
|
|||
|
|
previousAnimationId = scheduler.next(loop)
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
cancel: cancel
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function cancel() {
|
|||
|
|
scheduler.cancel(previousAnimationId)
|
|||
|
|
previousAnimationId = 0
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function loop() {
|
|||
|
|
var t = easing(frame/durationInFrames)
|
|||
|
|
frame += 1
|
|||
|
|
setValues(t)
|
|||
|
|
if (frame <= durationInFrames) {
|
|||
|
|
previousAnimationId = scheduler.next(loop)
|
|||
|
|
step(source)
|
|||
|
|
} else {
|
|||
|
|
previousAnimationId = 0
|
|||
|
|
setTimeout(function() { done(source) }, 0)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function setValues(t) {
|
|||
|
|
keys.forEach(function(key) {
|
|||
|
|
source[key] = diff[key] * t + start[key]
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function noop() { }
|
|||
|
|
|
|||
|
|
function getScheduler(scheduler) {
|
|||
|
|
if (!scheduler) {
|
|||
|
|
var canRaf = typeof window !== 'undefined' && window.requestAnimationFrame
|
|||
|
|
return canRaf ? rafScheduler() : timeoutScheduler()
|
|||
|
|
}
|
|||
|
|
if (typeof scheduler.next !== 'function') throw new Error('Scheduler is supposed to have next(cb) function')
|
|||
|
|
if (typeof scheduler.cancel !== 'function') throw new Error('Scheduler is supposed to have cancel(handle) function')
|
|||
|
|
|
|||
|
|
return scheduler
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function rafScheduler() {
|
|||
|
|
return {
|
|||
|
|
next: window.requestAnimationFrame.bind(window),
|
|||
|
|
cancel: window.cancelAnimationFrame.bind(window)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function timeoutScheduler() {
|
|||
|
|
return {
|
|||
|
|
next: function(cb) {
|
|||
|
|
return setTimeout(cb, 1000/60)
|
|||
|
|
},
|
|||
|
|
cancel: function (id) {
|
|||
|
|
return clearTimeout(id)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function makeAggregateRaf() {
|
|||
|
|
var frontBuffer = new Set();
|
|||
|
|
var backBuffer = new Set();
|
|||
|
|
var frameToken = 0;
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
next: next,
|
|||
|
|
cancel: next,
|
|||
|
|
clearAll: clearAll
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function clearAll() {
|
|||
|
|
frontBuffer.clear();
|
|||
|
|
backBuffer.clear();
|
|||
|
|
cancelAnimationFrame(frameToken);
|
|||
|
|
frameToken = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function next(callback) {
|
|||
|
|
backBuffer.add(callback);
|
|||
|
|
renderNextFrame();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function renderNextFrame() {
|
|||
|
|
if (!frameToken) frameToken = requestAnimationFrame(renderFrame);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function renderFrame() {
|
|||
|
|
frameToken = 0;
|
|||
|
|
|
|||
|
|
var t = backBuffer;
|
|||
|
|
backBuffer = frontBuffer;
|
|||
|
|
frontBuffer = t;
|
|||
|
|
|
|||
|
|
frontBuffer.forEach(function(callback) {
|
|||
|
|
callback();
|
|||
|
|
});
|
|||
|
|
frontBuffer.clear();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function cancel(callback) {
|
|||
|
|
backBuffer.delete(callback);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
},{"bezier-easing":2}],2:[function(require,module,exports){
|
|||
|
|
/**
|
|||
|
|
* https://github.com/gre/bezier-easing
|
|||
|
|
* BezierEasing - use bezier curve for transition easing function
|
|||
|
|
* by Gaëtan Renaudeau 2014 - 2015 – MIT License
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// These values are established by empiricism with tests (tradeoff: performance VS precision)
|
|||
|
|
var NEWTON_ITERATIONS = 4;
|
|||
|
|
var NEWTON_MIN_SLOPE = 0.001;
|
|||
|
|
var SUBDIVISION_PRECISION = 0.0000001;
|
|||
|
|
var SUBDIVISION_MAX_ITERATIONS = 10;
|
|||
|
|
|
|||
|
|
var kSplineTableSize = 11;
|
|||
|
|
var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);
|
|||
|
|
|
|||
|
|
var float32ArraySupported = typeof Float32Array === 'function';
|
|||
|
|
|
|||
|
|
function A (aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; }
|
|||
|
|
function B (aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; }
|
|||
|
|
function C (aA1) { return 3.0 * aA1; }
|
|||
|
|
|
|||
|
|
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
|
|||
|
|
function calcBezier (aT, aA1, aA2) { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; }
|
|||
|
|
|
|||
|
|
// Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
|
|||
|
|
function getSlope (aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); }
|
|||
|
|
|
|||
|
|
function binarySubdivide (aX, aA, aB, mX1, mX2) {
|
|||
|
|
var currentX, currentT, i = 0;
|
|||
|
|
do {
|
|||
|
|
currentT = aA + (aB - aA) / 2.0;
|
|||
|
|
currentX = calcBezier(currentT, mX1, mX2) - aX;
|
|||
|
|
if (currentX > 0.0) {
|
|||
|
|
aB = currentT;
|
|||
|
|
} else {
|
|||
|
|
aA = currentT;
|
|||
|
|
}
|
|||
|
|
} while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
|
|||
|
|
return currentT;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function newtonRaphsonIterate (aX, aGuessT, mX1, mX2) {
|
|||
|
|
for (var i = 0; i < NEWTON_ITERATIONS; ++i) {
|
|||
|
|
var currentSlope = getSlope(aGuessT, mX1, mX2);
|
|||
|
|
if (currentSlope === 0.0) {
|
|||
|
|
return aGuessT;
|
|||
|
|
}
|
|||
|
|
var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
|
|||
|
|
aGuessT -= currentX / currentSlope;
|
|||
|
|
}
|
|||
|
|
return aGuessT;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = function bezier (mX1, mY1, mX2, mY2) {
|
|||
|
|
if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) {
|
|||
|
|
throw new Error('bezier x values must be in [0, 1] range');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Precompute samples table
|
|||
|
|
var sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
|
|||
|
|
if (mX1 !== mY1 || mX2 !== mY2) {
|
|||
|
|
for (var i = 0; i < kSplineTableSize; ++i) {
|
|||
|
|
sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function getTForX (aX) {
|
|||
|
|
var intervalStart = 0.0;
|
|||
|
|
var currentSample = 1;
|
|||
|
|
var lastSample = kSplineTableSize - 1;
|
|||
|
|
|
|||
|
|
for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
|
|||
|
|
intervalStart += kSampleStepSize;
|
|||
|
|
}
|
|||
|
|
--currentSample;
|
|||
|
|
|
|||
|
|
// Interpolate to provide an initial guess for t
|
|||
|
|
var dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]);
|
|||
|
|
var guessForT = intervalStart + dist * kSampleStepSize;
|
|||
|
|
|
|||
|
|
var initialSlope = getSlope(guessForT, mX1, mX2);
|
|||
|
|
if (initialSlope >= NEWTON_MIN_SLOPE) {
|
|||
|
|
return newtonRaphsonIterate(aX, guessForT, mX1, mX2);
|
|||
|
|
} else if (initialSlope === 0.0) {
|
|||
|
|
return guessForT;
|
|||
|
|
} else {
|
|||
|
|
return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return function BezierEasing (x) {
|
|||
|
|
if (mX1 === mY1 && mX2 === mY2) {
|
|||
|
|
return x; // linear
|
|||
|
|
}
|
|||
|
|
// Because JavaScript number are imprecise, we should guarantee the extremes are right.
|
|||
|
|
if (x === 0) {
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
if (x === 1) {
|
|||
|
|
return 1;
|
|||
|
|
}
|
|||
|
|
return calcBezier(getTForX(x), mY1, mY2);
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
},{}]},{},[1])(1)
|
|||
|
|
});
|