UNPKG

@infinite-canvas-tutorial/webcomponents

Version:
126 lines 5.01 kB
/** * Borrow from https://github.com/excalidraw/excalidraw/blob/d87620b23907cef9a119c758124569b5f44991a4/packages/excalidraw/animated-trail.ts */ import { LaserPointer } from '@excalidraw/laser-pointer'; import { getSvgPathFromStroke, createSVGElement, } from '@infinite-canvas-tutorial/ecs'; export class AnimatedTrail { constructor(animationFrameHandler, api, options) { this.animationFrameHandler = animationFrameHandler; this.api = api; this.options = options; this.pastTrails = []; this.animationFrameHandler.register(this, this.onFrame.bind(this)); this.trailElement = createSVGElement('path'); if (this.options.animateTrail) { const { strokeDasharray = '7 7', strokeDashoffset = '10' } = this.options; this.trailAnimation = createSVGElement('animate'); // TODO: make this configurable this.trailAnimation.setAttribute('attributeName', 'stroke-dashoffset'); this.trailElement.setAttribute('stroke-dasharray', strokeDasharray); this.trailElement.setAttribute('stroke-dashoffset', strokeDashoffset); this.trailAnimation.setAttribute('from', '0'); this.trailAnimation.setAttribute('to', `-14`); this.trailAnimation.setAttribute('dur', '0.3s'); this.trailElement.appendChild(this.trailAnimation); } } get hasCurrentTrail() { return !!this.currentTrail; } hasLastPoint(x, y) { if (this.currentTrail) { const len = this.currentTrail.originalPoints.length; return (this.currentTrail.originalPoints[len - 1][0] === x && this.currentTrail.originalPoints[len - 1][1] === y); } return false; } start(container) { if (container) { this.container = container; } if (this.trailElement.parentNode !== this.container && this.container) { this.container.appendChild(this.trailElement); } this.animationFrameHandler.start(this); } stop() { var _a; this.animationFrameHandler.stop(this); if (this.trailElement.parentNode === this.container) { (_a = this.container) === null || _a === void 0 ? void 0 : _a.removeChild(this.trailElement); } } startPath(x, y) { this.currentTrail = new LaserPointer(this.options); this.currentTrail.addPoint([x, y, performance.now()]); this.update(); } addPointToPath(x, y) { if (this.currentTrail) { this.currentTrail.addPoint([x, y, performance.now()]); this.update(); } } endPath() { if (this.currentTrail) { this.currentTrail.close(); this.currentTrail.options.keepHead = false; this.pastTrails.push(this.currentTrail); this.currentTrail = undefined; this.update(); } } getCurrentTrail() { return this.currentTrail; } clearTrails() { this.pastTrails = []; this.currentTrail = undefined; this.update(); } update() { this.start(); if (this.trailAnimation) { this.trailAnimation.setAttribute('begin', 'indefinite'); this.trailAnimation.setAttribute('repeatCount', 'indefinite'); } } onFrame() { var _a, _b, _c, _d; const paths = []; for (const trail of this.pastTrails) { paths.push(this.drawTrail(trail)); } if (this.currentTrail) { const currentPath = this.drawTrail(this.currentTrail); paths.push(currentPath); } this.pastTrails = this.pastTrails.filter((trail) => { return trail.getStrokeOutline().length !== 0; }); if (paths.length === 0) { this.stop(); } const svgPaths = paths.join(' ').trim(); this.trailElement.setAttribute('d', svgPaths); if (this.trailAnimation) { this.trailElement.setAttribute('fill', ((_a = this.options.fill) !== null && _a !== void 0 ? _a : (() => 'black'))(this)); this.trailElement.setAttribute('stroke', ((_b = this.options.stroke) !== null && _b !== void 0 ? _b : (() => 'black'))(this)); this.trailElement.setAttribute('fill-opacity', ((_c = this.options.fillOpacity) !== null && _c !== void 0 ? _c : (() => 0.5))(this).toString()); } else { this.trailElement.setAttribute('fill', ((_d = this.options.fill) !== null && _d !== void 0 ? _d : (() => 'black'))(this)); } } drawTrail(trail) { const _stroke = trail.getStrokeOutline(trail.options.size).map(([x, y]) => { return [x, y]; }); const stroke = this.trailAnimation ? _stroke.slice(0, _stroke.length / 2) : _stroke; return getSvgPathFromStroke(stroke, true); } } //# sourceMappingURL=animated-trail.js.map