UNPKG

gsap

Version:

GSAP is a JavaScript library for building high-performance animations that work in **every** major browser. Animate CSS, SVG, canvas, React, Vue, WebGL, colors, strings, motion paths, generic objects...anything JavaScript can touch! No other library deliv

217 lines (211 loc) 8.74 kB
/*! * MotionPathPlugin 3.0.4 * https://greensock.com * * @license Copyright 2008-2019, GreenSock. All rights reserved. * Subject to the terms at https://greensock.com/standard-license or for * Club GreenSock members, the agreement issued with that membership. * @author: Jack Doyle, jack@greensock.com */ /* eslint-disable */ import { getRawPath, cacheRawPathMeasurements, getPositionOnPath, pointsToSegment, flatPointsToSegment, sliceRawPath, stringToRawPath, rawPathToString, transformRawPath, convertToPath } from "./utils/paths.js"; import { getGlobalMatrix } from "./utils/matrix.js"; let _xProps = ["x","translateX","left","marginLeft"], _yProps = ["y","translateY","top","marginTop"], _DEG2RAD = Math.PI / 180, gsap, PropTween, _getUnit, _toArray, _getGSAP = () => gsap || (typeof(window) !== "undefined" && (gsap = window.gsap) && gsap.registerPlugin && gsap), _populateSegmentFromArray = (segment, values, property, mode) => { //mode: 0 = x but don't fill y yet, 1 = y. let l = values.length, si = mode, i = 0; for (; i < l; i++) { segment[si] = parseFloat(values[i][property]); si += 2; } return segment; }, _getPropNum = (target, prop, unit) => parseFloat(target._gsap.get(target, prop, unit || "px")) || 0, _relativize = segment => { let x = segment[0], y = segment[1], i; for (i = 2; i < segment.length; i+=2) { x = (segment[i] += x); y = (segment[i+1] += y); } }, _segmentToRawPath = (plugin, segment, target, x, y, slicer, vars) => { if (vars.type === "cubic") { segment = [segment]; } else { segment.unshift(_getPropNum(target, x, vars.unitX), y ? _getPropNum(target, y, vars.unitY) : 0); if (vars.relative) { _relativize(segment); } let pointFunc = y ? pointsToSegment : flatPointsToSegment; segment = [pointFunc(segment, vars.curviness)]; } segment = slicer(_align(segment, target, vars)); _addDimensionalPropTween(plugin, target, x, segment, "x", vars.unitX); if (y) { _addDimensionalPropTween(plugin, target, y, segment, "y", vars.unitY); } return cacheRawPathMeasurements(segment, vars.resolution || (vars.curviness === 0 ? 20 : 12)); //when curviness is 0, it creates control points right on top of the anchors which makes it more sensitive to resolution, thus we change the default accordingly. }, _emptyFunc = v => v, _align = (rawPath, target, {align, matrix, offsetX, offsetY}) => { let x, y, tween, targetMatrix, alignTarget, alignPath, alignMatrix, invertedMatrix, tx, ty; if (!rawPath || !rawPath.length) { return getRawPath("M0,0L0,0"); } if (align) { if (align === "self" || ((alignTarget = _toArray(align)[0] || target) === target)) { x = _getPropNum(target, "x") - rawPath[0][0]; y = _getPropNum(target, "y") - rawPath[0][1]; transformRawPath(rawPath, 1, 0, 0, 1, x, y); } else { tween = gsap.to(target, {xPercent:0, yPercent:0, x:0, y:0}).progress(1); //get rid of transforms, otherwise they'll throw off the measurements. targetMatrix = getGlobalMatrix(target); //we cannot use something like getScreenCTM() because of a major bug in Firefox that has existed for years and prevents values from being reported correctly when an ancestor element has transforms applied. Our proprietary getGlobalMatrix() works across all browsers. tween.render(-1).kill(); if (alignTarget.getTotalLength && alignTarget.tagName.toLowerCase() === "path") { //path alignPath = getRawPath(alignTarget); alignMatrix = getGlobalMatrix(alignTarget.parentNode); x = alignPath[0][0]; y = alignPath[0][1]; } else { alignMatrix = getGlobalMatrix(alignTarget); x = 0; y = 0; } tx = alignMatrix.a * x + alignMatrix.c * y + alignMatrix.e - targetMatrix.e; ty = alignMatrix.b * x + alignMatrix.d * y + alignMatrix.f - targetMatrix.f; invertedMatrix = getGlobalMatrix(target.parentNode, true); x = invertedMatrix.a * tx + invertedMatrix.c * ty; y = invertedMatrix.b * tx + invertedMatrix.d * ty; tx = rawPath[0][0]; ty = rawPath[0][1]; alignMatrix.multiply(invertedMatrix); x -= alignMatrix.a * tx + alignMatrix.c * ty; y -= alignMatrix.b * tx + alignMatrix.d * ty; transformRawPath(rawPath, alignMatrix.a, alignMatrix.b, alignMatrix.c, alignMatrix.d, x, y); } } if (matrix) { transformRawPath(rawPath, matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f); } else if (offsetX || offsetY) { transformRawPath(rawPath, 1, 0, 0, 1, offsetX || 0, offsetY || 0); } return rawPath; }, _addDimensionalPropTween = (plugin, target, property, rawPath, pathProperty, forceUnit) => { let cache = target._gsap, pt = plugin._pt = new PropTween(plugin._pt, target, property, 0, 0, _emptyFunc, 0, cache.set(target, property, plugin)); pt.u = _getUnit(cache.get(target, property, forceUnit)) || 0; pt.path = rawPath; pt.pp = pathProperty; plugin._props.push(property); }, _sliceModifier = (start, end) => rawPath => (start || end !== 1) ? sliceRawPath(rawPath, start, end) : rawPath; export const MotionPathPlugin = { version:"3.0.4", name:"motionPath", register(core, Plugin, propTween) { gsap = core; _getUnit = gsap.utils.getUnit; _toArray = gsap.utils.toArray; PropTween = propTween; }, init(target, vars, tween, index, targets) { if (!gsap) { console.warn("Please gsap.registerPlugin(MotionPathPlugin)"); return false; } if (!(typeof(vars) === "object" && !vars.style) || !vars.path) { vars = {path:vars}; } let rawPaths = [], path = vars.path, firstObj = path[0], autoRotate = vars.autoRotate, slicer = _sliceModifier(vars.start, ("end" in vars) ? vars.end : 1), rawPath, p, x, y; this.rawPaths = rawPaths; this.target = target; if ((this.rotate = (autoRotate || autoRotate === 0))) { //get the rotational data FIRST so that the setTransform() method is called in the correct order in the render() loop - rotation gets set last. this.rOffset = parseFloat(autoRotate) || 0; this.radians = !!vars.useRadians; this.rProp = vars.rotation || "rotation"; // rotation property this.rSet = target._gsap.set(target, this.rProp, this); // rotation setter this.ru = _getUnit(target._gsap.get(target, this.rProp)) || 0; // rotation units } if (Array.isArray(path) && !("closed" in path) && typeof(firstObj) !== "number") { for (p in firstObj) { if (~_xProps.indexOf(p)) { x = p; } else if (~_yProps.indexOf(p)) { y = p; } } if (x && y) { //correlated values rawPaths.push(_segmentToRawPath(this, _populateSegmentFromArray(_populateSegmentFromArray([], path, x, 0), path, y, 1), target, vars.x || x, vars.y || y, slicer, vars)); } else { x = y = 0; } for (p in firstObj) { if (p !== x && p !== y) { rawPaths.push(_segmentToRawPath(this, _populateSegmentFromArray([], path, p, 0), target, p, 0, slicer, vars)); } } } else { rawPath = slicer(_align(getRawPath(vars.path), target, vars)); cacheRawPathMeasurements(rawPath, vars.resolution); rawPaths.push(rawPath); _addDimensionalPropTween(this, target, vars.x || "x", rawPath, "x", vars.unitX || "px"); _addDimensionalPropTween(this, target, vars.y || "y", rawPath, "y", vars.unitY || "px"); } }, render(ratio, data) { let rawPaths = data.rawPaths, i = rawPaths.length, pt = data._pt; if (ratio > 1) { ratio = 1; } else if (ratio < 0) { ratio = 0; } while (i--) { getPositionOnPath(rawPaths[i], ratio, !i && data.rotate, rawPaths[i]); } while (pt) { pt.set(pt.t, pt.p, pt.path[pt.pp] + pt.u, pt.d, ratio); pt = pt._next; } if (data.rotate) { data.rSet(data.target, data.rProp, rawPaths[0].angle * (data.radians ? _DEG2RAD : 1) + data.rOffset + data.ru, data, ratio); } }, getLength(path) { return cacheRawPathMeasurements(getRawPath(path)).totalLength; }, sliceRawPath: sliceRawPath, getRawPath: getRawPath, pointsToSegment: pointsToSegment, stringToRawPath: stringToRawPath, rawPathToString: rawPathToString, transformRawPath: transformRawPath, convertToPath: (targets, swap) => _toArray(targets).map(target => convertToPath(target, swap !== false)), getGlobalMatrix: getGlobalMatrix, getPositionOnPath: getPositionOnPath, cacheRawPathMeasurements: cacheRawPathMeasurements, arrayToRawPath(value, vars) { vars = vars || {}; let segment = _populateSegmentFromArray(_populateSegmentFromArray([], value, vars.x || "x", 0), value, vars.y || "y", 1); if (vars.relative) { _relativize(segment); } return [(vars.type === "cubic") ? segment : pointsToSegment(segment, vars.curviness)]; } }; _getGSAP() && gsap.registerPlugin(MotionPathPlugin); export { MotionPathPlugin as default };