599 lines
11 KiB
JavaScript
599 lines
11 KiB
JavaScript
// Patch IE9 and below
|
|
try {
|
|
document.createElement('DIV').style.setProperty('opacity', 0, '');
|
|
} catch (error) {
|
|
CSSStyleDeclaration.prototype.getProperty = function(a) {
|
|
return this.getAttribute(a);
|
|
};
|
|
|
|
CSSStyleDeclaration.prototype.setProperty = function(a,b) {
|
|
return this.setAttribute(a, b + '');
|
|
};
|
|
|
|
CSSStyleDeclaration.prototype.removeProperty = function(a) {
|
|
return this.removeAttribute(a);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Module Dependencies.
|
|
*/
|
|
|
|
var Emitter = require('component-emitter');
|
|
var query = require('component-query');
|
|
var after = require('after-transition');
|
|
var has3d = require('has-translate3d');
|
|
var ease = require('css-ease');
|
|
|
|
/**
|
|
* CSS Translate
|
|
*/
|
|
|
|
var translate = has3d
|
|
? ['translate3d(', ', 0)']
|
|
: ['translate(', ')'];
|
|
|
|
|
|
/**
|
|
* Export `Move`
|
|
*/
|
|
|
|
module.exports = Move;
|
|
|
|
/**
|
|
* Get computed style.
|
|
*/
|
|
|
|
var style = window.getComputedStyle
|
|
|| window.currentStyle;
|
|
|
|
/**
|
|
* Library version.
|
|
*/
|
|
|
|
Move.version = '0.5.0';
|
|
|
|
/**
|
|
* Export `ease`
|
|
*/
|
|
|
|
Move.ease = ease;
|
|
|
|
/**
|
|
* Defaults.
|
|
*
|
|
* `duration` - default duration of 500ms
|
|
*
|
|
*/
|
|
|
|
Move.defaults = {
|
|
duration: 500
|
|
};
|
|
|
|
/**
|
|
* Default element selection utilized by `move(selector)`.
|
|
*
|
|
* Override to implement your own selection, for example
|
|
* with jQuery one might write:
|
|
*
|
|
* move.select = function(selector) {
|
|
* return jQuery(selector).get(0);
|
|
* };
|
|
*
|
|
* @param {Object|String} selector
|
|
* @return {Element}
|
|
* @api public
|
|
*/
|
|
|
|
Move.select = function(selector){
|
|
if ('string' != typeof selector) return selector;
|
|
return query(selector);
|
|
};
|
|
|
|
/**
|
|
* Initialize a new `Move` with the given `el`.
|
|
*
|
|
* @param {Element} el
|
|
* @api public
|
|
*/
|
|
|
|
function Move(el) {
|
|
if (!(this instanceof Move)) return new Move(el);
|
|
if ('string' == typeof el) el = query(el);
|
|
if (!el) throw new TypeError('Move must be initialized with element or selector');
|
|
this.el = el;
|
|
this._props = {};
|
|
this._rotate = 0;
|
|
this._transitionProps = [];
|
|
this._transforms = [];
|
|
this.duration(Move.defaults.duration)
|
|
};
|
|
|
|
|
|
/**
|
|
* Inherit from `EventEmitter.prototype`.
|
|
*/
|
|
|
|
Emitter(Move.prototype);
|
|
|
|
/**
|
|
* Buffer `transform`.
|
|
*
|
|
* @param {String} transform
|
|
* @return {Move} for chaining
|
|
* @api private
|
|
*/
|
|
|
|
Move.prototype.transform = function(transform){
|
|
this._transforms.push(transform);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Skew `x` and `y`.
|
|
*
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.skew = function(x, y){
|
|
return this.transform('skew('
|
|
+ x + 'deg, '
|
|
+ (y || 0)
|
|
+ 'deg)');
|
|
};
|
|
|
|
/**
|
|
* Skew x by `n`.
|
|
*
|
|
* @param {Number} n
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.skewX = function(n){
|
|
return this.transform('skewX(' + n + 'deg)');
|
|
};
|
|
|
|
/**
|
|
* Skew y by `n`.
|
|
*
|
|
* @param {Number} n
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.skewY = function(n){
|
|
return this.transform('skewY(' + n + 'deg)');
|
|
};
|
|
|
|
/**
|
|
* Translate `x` and `y` axis.
|
|
*
|
|
* @param {Number|String} x
|
|
* @param {Number|String} y
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.translate =
|
|
Move.prototype.to = function(x, y){
|
|
return this.transform(translate.join(''
|
|
+ fixUnits(x) + ', '
|
|
+ fixUnits(y || 0)));
|
|
};
|
|
|
|
/**
|
|
* Translate on the x axis to `n`.
|
|
*
|
|
* @param {Number|String} n
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.translateX =
|
|
Move.prototype.x = function(n){
|
|
return this.transform('translateX(' + fixUnits(n) + ')');
|
|
};
|
|
|
|
/**
|
|
* Translate on the y axis to `n`.
|
|
*
|
|
* @param {Number|String} n
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.translateY =
|
|
Move.prototype.y = function(n){
|
|
return this.transform('translateY(' + fixUnits(n) + ')');
|
|
};
|
|
|
|
/**
|
|
* Scale the x and y axis by `x`, or
|
|
* individually scale `x` and `y`.
|
|
*
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.scale = function(x, y){
|
|
return this.transform('scale('
|
|
+ x + ', '
|
|
+ (y || x)
|
|
+ ')');
|
|
};
|
|
|
|
/**
|
|
* Scale x axis by `n`.
|
|
*
|
|
* @param {Number} n
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.scaleX = function(n){
|
|
return this.transform('scaleX(' + n + ')')
|
|
};
|
|
|
|
/**
|
|
* Apply a matrix transformation
|
|
*
|
|
* @param {Number} m11 A matrix coefficient
|
|
* @param {Number} m12 A matrix coefficient
|
|
* @param {Number} m21 A matrix coefficient
|
|
* @param {Number} m22 A matrix coefficient
|
|
* @param {Number} m31 A matrix coefficient
|
|
* @param {Number} m32 A matrix coefficient
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.matrix = function(m11, m12, m21, m22, m31, m32){
|
|
return this.transform('matrix(' + [m11,m12,m21,m22,m31,m32].join(',') + ')');
|
|
};
|
|
|
|
/**
|
|
* Scale y axis by `n`.
|
|
*
|
|
* @param {Number} n
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.scaleY = function(n){
|
|
return this.transform('scaleY(' + n + ')')
|
|
};
|
|
|
|
/**
|
|
* Rotate `n` degrees.
|
|
*
|
|
* @param {Number} n
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.rotate = function(n){
|
|
return this.transform('rotate(' + n + 'deg)');
|
|
};
|
|
|
|
/**
|
|
* Set transition easing function to to `fn` string.
|
|
*
|
|
* When:
|
|
*
|
|
* - null "ease" is used
|
|
* - "in" "ease-in" is used
|
|
* - "out" "ease-out" is used
|
|
* - "in-out" "ease-in-out" is used
|
|
*
|
|
* @param {String} fn
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.ease = function(fn){
|
|
fn = ease[fn] || fn || 'ease';
|
|
return this.setVendorProperty('transition-timing-function', fn);
|
|
};
|
|
|
|
/**
|
|
* Set animation properties
|
|
*
|
|
* @param {String} name
|
|
* @param {Object} props
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.animate = function(name, props){
|
|
for (var i in props){
|
|
if (props.hasOwnProperty(i)){
|
|
this.setVendorProperty('animation-' + i, props[i])
|
|
}
|
|
}
|
|
return this.setVendorProperty('animation-name', name);
|
|
}
|
|
|
|
/**
|
|
* Set duration to `n`.
|
|
*
|
|
* @param {Number|String} n
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.duration = function(n){
|
|
n = this._duration = 'string' == typeof n
|
|
? parseFloat(n) * 1000
|
|
: n;
|
|
return this.setVendorProperty('transition-duration', n + 'ms');
|
|
};
|
|
|
|
/**
|
|
* Delay the animation by `n`.
|
|
*
|
|
* @param {Number|String} n
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.delay = function(n){
|
|
n = 'string' == typeof n
|
|
? parseFloat(n) * 1000
|
|
: n;
|
|
return this.setVendorProperty('transition-delay', n + 'ms');
|
|
};
|
|
|
|
/**
|
|
* Set `prop` to `val`, deferred until `.end()` is invoked.
|
|
*
|
|
* @param {String} prop
|
|
* @param {String} val
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.setProperty = function(prop, val){
|
|
this._props[prop] = val;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Set a vendor prefixed `prop` with the given `val`.
|
|
*
|
|
* @param {String} prop
|
|
* @param {String} val
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.setVendorProperty = function(prop, val){
|
|
this.setProperty('-webkit-' + prop, val);
|
|
this.setProperty('-moz-' + prop, val);
|
|
this.setProperty('-ms-' + prop, val);
|
|
this.setProperty('-o-' + prop, val);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Set `prop` to `value`, deferred until `.end()` is invoked
|
|
* and adds the property to the list of transition props.
|
|
*
|
|
* @param {String} prop
|
|
* @param {String} val
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.set = function(prop, val){
|
|
this.transition(prop);
|
|
this._props[prop] = val;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Increment `prop` by `val`, deferred until `.end()` is invoked
|
|
* and adds the property to the list of transition props.
|
|
*
|
|
* @param {String} prop
|
|
* @param {Number} val
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.add = function(prop, val){
|
|
if (!style) return;
|
|
var self = this;
|
|
return this.on('start', function(){
|
|
var curr = parseInt(self.current(prop), 10);
|
|
self.set(prop, curr + val + 'px');
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Decrement `prop` by `val`, deferred until `.end()` is invoked
|
|
* and adds the property to the list of transition props.
|
|
*
|
|
* @param {String} prop
|
|
* @param {Number} val
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.sub = function(prop, val){
|
|
if (!style) return;
|
|
var self = this;
|
|
return this.on('start', function(){
|
|
var curr = parseInt(self.current(prop), 10);
|
|
self.set(prop, curr - val + 'px');
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Get computed or "current" value of `prop`.
|
|
*
|
|
* @param {String} prop
|
|
* @return {String}
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.current = function(prop){
|
|
return style(this.el).getPropertyValue(prop);
|
|
};
|
|
|
|
/**
|
|
* Add `prop` to the list of internal transition properties.
|
|
*
|
|
* @param {String} prop
|
|
* @return {Move} for chaining
|
|
* @api private
|
|
*/
|
|
|
|
Move.prototype.transition = function(prop){
|
|
if (!this._transitionProps.indexOf(prop)) return this;
|
|
this._transitionProps.push(prop);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Commit style properties, aka apply them to `el.style`.
|
|
*
|
|
* @return {Move} for chaining
|
|
* @see Move#end()
|
|
* @api private
|
|
*/
|
|
|
|
Move.prototype.applyProperties = function(){
|
|
for (var prop in this._props) {
|
|
this.el.style.setProperty(prop, this._props[prop], '');
|
|
}
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Re-select element via `selector`, replacing
|
|
* the current element.
|
|
*
|
|
* @param {String} selector
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.move =
|
|
Move.prototype.select = function(selector){
|
|
this.el = Move.select(selector);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Defer the given `fn` until the animation
|
|
* is complete. `fn` may be one of the following:
|
|
*
|
|
* - a function to invoke
|
|
* - an instanceof `Move` to call `.end()`
|
|
* - nothing, to return a clone of this `Move` instance for chaining
|
|
*
|
|
* @param {Function|Move} fn
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.then = function(fn){
|
|
// invoke .end()
|
|
if (fn instanceof Move) {
|
|
this.on('end', function(){
|
|
fn.end();
|
|
});
|
|
// callback
|
|
} else if ('function' == typeof fn) {
|
|
this.on('end', fn);
|
|
// chain
|
|
} else {
|
|
var clone = new Move(this.el);
|
|
clone._transforms = this._transforms.slice(0);
|
|
this.then(clone);
|
|
clone.parent = this;
|
|
return clone;
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Pop the move context.
|
|
*
|
|
* @return {Move} parent Move
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.pop = function(){
|
|
return this.parent;
|
|
};
|
|
|
|
/**
|
|
* Reset duration.
|
|
*
|
|
* @return {Move}
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.reset = function(){
|
|
this.el.style.webkitTransitionDuration =
|
|
this.el.style.mozTransitionDuration =
|
|
this.el.style.msTransitionDuration =
|
|
this.el.style.oTransitionDuration = '';
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Start animation, optionally calling `fn` when complete.
|
|
*
|
|
* @param {Function} fn
|
|
* @return {Move} for chaining
|
|
* @api public
|
|
*/
|
|
|
|
Move.prototype.end = function(fn){
|
|
var self = this;
|
|
|
|
// emit "start" event
|
|
this.emit('start');
|
|
|
|
// transforms
|
|
if (this._transforms.length) {
|
|
this.setVendorProperty('transform', this._transforms.join(' '));
|
|
}
|
|
|
|
// transition properties
|
|
this.setVendorProperty('transition-properties', this._transitionProps.join(', '));
|
|
this.applyProperties();
|
|
|
|
// callback given
|
|
if (fn) this.then(fn);
|
|
|
|
// emit "end" when complete
|
|
after.once(this.el, function(){
|
|
self.reset();
|
|
self.emit('end');
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Fix value units
|
|
*
|
|
* @param {Number|String} val
|
|
* @return {String}
|
|
* @api private
|
|
*/
|
|
|
|
function fixUnits(val) {
|
|
return 'string' === typeof val && isNaN(+val) ? val : val + 'px';
|
|
}
|