react-dip
Version:
Simple & declarative transition animations for React
286 lines (233 loc) • 10 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('react')) :
typeof define === 'function' && define.amd ? define(['react'], factory) :
(global.ReactDip = factory(global.React));
}(this, (function (React) { 'use strict';
var React__default = 'default' in React ? React['default'] : React;
function pick(o, fields) {
return fields.reduce(function (a, x) {
if (o.hasOwnProperty(x)) a[x] = o[x];
return a;
}, {});
}
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
var inherits = function (subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
};
var objectWithoutProperties = function (obj, keys) {
var target = {};
for (var i in obj) {
if (keys.indexOf(i) >= 0) continue;
if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;
target[i] = obj[i];
}
return target;
};
var possibleConstructorReturn = function (self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
};
/*
* animate using web animations api
*/
var webAnimations = function (element, _ref3, _ref4, duration, easing) {
var styleStart = _ref3.styleStart,
styleDestination = _ref3.styleDestination;
var xFrom = _ref4.xFrom,
yFrom = _ref4.yFrom,
scaleFromX = _ref4.scaleFromX,
scaleFromY = _ref4.scaleFromY,
xTo = _ref4.xTo,
yTo = _ref4.yTo;
if ('animate' in element) {
var returnPromise = new Promise(function (resolve) {
// $FlowFixMe
var animation = element.animate([_extends({}, styleStart, {
transform: 'translate3d(' + xFrom + 'px, ' + yFrom + 'px, 0) scale3d(' + scaleFromX + ', ' + scaleFromY + ', 1)'
}), _extends({}, styleDestination, {
transform: 'translate3d(' + xTo + 'px, ' + yTo + 'px, 0)'
})], {
duration: duration,
easing: easing,
fill: 'both'
});
animation.onfinish = function () {
resolve();
};
});
return returnPromise;
}
return new Promise(function (resolve, reject) {
return reject(new Error('web animations are not supported by this platform'));
});
};
function createAnimationDomLayer() {
if (typeof document === 'undefined') return undefined;
var animationLayer = document.createElement('div');
animationLayer.id = 'dip-animations';
animationLayer.style.cssText = 'position: absolute; top: 0; left: 0; pointer-events: none; backfaceVisibilty: hidden;';
document.body && document.body.appendChild(animationLayer);
return animationLayer;
}
function cloneElementWithDimensions(element) {
var rect = element.getBoundingClientRect();
var clone = element.cloneNode(true);
clone.style.position = 'absolute';
clone.style.width = rect.width + 'px';
clone.style.height = rect.height + 'px';
clone.style.top = window.scrollY + rect.top + 'px';
clone.style.left = window.scrollX + rect.left + 'px';
clone.style.transformOrigin = 'left top';
clone.style.margin = '0';
clone.style.backfaceVisibility = 'hidden';
return { element: clone, rect: rect };
}
function calcTransformParams(rectFrom, rectTo) {
var xFrom = rectFrom.left - rectTo.left;
var yFrom = rectFrom.top - rectTo.top;
var scaleFromX = rectFrom.width / rectTo.width;
var scaleFromY = rectFrom.height / rectTo.height;
return { xFrom: xFrom, yFrom: yFrom, xTo: 0, yTo: 0, scaleFromX: scaleFromX, scaleFromY: scaleFromY };
}
function calcStyleParams(styleStartNode, styleDestinationNode, optInCssStyles) {
var styleStart = pick(styleStartNode, optInCssStyles);
var styleDestination = pick(styleDestinationNode, optInCssStyles);
return { styleStart: styleStart, styleDestination: styleDestination };
}
var Dip = function (_Component) {
inherits(Dip, _Component);
function Dip() {
var _temp, _this, _ret;
classCallCheck(this, Dip);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = possibleConstructorReturn(this, _Component.call.apply(_Component, [this].concat(args))), _this), _this.startNodeValues = Dip.getStartNodeValues(_this.props.dipId), _this.appendAnimationRef = function (element) {
_this.animationElement = element;
var animationLayer = Dip.animationLayer;
animationLayer && animationLayer.appendChild(element);
}, _this.removeAnimationRef = function () {
_this.animationElement && _this.animationElement.parentNode && Dip.animationLayer && Dip.animationLayer.removeChild(_this.animationElement);
_this.animationElement = undefined;
}, _this.addRef = function (ref) {
_this.ref = ref;
ref != null && Dip.registerStartNode(_this.props.dipId, ref);
}, _this.logWarnings = function () {
var _this$props = _this.props,
dipId = _this$props.dipId,
element = _this$props.element,
render = _this$props.render; // eslint-disable-line no-unused-vars // eslint-disable-line no-unused-vars
/* eslint-disable */
if (dipId == null) {
console.error("please specify a `dipId`-Prop. Otherwise you won't see any or unexcpected animations. See https://github.com/mdugue/react-dip");
}
if (render != null && element != null) {
console.warn('the `element`-Prop will be ignored as you specified a `render`-Prop. See https://github.com/mdugue/react-dip');
}
/* eslint-disable */
}, _temp), possibleConstructorReturn(_this, _ret);
}
Dip.prototype.componentDidMount = function componentDidMount() {
var _this2 = this;
var ref = this.ref,
startNodeValues = this.startNodeValues;
if (ref == null || startNodeValues == null) return;
var _props = this.props,
_props$easing = _props.easing,
easing = _props$easing === undefined ? 'ease-out' : _props$easing,
_props$duration = _props.duration,
duration = _props$duration === undefined ? 200 : _props$duration,
_props$optInCssStyles = _props.optInCssStyles,
optInCssStyles = _props$optInCssStyles === undefined ? [] : _props$optInCssStyles;
var _cloneElementWithDime = cloneElementWithDimensions(ref),
animationElement = _cloneElementWithDime.element,
rectTo = _cloneElementWithDime.rect;
this.appendAnimationRef(animationElement);
var transformParams = calcTransformParams(startNodeValues.rect, rectTo);
var optionalStyleParams = calcStyleParams(startNodeValues.computedStyle, getComputedStyle(ref), optInCssStyles);
ref.style.visibility = 'hidden';
webAnimations(animationElement, optionalStyleParams, transformParams, duration, easing).then(function () {
ref.style.visibility = 'visible';
_this2.removeAnimationRef();
});
};
Dip.prototype.componentWillUnmount = function componentWillUnmount() {
Dip.unregisterStartNode(this.props.dipId, this.ref);
this.removeAnimationRef();
window.cancelAnimationFrame(this.rafId);
};
/**
* notify the user if props are not set correctly via console.log / console.warn
*/
Dip.prototype.render = function () {
var _props2 = this.props,
children = _props2.children,
_ignoreDipId_ = _props2.dipId,
_ignoreDuration_ = _props2.duration,
_props2$element = _props2.element,
Element = _props2$element === undefined ? 'div' : _props2$element,
_ignoreOptInCssStyles_ = _props2.optInCssStyles,
render = _props2.render,
rest = objectWithoutProperties(_props2, ['children', 'dipId', 'duration', 'element', 'optInCssStyles', 'render']);
this.logWarnings();
if (render != null) return render(_extends({}, rest, {
ref: this.addRef
}));
return React__default.createElement(
Element,
_extends({}, rest, { ref: this.addRef }),
children
);
};
return Dip;
}(React.Component);
Dip.animationLayer = createAnimationDomLayer();
Dip.registeredNodes = {};
Dip.unregisterStartNode = function (id, node) {
if (Dip.registeredNodes[id] === node) {
delete Dip.registeredNodes[id];
}
};
Dip.registerStartNode = function (dipId, node) {
return Dip.registeredNodes[dipId] = node;
};
Dip.getStartNodeValues = function (dipId) {
var startNode = Dip.registeredNodes[dipId];
if (!startNode) return undefined;
var computedStyle = getComputedStyle(startNode);
return {
rect: startNode.getBoundingClientRect(),
computedStyle: _extends({}, computedStyle)
};
};
return Dip;
})));
//# sourceMappingURL=react-dip.umd.js.map