UNPKG

lottie-web

Version:

After Effects plugin for exporting animations to SVG + JavaScript or canvas + JavaScript

360 lines (346 loc) 12.1 kB
import { extendPrototype, } from '../functionExtensions'; import PropertyFactory from '../PropertyFactory'; import shapePool from '../pooling/shape_pool'; import bez from '../bez'; import { ShapeModifier, } from './ShapeModifiers'; import segmentsLengthPool from '../pooling/segments_length_pool'; function TrimModifier() { } extendPrototype([ShapeModifier], TrimModifier); TrimModifier.prototype.initModifierProperties = function (elem, data) { this.s = PropertyFactory.getProp(elem, data.s, 0, 0.01, this); this.e = PropertyFactory.getProp(elem, data.e, 0, 0.01, this); this.o = PropertyFactory.getProp(elem, data.o, 0, 0, this); this.sValue = 0; this.eValue = 0; this.getValue = this.processKeys; this.m = data.m; this._isAnimated = !!this.s.effectsSequence.length || !!this.e.effectsSequence.length || !!this.o.effectsSequence.length; }; TrimModifier.prototype.addShapeToModifier = function (shapeData) { shapeData.pathsData = []; }; TrimModifier.prototype.calculateShapeEdges = function (s, e, shapeLength, addedLength, totalModifierLength) { var segments = []; if (e <= 1) { segments.push({ s: s, e: e, }); } else if (s >= 1) { segments.push({ s: s - 1, e: e - 1, }); } else { segments.push({ s: s, e: 1, }); segments.push({ s: 0, e: e - 1, }); } var shapeSegments = []; var i; var len = segments.length; var segmentOb; for (i = 0; i < len; i += 1) { segmentOb = segments[i]; if (!(segmentOb.e * totalModifierLength < addedLength || segmentOb.s * totalModifierLength > addedLength + shapeLength)) { var shapeS; var shapeE; if (segmentOb.s * totalModifierLength <= addedLength) { shapeS = 0; } else { shapeS = (segmentOb.s * totalModifierLength - addedLength) / shapeLength; } if (segmentOb.e * totalModifierLength >= addedLength + shapeLength) { shapeE = 1; } else { shapeE = ((segmentOb.e * totalModifierLength - addedLength) / shapeLength); } shapeSegments.push([shapeS, shapeE]); } } if (!shapeSegments.length) { shapeSegments.push([0, 0]); } return shapeSegments; }; TrimModifier.prototype.releasePathsData = function (pathsData) { var i; var len = pathsData.length; for (i = 0; i < len; i += 1) { segmentsLengthPool.release(pathsData[i]); } pathsData.length = 0; return pathsData; }; TrimModifier.prototype.processShapes = function (_isFirstFrame) { var s; var e; if (this._mdf || _isFirstFrame) { var o = (this.o.v % 360) / 360; if (o < 0) { o += 1; } if (this.s.v > 1) { s = 1 + o; } else if (this.s.v < 0) { s = 0 + o; } else { s = this.s.v + o; } if (this.e.v > 1) { e = 1 + o; } else if (this.e.v < 0) { e = 0 + o; } else { e = this.e.v + o; } if (s > e) { var _s = s; s = e; e = _s; } s = Math.round(s * 10000) * 0.0001; e = Math.round(e * 10000) * 0.0001; this.sValue = s; this.eValue = e; } else { s = this.sValue; e = this.eValue; } var shapePaths; var i; var len = this.shapes.length; var j; var jLen; var pathsData; var pathData; var totalShapeLength; var totalModifierLength = 0; if (e === s) { for (i = 0; i < len; i += 1) { this.shapes[i].localShapeCollection.releaseShapes(); this.shapes[i].shape._mdf = true; this.shapes[i].shape.paths = this.shapes[i].localShapeCollection; if (this._mdf) { this.shapes[i].pathsData.length = 0; } } } else if (!((e === 1 && s === 0) || (e === 0 && s === 1))) { var segments = []; var shapeData; var localShapeCollection; for (i = 0; i < len; i += 1) { shapeData = this.shapes[i]; // if shape hasn't changed and trim properties haven't changed, cached previous path can be used if (!shapeData.shape._mdf && !this._mdf && !_isFirstFrame && this.m !== 2) { shapeData.shape.paths = shapeData.localShapeCollection; } else { shapePaths = shapeData.shape.paths; jLen = shapePaths._length; totalShapeLength = 0; if (!shapeData.shape._mdf && shapeData.pathsData.length) { totalShapeLength = shapeData.totalShapeLength; } else { pathsData = this.releasePathsData(shapeData.pathsData); for (j = 0; j < jLen; j += 1) { pathData = bez.getSegmentsLength(shapePaths.shapes[j]); pathsData.push(pathData); totalShapeLength += pathData.totalLength; } shapeData.totalShapeLength = totalShapeLength; shapeData.pathsData = pathsData; } totalModifierLength += totalShapeLength; shapeData.shape._mdf = true; } } var shapeS = s; var shapeE = e; var addedLength = 0; var edges; for (i = len - 1; i >= 0; i -= 1) { shapeData = this.shapes[i]; if (shapeData.shape._mdf) { localShapeCollection = shapeData.localShapeCollection; localShapeCollection.releaseShapes(); // if m === 2 means paths are trimmed individually so edges need to be found for this specific shape relative to whoel group if (this.m === 2 && len > 1) { edges = this.calculateShapeEdges(s, e, shapeData.totalShapeLength, addedLength, totalModifierLength); addedLength += shapeData.totalShapeLength; } else { edges = [[shapeS, shapeE]]; } jLen = edges.length; for (j = 0; j < jLen; j += 1) { shapeS = edges[j][0]; shapeE = edges[j][1]; segments.length = 0; if (shapeE <= 1) { segments.push({ s: shapeData.totalShapeLength * shapeS, e: shapeData.totalShapeLength * shapeE, }); } else if (shapeS >= 1) { segments.push({ s: shapeData.totalShapeLength * (shapeS - 1), e: shapeData.totalShapeLength * (shapeE - 1), }); } else { segments.push({ s: shapeData.totalShapeLength * shapeS, e: shapeData.totalShapeLength, }); segments.push({ s: 0, e: shapeData.totalShapeLength * (shapeE - 1), }); } var newShapesData = this.addShapes(shapeData, segments[0]); if (segments[0].s !== segments[0].e) { if (segments.length > 1) { var lastShapeInCollection = shapeData.shape.paths.shapes[shapeData.shape.paths._length - 1]; if (lastShapeInCollection.c) { var lastShape = newShapesData.pop(); this.addPaths(newShapesData, localShapeCollection); newShapesData = this.addShapes(shapeData, segments[1], lastShape); } else { this.addPaths(newShapesData, localShapeCollection); newShapesData = this.addShapes(shapeData, segments[1]); } } this.addPaths(newShapesData, localShapeCollection); } } shapeData.shape.paths = localShapeCollection; } } } else if (this._mdf) { for (i = 0; i < len; i += 1) { // Releasign Trim Cached paths data when no trim applied in case shapes are modified inbetween. // Don't remove this even if it's losing cached info. this.shapes[i].pathsData.length = 0; this.shapes[i].shape._mdf = true; } } }; TrimModifier.prototype.addPaths = function (newPaths, localShapeCollection) { var i; var len = newPaths.length; for (i = 0; i < len; i += 1) { localShapeCollection.addShape(newPaths[i]); } }; TrimModifier.prototype.addSegment = function (pt1, pt2, pt3, pt4, shapePath, pos, newShape) { shapePath.setXYAt(pt2[0], pt2[1], 'o', pos); shapePath.setXYAt(pt3[0], pt3[1], 'i', pos + 1); if (newShape) { shapePath.setXYAt(pt1[0], pt1[1], 'v', pos); } shapePath.setXYAt(pt4[0], pt4[1], 'v', pos + 1); }; TrimModifier.prototype.addSegmentFromArray = function (points, shapePath, pos, newShape) { shapePath.setXYAt(points[1], points[5], 'o', pos); shapePath.setXYAt(points[2], points[6], 'i', pos + 1); if (newShape) { shapePath.setXYAt(points[0], points[4], 'v', pos); } shapePath.setXYAt(points[3], points[7], 'v', pos + 1); }; TrimModifier.prototype.addShapes = function (shapeData, shapeSegment, shapePath) { var pathsData = shapeData.pathsData; var shapePaths = shapeData.shape.paths.shapes; var i; var len = shapeData.shape.paths._length; var j; var jLen; var addedLength = 0; var currentLengthData; var segmentCount; var lengths; var segment; var shapes = []; var initPos; var newShape = true; if (!shapePath) { shapePath = shapePool.newElement(); segmentCount = 0; initPos = 0; } else { segmentCount = shapePath._length; initPos = shapePath._length; } shapes.push(shapePath); for (i = 0; i < len; i += 1) { lengths = pathsData[i].lengths; shapePath.c = shapePaths[i].c; jLen = shapePaths[i].c ? lengths.length : lengths.length + 1; for (j = 1; j < jLen; j += 1) { currentLengthData = lengths[j - 1]; if (addedLength + currentLengthData.addedLength < shapeSegment.s) { addedLength += currentLengthData.addedLength; shapePath.c = false; } else if (addedLength > shapeSegment.e) { shapePath.c = false; break; } else { if (shapeSegment.s <= addedLength && shapeSegment.e >= addedLength + currentLengthData.addedLength) { this.addSegment(shapePaths[i].v[j - 1], shapePaths[i].o[j - 1], shapePaths[i].i[j], shapePaths[i].v[j], shapePath, segmentCount, newShape); newShape = false; } else { segment = bez.getNewSegment(shapePaths[i].v[j - 1], shapePaths[i].v[j], shapePaths[i].o[j - 1], shapePaths[i].i[j], (shapeSegment.s - addedLength) / currentLengthData.addedLength, (shapeSegment.e - addedLength) / currentLengthData.addedLength, lengths[j - 1]); this.addSegmentFromArray(segment, shapePath, segmentCount, newShape); // this.addSegment(segment.pt1, segment.pt3, segment.pt4, segment.pt2, shapePath, segmentCount, newShape); newShape = false; shapePath.c = false; } addedLength += currentLengthData.addedLength; segmentCount += 1; } } if (shapePaths[i].c && lengths.length) { currentLengthData = lengths[j - 1]; if (addedLength <= shapeSegment.e) { var segmentLength = lengths[j - 1].addedLength; if (shapeSegment.s <= addedLength && shapeSegment.e >= addedLength + segmentLength) { this.addSegment(shapePaths[i].v[j - 1], shapePaths[i].o[j - 1], shapePaths[i].i[0], shapePaths[i].v[0], shapePath, segmentCount, newShape); newShape = false; } else { segment = bez.getNewSegment(shapePaths[i].v[j - 1], shapePaths[i].v[0], shapePaths[i].o[j - 1], shapePaths[i].i[0], (shapeSegment.s - addedLength) / segmentLength, (shapeSegment.e - addedLength) / segmentLength, lengths[j - 1]); this.addSegmentFromArray(segment, shapePath, segmentCount, newShape); // this.addSegment(segment.pt1, segment.pt3, segment.pt4, segment.pt2, shapePath, segmentCount, newShape); newShape = false; shapePath.c = false; } } else { shapePath.c = false; } addedLength += currentLengthData.addedLength; segmentCount += 1; } if (shapePath._length) { shapePath.setXYAt(shapePath.v[initPos][0], shapePath.v[initPos][1], 'i', initPos); shapePath.setXYAt(shapePath.v[shapePath._length - 1][0], shapePath.v[shapePath._length - 1][1], 'o', shapePath._length - 1); } if (addedLength > shapeSegment.e) { break; } if (i < len - 1) { shapePath = shapePool.newElement(); newShape = true; shapes.push(shapePath); segmentCount = 0; } } return shapes; }; export default TrimModifier;