UNPKG

react-flip-toolkit

Version:

Configurable FLIP animation helpers for React

252 lines (203 loc) 9.7 kB
"use strict"; exports.__esModule = true; exports.animateMove = exports.getFlippedElementPositions = undefined; 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 _tween = require("popmotion/animations/tween"); var _tween2 = _interopRequireDefault(_tween); var _easing = require("popmotion/easing"); var popmotionEasing = _interopRequireWildcard(_easing); var _parallel = require("popmotion/compositors/parallel"); var _parallel2 = _interopRequireDefault(_parallel); var _rematrix = require("rematrix"); var Rematrix = _interopRequireWildcard(_rematrix); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var getInvertedChildren = function getInvertedChildren(element, id) { return [].slice.call(element.querySelectorAll("*[data-inverse-flip-id=\"" + id + "\"]")); }; var passesComponentFilter = function passesComponentFilter(flipFilters, flipId) { if (typeof flipFilters === "string") { flipFilters = flipFilters.split(",").filter(function (x) { return x; }); if (!flipFilters.some(function (f) { return f === flipId; })) { return false; } } return true; }; var applyStyles = function applyStyles(element, _ref) { var matrix = _ref.matrix, opacity = _ref.opacity; // because matrix3d screws with opacity animations in Chrome (why??) element.style.transform = "matrix(" + [matrix[0], matrix[1], matrix[4], matrix[5], matrix[12], matrix[13]].join(", ") + ")"; element.style.opacity = opacity; }; var shouldApplyTransform = function shouldApplyTransform(element, flipStartId, flipEndId) { if (element.dataset.flipComponentIdFilter && !passesComponentFilter(element.dataset.flipComponentIdFilter, flipStartId) && !passesComponentFilter(element.dataset.flipComponentIdFilter, flipEndId)) { return false; } return true; }; // if we're scaling an element and we have element children with data-inverse-flip-ids, // apply the inverse of the transforms so that the children don't distort var invertTransformsForChildren = function invertTransformsForChildren(childElements, matrix) { var _ref2 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, flipStartId = _ref2.flipStartId, flipEndId = _ref2.flipEndId; childElements.forEach(function (child) { if (!shouldApplyTransform(child, flipStartId, flipEndId)) return; var translateX = matrix[12]; var translateY = matrix[13]; var scaleX = matrix[0]; var scaleY = matrix[5]; var inverseVals = {}; if (child.dataset.translateX) inverseVals.translateX = -translateX / scaleX; if (child.dataset.translateY) inverseVals.translateY = -translateY / scaleY; if (child.dataset.scaleX) inverseVals.scaleX = 1 / scaleX; if (child.dataset.scaleY) inverseVals.scaleY = 1 / scaleY; child.style.transform = "translate(" + inverseVals.translateX + "px, " + inverseVals.translateY + "px) scale(" + inverseVals.scaleX + ", " + inverseVals.scaleY + ")"; }); }; var getFlippedElementPositions = exports.getFlippedElementPositions = function getFlippedElementPositions(element) { return [].slice.apply(element.querySelectorAll("*[data-flip-id]")).map(function (child) { return [child.dataset.flipId, { rect: child.getBoundingClientRect(), opacity: parseFloat(window.getComputedStyle(child).opacity), flipComponentId: child.dataset.flipComponentId }]; }).reduce(function (acc, curr) { var _extends2; return _extends({}, acc, (_extends2 = {}, _extends2[curr[0]] = curr[1], _extends2)); }, {}); }; var rectInViewport = function rectInViewport(_ref3) { var top = _ref3.top, bottom = _ref3.bottom, left = _ref3.left, right = _ref3.right; return bottom > 0 && top < window.innerHeight && right > 0 && left < window.innerWidth; }; var animateMove = exports.animateMove = function animateMove(_ref4) { var _ref4$inProgressAnima = _ref4.inProgressAnimations, inProgressAnimations = _ref4$inProgressAnima === undefined ? {} : _ref4$inProgressAnima, _ref4$cachedFlipChild = _ref4.cachedFlipChildrenPositions, cachedFlipChildrenPositions = _ref4$cachedFlipChild === undefined ? {} : _ref4$cachedFlipChild, _ref4$flipCallbacks = _ref4.flipCallbacks, flipCallbacks = _ref4$flipCallbacks === undefined ? {} : _ref4$flipCallbacks, containerEl = _ref4.containerEl, duration = _ref4.duration, ease = _ref4.ease; var body = document.querySelector("body"); var newFlipChildrenPositions = getFlippedElementPositions(containerEl); Object.keys(newFlipChildrenPositions).forEach(function (id) { if (!cachedFlipChildrenPositions[id] || !newFlipChildrenPositions[id]) { return; } var prevRect = cachedFlipChildrenPositions[id].rect; var currentRect = newFlipChildrenPositions[id].rect; var prevOpacity = cachedFlipChildrenPositions[id].opacity; var currentOpacity = newFlipChildrenPositions[id].opacity; // don't animate invisible elements if (!rectInViewport(prevRect) && !rectInViewport(currentRect)) { return; } // don't animate elements that didn't change if (prevRect.left === currentRect.left && prevRect.top === currentRect.top && prevRect.width === currentRect.width && prevRect.height === currentRect.height && prevOpacity === currentOpacity) { return; } var element = containerEl.querySelector("*[data-flip-id=\"" + id + "\"]"); var flipStartId = cachedFlipChildrenPositions[id].flipComponentId; var flipEndId = element.dataset.flipComponentId; if (!shouldApplyTransform(element, flipStartId, flipEndId)) return; var currentTransform = Rematrix.parse(getComputedStyle(element)["transform"]); var toVals = { matrix: currentTransform }; var fromVals = {}; var transformsArray = [currentTransform]; // we're only going to animate the values that the child wants animated, // based on its data-* attributes if (element.dataset.translateX) { transformsArray.push(Rematrix.translateX(prevRect.left - currentRect.left)); } if (element.dataset.translateY) { transformsArray.push(Rematrix.translateY(prevRect.top - currentRect.top)); } if (element.dataset.scaleX) { transformsArray.push(Rematrix.scaleX(prevRect.width / Math.max(currentRect.width, 0.0001))); } if (element.dataset.scaleY) { transformsArray.push(Rematrix.scaleY(prevRect.height / Math.max(currentRect.height, 0.0001))); } if (element.dataset.opacity) { fromVals.opacity = prevOpacity; toVals.opacity = currentOpacity; } if (element.dataset.transformOrigin) { element.style.transformOrigin = element.dataset.transformOrigin; } getInvertedChildren(element, id).forEach(function (child) { if (child.dataset.transformOrigin) { child.style.transformOrigin = child.dataset.transformOrigin; } }); if (inProgressAnimations[id]) { inProgressAnimations[id].stop(); inProgressAnimations[id].onComplete(); delete inProgressAnimations[id]; } fromVals.matrix = transformsArray.reduce(Rematrix.multiply); // before animating, immediately apply FLIP styles to prevent flicker applyStyles(element, fromVals); invertTransformsForChildren(getInvertedChildren(element, id), fromVals, { flipStartId: flipStartId, flipEndId: flipEndId }); if (flipCallbacks[id] && flipCallbacks[id].onStart) flipCallbacks[id].onStart(element, flipStartId); var settings = { duration: element.dataset.flipDuration || duration, ease: element.dataset.flipEase && popmotionEasing[element.dataset.flipEase] || popmotionEasing[ease] }; var onComplete = function onComplete() {}; if (flipCallbacks[id] && flipCallbacks[id].onComplete) { onComplete = function onComplete() { return flipCallbacks[id].onComplete(element, flipStartId); }; } // now start the animation var _parallel$start = (0, _parallel2.default)((0, _tween2.default)(_extends({ from: fromVals.matrix, to: toVals.matrix }, settings)), (0, _tween2.default)(_extends({ from: { opacity: fromVals.opacity }, to: { opacity: toVals.opacity } }, settings))).start({ update: function update(_ref5) { var matrix = _ref5[0], otherVals = _ref5[1]; if (!body.contains(element)) { stop && stop(); return; } applyStyles(element, _extends({}, otherVals, { matrix: matrix })); // for children that requested it, cancel out the transform by applying the inverse transform invertTransformsForChildren(getInvertedChildren(element, id), matrix, { flipStartId: flipStartId, flipEndId: flipEndId }); }, complete: function complete() { delete inProgressAnimations[id]; requestAnimationFrame(function () { element.style.transform = ""; }); onComplete(); } }), stop = _parallel$start.stop; // in case we have to cancel inProgressAnimations[id] = { stop: stop, onComplete: onComplete }; }); return inProgressAnimations; };