curvity 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports):typeof define==`function`&&define.amd?define([`exports`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.curvity={}))})(this,function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t={showRuler:!0,showYAxis:!0,yAxisWidth:44,rulerHeight:20,showSidebar:!0,sidebarWidth:140};function n(e,t,n=`spline`){return{time:e,value:t,inTangent:{type:n,slope:0},outTangent:{type:n,slope:0}}}var r={curves:[{name:`translateX`,color:`#e06060`,keyframes:[n(0,0),n(.5,3),n(1,0),n(1.5,-3),n(2,0)]},{name:`translateY`,color:`#60c060`,keyframes:[n(0,0,`linear`),n(.3,5,`flat`),n(.6,0,`linear`),n(.9,3,`flat`),n(1.2,0,`linear`)]},{name:`translateZ`,color:`#6090e0`,keyframes:[n(0,-2),n(1,2),n(2,-2)]}]},i=40;e.Graph=class{svg;config;state;constructor(e,n={},i=r){if(typeof e==`string`){let t=document.querySelector(`#${e}`);if(!t)throw Error(`Container not found: '${e}'`);this.svg=t}else this.svg=document.createElementNS(`http://www.w3.org/2000/svg`,`svg`),e.appendChild(this.svg),this.svg.style.cssText=`width:100%;height:100%;user-select:none;display:block;outline:none`,this.svg.setAttribute(`tabindex`,`0`);this.config={...t,...n},this.state={svgWidth:this.svg.clientWidth,svgHeight:this.svg.clientHeight,timeScale:200,timeOffset:-.1,valueScale:50,valueOffset:-6,playHead:0,selection:[],hiddenCurves:new Set,data:i},this._setupEvents(),new ResizeObserver(()=>{this.state.svgWidth=this.svg.clientWidth,this.state.svgHeight=this.svg.clientHeight,this.redraw()}).observe(this.svg),requestAnimationFrame(()=>{this.state.svgWidth=this.svg.clientWidth,this.state.svgHeight=this.svg.clientHeight,this.autoFit(),this.redraw()})}timeToX(e){return this._chartLeft()+(e-this.state.timeOffset)*this.state.timeScale}xToTime(e){return(e-this._chartLeft())/this.state.timeScale+this.state.timeOffset}valueToY(e){return this._chartBottom()-(e-this.state.valueOffset)*this.state.valueScale}yToValue(e){return(this._chartBottom()-e)/this.state.valueScale+this.state.valueOffset}_chartBottom(){return this.state.svgHeight-this.config.rulerHeight}_chartLeft(){return(this.config.showSidebar?this.config.sidebarWidth:0)+(this.config.showYAxis?this.config.yAxisWidth:0)}_chartArea(){return{left:this._chartLeft(),right:this.state.svgWidth,top:0,bottom:this._chartBottom()}}_rulerDims(){let{rulerHeight:e}=this.config,{svgWidth:t,svgHeight:n}=this.state;return{top:n-e,bottom:n,left:this._chartLeft(),right:t,height:e}}_computeTangent(e,t,n){let r=this.state.data.curves[e].keyframes,i=r[t],a=n===`in`?i.inTangent:i.outTangent,o=t>0?r[t-1]:null,s=t<r.length-1?r[t+1]:null;if(a.type===`fixed`){let e=n===`out`?s:o;if(!e)return{dx:0,dy:0};let t=n===`out`?(e.time-i.time)/3:-(i.time-e.time)/3;return{dx:t,dy:a.slope*t}}if(a.type===`flat`)return{dx:n===`out`?s?(s.time-i.time)/3:.3:o?-(i.time-o.time)/3:-.3,dy:0};if(a.type===`linear`)return n===`out`&&s?{dx:(s.time-i.time)/3,dy:(s.value-i.value)/3}:n===`in`&&o?{dx:(o.time-i.time)/3,dy:(o.value-i.value)/3}:{dx:0,dy:0};let c=0;if(o&&s?c=(s.value-o.value)/(s.time-o.time):s?c=(s.value-i.value)/(s.time-i.time):o&&(c=(i.value-o.value)/(i.time-o.time)),n===`out`&&s){let e=(s.time-i.time)/3;return{dx:e,dy:c*e}}if(n===`in`&&o){let e=-(i.time-o.time)/3;return{dx:e,dy:c*e}}return{dx:0,dy:0}}_tangentHandlePos(e,t,n){let r=this.state.data.curves[e].keyframes[t],a=this.timeToX(r.time),o=this.valueToY(r.value),s=this._computeTangent(e,t,n),c=s.dx*this.state.timeScale,l=-s.dy*this.state.valueScale,u=Math.hypot(c,l);return u<1e-6?{x:a+(n===`out`?i:-i),y:o}:{x:a+c/u*i,y:o+l/u*i}}_isKfSelected(e,t){return this.state.selection.some(n=>n.kind===`keyframe`&&n.curveIdx===e&&n.kfIdx===t)}_isTangentSelected(e,t,n){return this.state.selection.some(r=>r.kind===`tangent`&&r.curveIdx===e&&r.kfIdx===t&&r.side===n)}_hasHandleVisible(e,t){return this._isKfSelected(e,t)||this._isTangentSelected(e,t,`in`)||this._isTangentSelected(e,t,`out`)}redraw(){this.svg.innerHTML=``,this._addClipPath(),this._drawBackground(),this._drawGrid(),this.config.showYAxis&&this._drawYAxis(),this._drawCurves(),this._drawKeyframes(),this._drawTangentHandles(),this.config.showRuler&&this._drawRuler(),this.state.drag?.type===`marquee`&&this._drawMarquee(),this._drawPlayhead(),this.config.showSidebar&&this._drawSidebar()}_addClipPath(){let e=this._chartArea(),t=`http://www.w3.org/2000/svg`,n=document.createElementNS(t,`defs`),r=document.createElementNS(t,`clipPath`);r.setAttribute(`id`,`chart-clip`);let i=document.createElementNS(t,`rect`);i.setAttribute(`x`,String(e.left)),i.setAttribute(`y`,String(e.top)),i.setAttribute(`width`,String(e.right-e.left)),i.setAttribute(`height`,String(e.bottom-e.top)),r.appendChild(i),n.appendChild(r),this.svg.appendChild(n)}_drawBackground(){let{svgWidth:e,svgHeight:t}=this.state;this._el(`rect`,{x:0,y:0,width:e,height:t,fill:`#1c2230`})}_drawGrid(){let e=this._chartArea(),t=this.yToValue(e.top),n=this.yToValue(e.bottom),r=this._niceValueStep((t-n)/8);for(let i=Math.ceil(n/r)*r;i<=t+1e-9;i+=r){let t=this.valueToY(i),n=Math.abs(i)<r*.01;this._el(`line`,{x1:e.left,y1:t,x2:e.right,y2:t,stroke:n?`#2e4a6e`:`#1e2d40`,"stroke-width":n?1.5:1})}let i=this.xToTime(e.right)-this.xToTime(e.left),a=this._niceTimeStep(i/8);for(let t=Math.ceil(this.xToTime(e.left)/a)*a;t<=this.xToTime(e.right)+1e-9;t+=a){let n=this.timeToX(t);this._el(`line`,{x1:n,y1:e.top,x2:n,y2:e.bottom,stroke:`#1e2d40`,"stroke-width":1})}}_drawYAxis(){let{yAxisWidth:e}=this.config,t=this.config.showSidebar?this.config.sidebarWidth:0,n=this._chartArea();this._el(`rect`,{x:t,y:n.top,width:e,height:n.bottom-n.top,fill:`#141b26`});let r=this.yToValue(n.top),i=this.yToValue(n.bottom),a=this._niceValueStep((r-i)/8);for(let o=Math.ceil(i/a)*a;o<=r+1e-9;o+=a){let r=this.valueToY(o);if(r<n.top+5||r>n.bottom-3)continue;this._el(`line`,{x1:t+e-3,y1:r,x2:t+e,y2:r,stroke:`#3a4a60`,"stroke-width":1});let i=Math.abs(o)<a*.001?`0`:Math.abs(o)>=10?o.toFixed(0):o.toPrecision(2).replace(/\.?0+$/,``);this._el(`text`,{x:t+e-5,y:r+3.5,fill:`#4a5a70`,"font-size":9,"text-anchor":`end`,"font-family":`system-ui,sans-serif`},i)}this._el(`line`,{x1:t+e,y1:n.top,x2:t+e,y2:n.bottom,stroke:`#263040`,"stroke-width":1})}_drawSidebar(){let{sidebarWidth:e,data:t}={...this.config,data:this.state.data},{svgHeight:n}=this.state;this._el(`rect`,{x:0,y:0,width:e,height:n,fill:`#111720`}),this._el(`line`,{x1:e,y1:0,x2:e,y2:n,stroke:`#1e2d40`,"stroke-width":1}),this._el(`text`,{x:10,y:16,fill:`#3a4a5a`,"font-size":9,"font-family":`system-ui,sans-serif`,"font-weight":`600`,"letter-spacing":`1`},`CURVES`),this._el(`line`,{x1:0,y1:22,x2:e,y2:22,stroke:`#1a2535`,"stroke-width":1});for(let n=0;n<t.curves.length;n++){let r=t.curves[n],i=!this.state.hiddenCurves.has(n),a=this.state.selection.some(e=>e.curveIdx===n),o=30+n*24;a&&this._el(`rect`,{x:0,y:o-1,width:e,height:24,fill:`#1a2840`}),this._el(`rect`,{x:8,y:o+7,width:10,height:10,rx:2,fill:i?r.color:`#2a3040`}),this._el(`text`,{x:24,y:o+16,fill:i?`#b0bcc8`:`#3a4a5a`,"font-size":11,"font-family":`system-ui,sans-serif`},r.name);let s=e-14,c=o+12;i?(this._el(`ellipse`,{cx:s,cy:c,rx:5,ry:3.5,fill:`none`,stroke:`#4a5a70`,"stroke-width":1.2}),this._el(`circle`,{cx:s,cy:c,r:1.8,fill:`#4a5a70`})):(this._el(`ellipse`,{cx:s,cy:c,rx:5,ry:3.5,fill:`none`,stroke:`#2a3a4a`,"stroke-width":1.2}),this._el(`line`,{x1:s-6,y1:c-4,x2:s+6,y2:c+4,stroke:`#2a3a4a`,"stroke-width":1.5}))}}_drawCurves(){let{data:e}=this.state,t=this._chartArea();for(let n=0;n<e.curves.length;n++){if(this.state.hiddenCurves.has(n))continue;let r=e.curves[n],i=r.keyframes;if(i.length===0)continue;let a=this.state.selection.some(e=>e.curveIdx===n);if(i.length===1){let e=this.valueToY(i[0].value);this._el(`line`,{x1:t.left,y1:e,x2:t.right,y2:e,stroke:r.color,"stroke-width":a?2:1.5,opacity:.7,"clip-path":`url(#chart-clip)`});continue}let o=``;for(let e=0;e<i.length-1;e++){let t=i[e],r=i[e+1],a=this.timeToX(t.time),s=this.valueToY(t.value),c=this.timeToX(r.time),l=this.valueToY(r.value);if(e===0&&(o+=`M ${a} ${s} `),t.outTangent.type===`stepped`)o+=`H ${c} V ${l} `;else{let i=this._computeTangent(n,e,`out`),a=this._computeTangent(n,e+1,`in`),s=this.timeToX(t.time+i.dx),u=this.valueToY(t.value+i.dy),d=this.timeToX(r.time+a.dx),f=this.valueToY(r.value+a.dy);o+=`C ${s} ${u} ${d} ${f} ${c} ${l} `}}this._el(`path`,{d:o,stroke:r.color,"stroke-width":a?2:1.5,fill:`none`,"clip-path":`url(#chart-clip)`})}}_drawKeyframes(){let{data:e}=this.state,t=this._chartArea(),n=4.5;for(let r=0;r<e.curves.length;r++){if(this.state.hiddenCurves.has(r))continue;let i=e.curves[r];for(let e=0;e<i.keyframes.length;e++){let a=i.keyframes[e],o=this.timeToX(a.time),s=this.valueToY(a.value);if(o<t.left-n||o>t.right+n||s<t.top-n||s>t.bottom+n)continue;let c=this._isKfSelected(r,e),l=this.state.hover?.type===`keyframe`&&this.state.hover.curveIdx===r&&this.state.hover.kfIdx===e,u=`${o},${s-n} ${o+n},${s} ${o},${s+n} ${o-n},${s}`;c?this._el(`polygon`,{points:u,fill:`#ffe060`,stroke:`#fffb`,"stroke-width":.75}):this._el(`polygon`,{points:u,fill:l?i.color:`#1c222e`,stroke:i.color,"stroke-width":1.5})}}}_drawTangentHandles(){let{data:e}=this.state;for(let t=0;t<e.curves.length;t++){if(this.state.hiddenCurves.has(t))continue;let n=e.curves[t];for(let e=0;e<n.keyframes.length;e++){if(!this._hasHandleVisible(t,e))continue;let r=n.keyframes[e],i=this.timeToX(r.time),a=this.valueToY(r.value);for(let o of[`in`,`out`]){if(o===`in`&&e===0||o===`out`&&e===n.keyframes.length-1||o===`out`&&r.outTangent.type===`stepped`)continue;let s=this._tangentHandlePos(t,e,o),c=this._isTangentSelected(t,e,o),l=this.state.hover?.type===`tangent`&&this.state.hover.curveIdx===t&&this.state.hover.kfIdx===e&&this.state.hover.side===o;this._el(`line`,{x1:i,y1:a,x2:s.x,y2:s.y,stroke:n.color,"stroke-width":1,opacity:.5}),this._el(`circle`,{cx:s.x,cy:s.y,r:3.5,fill:c?`#ffe060`:l?`#fff`:n.color,stroke:`#fff5`,"stroke-width":.5})}}}}_drawRuler(){let e=this._rulerDims(),t=this._chartArea(),{top:n,left:r,right:i,height:a}=e;this._el(`rect`,{x:r,y:n,width:i-r,height:a,fill:`#141b26`}),this._el(`line`,{x1:r,y1:n,x2:i,y2:n,stroke:`#263040`,"stroke-width":1});let o=this.xToTime(t.right)-this.xToTime(t.left),s=this._niceTimeStep(o/8);for(let e=Math.ceil(this.xToTime(t.left)/s)*s;e<=this.xToTime(i)+1e-9;e+=s){let t=this.timeToX(e);t<r||(this._el(`line`,{x1:t,y1:n,x2:t,y2:n+4,stroke:`#3a4a60`,"stroke-width":1}),this._el(`text`,{x:t+2,y:n+13,fill:`#4a5a70`,"font-size":9,"font-family":`system-ui,sans-serif`},this._formatTime(e,s)))}}_drawMarquee(){let e=this.state.drag;if(e?.type!==`marquee`)return;let t=Math.min(e.x0,e.x1),n=Math.min(e.y0,e.y1),r=Math.abs(e.x1-e.x0),i=Math.abs(e.y1-e.y0);this._el(`rect`,{x:t,y:n,width:r,height:i,fill:`rgba(100,150,255,0.06)`,stroke:`#6496ff`,"stroke-width":1,"stroke-dasharray":`3 2`})}_drawPlayhead(){let{playHead:e,svgWidth:t}=this.state,n=this._chartArea(),r=this._rulerDims(),i=this.timeToX(e);i<this.config.yAxisWidth||i>t||(this._el(`line`,{x1:i,y1:n.top,x2:i,y2:n.bottom,stroke:`#50e880`,"stroke-width":1,opacity:.45}),this._el(`polygon`,{points:`${i-5},${r.top} ${i+5},${r.top} ${i},${r.top+7}`,fill:`#50e880`,opacity:.9}))}_setupEvents(){this.svg.addEventListener(`contextmenu`,e=>e.preventDefault()),this.svg.addEventListener(`wheel`,e=>{e.preventDefault();let{x:t,y:n}=this._svgPoint(e),r=e.deltaY<0?1.12:1/1.12;if(e.altKey){let e=this.yToValue(n);this.state.valueScale=Math.max(2,Math.min(1e4,this.state.valueScale*r)),this.state.valueOffset=e-(this._chartBottom()-n)/this.state.valueScale}else{let e=this.xToTime(t);this.state.timeScale=Math.max(5,Math.min(2e4,this.state.timeScale*r)),this.state.timeOffset=e-(t-this.config.yAxisWidth)/this.state.timeScale}this.redraw()},{passive:!1}),this.svg.addEventListener(`mousedown`,e=>{let{x:t,y:n}=this._svgPoint(e),r=this._chartArea(),i=this._rulerDims();if(e.altKey&&e.button===2){e.preventDefault(),this.state.drag={type:`zoom`,startX:t,startY:n,startTimeScale:this.state.timeScale,startValueScale:this.state.valueScale,startTimeOffset:this.state.timeOffset,startValueOffset:this.state.valueOffset,pivotTime:this.xToTime(t),pivotValue:this.yToValue(n)},this.svg.style.cursor=`zoom-in`;return}if(e.altKey&&(e.button===0||e.button===1)||e.button===2){e.preventDefault(),this.state.drag={type:`pan`,startX:t,startY:n,startTimeOffset:this.state.timeOffset,startValueOffset:this.state.valueOffset},this.svg.style.cursor=`grabbing`;return}if(this.config.showSidebar&&t>=0&&t<=this.config.sidebarWidth&&e.button===0){let e=Math.floor((n-30)/24);e>=0&&e<this.state.data.curves.length&&(this.state.hiddenCurves.has(e)?this.state.hiddenCurves.delete(e):this.state.hiddenCurves.add(e),this.redraw());return}if(this._inside({x:t,y:n},i)){this.state.drag={type:`scrubPlayhead`},this.state.playHead=Math.max(0,this.xToTime(t)),this.redraw();return}if(e.button===1&&this._inside({x:t,y:n},r)){e.preventDefault();let r=this.state.selection.filter(e=>e.kind===`keyframe`);r.length>0&&(this.state.drag={type:`moveKeyframes`,startX:t,startY:n,entries:r.map(e=>({curveIdx:e.curveIdx,kfIdx:e.kfIdx,kfRef:this.state.data.curves[e.curveIdx].keyframes[e.kfIdx],origTime:this.state.data.curves[e.curveIdx].keyframes[e.kfIdx].time,origValue:this.state.data.curves[e.curveIdx].keyframes[e.kfIdx].value}))},this.redraw());return}if(!this._inside({x:t,y:n},r)||e.button!==0)return;let a=this._hitTangent(t,n);if(a){let{curveIdx:t,kfIdx:n,side:r}=a,i=this._isKfSelected(t,n);if(!e.shiftKey)this.state.selection=[{kind:`tangent`,curveIdx:t,kfIdx:n,side:r}];else{let e=this._isTangentSelected(t,n,r);this.state.selection=e?this.state.selection.filter(e=>!(e.kind===`tangent`&&e.curveIdx===t&&e.kfIdx===n&&e.side===r)):[...this.state.selection,{kind:`tangent`,curveIdx:t,kfIdx:n,side:r}]}this.state.drag={type:`moveTangent`,curveIdx:t,kfIdx:n,side:r,unified:i},this.redraw();return}let o=this._hitKeyframe(t,n);if(o){let{curveIdx:r,kfIdx:i}=o,a=this._isKfSelected(r,i);e.shiftKey?this.state.selection=a?this.state.selection.filter(e=>!(e.kind===`keyframe`&&e.curveIdx===r&&e.kfIdx===i)):[...this.state.selection,{kind:`keyframe`,curveIdx:r,kfIdx:i}]:a||(this.state.selection=[{kind:`keyframe`,curveIdx:r,kfIdx:i}]),this._isKfSelected(r,i)&&(this.state.drag={type:`moveKeyframes`,startX:t,startY:n,entries:this.state.selection.filter(e=>e.kind===`keyframe`).map(e=>({curveIdx:e.curveIdx,kfIdx:e.kfIdx,kfRef:this.state.data.curves[e.curveIdx].keyframes[e.kfIdx],origTime:this.state.data.curves[e.curveIdx].keyframes[e.kfIdx].time,origValue:this.state.data.curves[e.curveIdx].keyframes[e.kfIdx].value}))}),this.redraw();return}if(Math.abs(this.timeToX(this.state.playHead)-t)<6){this.state.drag={type:`scrubPlayhead`},this.redraw();return}e.shiftKey||(this.state.selection=[]),this.state.drag={type:`marquee`,x0:t,y0:n,x1:t,y1:n,additive:e.shiftKey},this.redraw()}),window.addEventListener(`mousemove`,e=>{let{x:t,y:n}=this._svgPoint(e),{drag:r}=this.state,i=this._chartArea(),a=this._rulerDims();if(!r){let e=this._hitTangent(t,n),r=e?null:this._hitKeyframe(t,n),i=Math.abs(this.timeToX(this.state.playHead)-t)<6,o=this._inside({x:t,y:n},a);e?(this.state.hover={type:`tangent`,...e},this.svg.style.cursor=`crosshair`):r?(this.state.hover={type:`keyframe`,...r},this.svg.style.cursor=`pointer`):i||o?(this.state.hover={type:`playhead`},this.svg.style.cursor=`col-resize`):(this.state.hover=void 0,this.svg.style.cursor=`default`),this.redraw();return}if(r.type===`pan`){this.state.timeOffset=r.startTimeOffset-(t-r.startX)/this.state.timeScale,this.state.valueOffset=r.startValueOffset+(n-r.startY)/this.state.valueScale,this.redraw();return}if(r.type===`zoom`){let e=.007,i=Math.exp((t-r.startX)*e),a=Math.exp((n-r.startY)*-e);this.state.timeScale=Math.max(5,Math.min(2e4,r.startTimeScale*i)),this.state.valueScale=Math.max(2,Math.min(1e4,r.startValueScale*a)),this.state.timeOffset=r.pivotTime-(r.startX-this.config.yAxisWidth)/this.state.timeScale,this.state.valueOffset=r.pivotValue-(this._chartBottom()-r.startY)/this.state.valueScale,this.redraw();return}if(r.type===`scrubPlayhead`){this.state.playHead=Math.max(0,this.xToTime(t)),this.redraw(),this._firePlayheadChange();return}if(r.type===`marquee`){r.x1=Math.max(i.left,Math.min(t,i.right)),r.y1=Math.max(i.top,Math.min(n,i.bottom)),this._applyMarqueeSelection(r),this.redraw();return}if(r.type===`moveKeyframes`){let i=t-r.startX,a=n-r.startY;e.shiftKey&&!r.axisLock&&(Math.abs(i)>5||Math.abs(a)>5)&&(r.axisLock=Math.abs(i)>=Math.abs(a)?`x`:`y`),e.shiftKey||(r.axisLock=void 0);let o=r.axisLock===`y`?0:i/this.state.timeScale,s=r.axisLock===`x`?0:-a/this.state.valueScale;for(let e of r.entries)e.kfRef.time=e.origTime+o,e.kfRef.value=e.origValue+s;let c=new Set(r.entries.map(e=>e.curveIdx));for(let e of c)this.state.data.curves[e].keyframes.sort((e,t)=>e.time-t.time);for(let e of r.entries){let t=this.state.data.curves[e.curveIdx].keyframes.indexOf(e.kfRef);t>=0&&(e.kfIdx=t)}let l=r.entries.map(e=>({kind:`keyframe`,curveIdx:e.curveIdx,kfIdx:e.kfIdx}));this.state.selection=[...this.state.selection.filter(e=>e.kind!==`keyframe`),...l],this.redraw();return}if(r.type===`moveTangent`){let{curveIdx:e,kfIdx:i,side:a,unified:o}=r,s=this.state.data.curves[e].keyframes[i],c=a===`in`?s.inTangent:s.outTangent,l=this.timeToX(s.time),u=this.valueToY(s.value),d=t-l,f=n-u,p=a===`out`?Math.max(d,1):Math.min(d,-1),m=-(f*this.state.timeScale)/(p*this.state.valueScale);if(c.type=`fixed`,c.slope=m,o){let e=a===`in`?s.outTangent:s.inTangent;e.type!==`stepped`&&(e.type=`fixed`,e.slope=m)}this.redraw();return}}),window.addEventListener(`mouseup`,()=>{this.state.drag=void 0,this.svg.style.cursor=`default`,this.redraw()}),window.addEventListener(`keydown`,e=>{let t=e.target;if(!(t.tagName===`INPUT`||t.tagName===`TEXTAREA`))switch(e.key){case`a`:case`A`:this.autoFit(),this.redraw();break;case`f`:case`F`:this.frameSelection(),this.redraw();break;case`s`:case`S`:{let e=this.state.playHead;for(let t=0;t<this.state.data.curves.length;t++){let r=this.state.data.curves[t];if(r.keyframes.some(t=>Math.abs(t.time-e)<1e-6))continue;let i=this._evalCurveAt(t,e);r.keyframes.push(n(e,i)),r.keyframes.sort((e,t)=>e.time-t.time)}this.redraw();break}case`d`:case`D`:{let{data:e,selection:t}=this.state,n=t.filter(e=>e.kind===`keyframe`),r=n.length>0?n.map(t=>e.curves[t.curveIdx].keyframes[t.kfIdx]):e.curves.flatMap(e=>e.keyframes);for(let e of r)e.inTangent={type:`spline`,slope:0},e.outTangent={type:`spline`,slope:0};this.redraw();break}case`Delete`:case`Backspace`:this.deleteSelected(),this.redraw();break}})}_hitKeyframe(e,t){let{data:n}=this.state,r=null;for(let i=0;i<n.curves.length;i++)for(let a=0;a<n.curves[i].keyframes.length;a++){let o=n.curves[i].keyframes[a],s=Math.hypot(this.timeToX(o.time)-e,this.valueToY(o.value)-t);s<9&&(!r||s<r.dist)&&(r={curveIdx:i,kfIdx:a,dist:s})}return r?{curveIdx:r.curveIdx,kfIdx:r.kfIdx}:null}_hitTangent(e,t){let{data:n}=this.state;for(let r=0;r<n.curves.length;r++)for(let i=0;i<n.curves[r].keyframes.length;i++){if(!this._hasHandleVisible(r,i))continue;let a=n.curves[r].keyframes[i];for(let o of[`in`,`out`]){if(o===`in`&&i===0||o===`out`&&i===n.curves[r].keyframes.length-1||o===`out`&&a.outTangent.type===`stepped`)continue;let s=this._tangentHandlePos(r,i,o);if(Math.hypot(s.x-e,s.y-t)<8)return{curveIdx:r,kfIdx:i,side:o}}}return null}_applyMarqueeSelection(e){let{data:t}=this.state,n=Math.min(e.x0,e.x1),r=Math.max(e.x0,e.x1),i=Math.min(e.y0,e.y1),a=Math.max(e.y0,e.y1),o=[];for(let e=0;e<t.curves.length;e++)for(let s=0;s<t.curves[e].keyframes.length;s++){let c=t.curves[e].keyframes[s],l=this.timeToX(c.time),u=this.valueToY(c.value);l>=n&&l<=r&&u>=i&&u<=a&&o.push({kind:`keyframe`,curveIdx:e,kfIdx:s})}if(e.additive){let e=this.state.selection.filter(e=>!o.some(t=>t.kind===e.kind&&t.curveIdx===e.curveIdx&&t.kfIdx===e.kfIdx));this.state.selection=[...e,...o]}else this.state.selection=o}autoFit(){let{data:e}=this.state,t=this._chartArea(),n=1/0,r=-1/0,i=1/0,a=-1/0;for(let t=0;t<e.curves.length;t++)if(!this.state.hiddenCurves.has(t))for(let o of e.curves[t].keyframes)n=Math.min(n,o.time),r=Math.max(r,o.time),i=Math.min(i,o.value),a=Math.max(a,o.value);if(!isFinite(n))return;let o=(r-n)*.15||.5,s=(a-i)*.2||2;this.state.timeScale=(t.right-t.left)/(r-n+2*o),this.state.timeOffset=n-o,this.state.valueScale=(t.bottom-t.top)/(a-i+2*s),this.state.valueOffset=i-s}frameSelection(){let{data:e,selection:t}=this.state;if(t.length===0){this.autoFit();return}let n=this._chartArea(),r=1/0,i=-1/0,a=1/0,o=-1/0;for(let n of t){if(n.kind!==`keyframe`)continue;let t=e.curves[n.curveIdx].keyframes[n.kfIdx];r=Math.min(r,t.time),i=Math.max(i,t.time),a=Math.min(a,t.value),o=Math.max(o,t.value)}if(!isFinite(r))return;let s=(i-r)*.25||.5,c=(o-a)*.3||2;this.state.timeScale=(n.right-n.left)/(i-r+2*s),this.state.timeOffset=r-s,this.state.valueScale=(n.bottom-n.top)/(o-a+2*c),this.state.valueOffset=a-c}deleteSelected(){let{data:e}=this.state,t=new Map;for(let e of this.state.selection)e.kind===`keyframe`&&(t.has(e.curveIdx)||t.set(e.curveIdx,[]),t.get(e.curveIdx).push(e.kfIdx));for(let[n,r]of t)for(let t of r.sort((e,t)=>t-e))e.curves[n].keyframes.splice(t,1);this.state.selection=[]}_evalCurveAt(e,t){let n=this.state.data.curves[e].keyframes;if(n.length===0)return 0;if(t<=n[0].time)return n[0].value;if(t>=n[n.length-1].time)return n[n.length-1].value;let r=0;for(let e=0;e<n.length-1;e++)if(t<=n[e+1].time){r=e;break}let i=n[r],a=n[r+1];if(i.outTangent.type===`stepped`)return i.value;let o=this._computeTangent(e,r,`out`),s=this._computeTangent(e,r+1,`in`),c=i.time,l=i.value,u=a.time,d=a.value,f=c+o.dx,p=l+o.dy,m=u+s.dx,h=d+s.dy,g=0,_=1;for(let e=0;e<32;e++){let e=(g+_)/2,n=1-e;n*n*n*c+3*n*n*e*f+3*n*e*e*m+e*e*e*u<t?g=e:_=e}let v=(g+_)/2,y=1-v;return y*y*y*l+3*y*y*v*p+3*y*v*v*h+v*v*v*d}totalDuration(){let e=0;for(let t of this.state.data.curves)for(let n of t.keyframes)e=Math.max(e,n.time);return e||1}getPlayhead(){return this.state.playHead}setPlayhead(e,t=!0){this.state.playHead=Math.max(0,e),t&&this.redraw(),this._firePlayheadChange()}stepPlayhead(e,t=!0){this.setPlayhead(this.state.playHead+e,t)}getValuesAt(e){let t={},{curves:n}=this.state.data;for(let r=0;r<n.length;r++)t[n[r].name]=this._evalCurveAt(r,e);return t}getValuesAtPlayhead(){return this.getValuesAt(this.state.playHead)}_onPlayheadChange;onPlayheadChange(e){this._onPlayheadChange=e??void 0}_firePlayheadChange(){this._onPlayheadChange?.(this.state.playHead,this.getValuesAtPlayhead())}_niceTimeStep(e){return[.001,.002,.005,.01,.02,.05,.1,.2,.25,.5,1,2,5,10,20,50].find(t=>t>=e)??50}_niceValueStep(e){if(e<=0)return 1;let t=10**Math.floor(Math.log10(e)),n=e/t;return(n<1.5?1:n<3.5?2:n<7.5?5:10)*t}_formatTime(e,t){return t>=1?`${e.toFixed(0)}s`:t>=.1?`${e.toFixed(1)}s`:t>=.01?`${e.toFixed(2)}s`:`${e.toFixed(3)}s`}_el(e,t,n){let r=document.createElementNS(`http://www.w3.org/2000/svg`,e);for(let[e,n]of Object.entries(t))r.setAttribute(e,String(n));return n!=null&&(r.textContent=n),this.svg.appendChild(r),r}_svgPoint(e){let t=this.svg.getBoundingClientRect();return{x:e.clientX-t.left,y:e.clientY-t.top}}_inside(e,t){return e.x>=t.left&&e.x<=t.right&&e.y>=t.top&&e.y<=t.bottom}}});
|
package/package.json
CHANGED
package/dist/my-lib.umd.cjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports):typeof define==`function`&&define.amd?define([`exports`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e[`my-lib`]={}))})(this,function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t={showRuler:!0,showYAxis:!0,yAxisWidth:44,rulerHeight:20,showSidebar:!0,sidebarWidth:140};function n(e,t,n=`spline`){return{time:e,value:t,inTangent:{type:n,slope:0},outTangent:{type:n,slope:0}}}var r={curves:[{name:`translateX`,color:`#e06060`,keyframes:[n(0,0),n(.5,3),n(1,0),n(1.5,-3),n(2,0)]},{name:`translateY`,color:`#60c060`,keyframes:[n(0,0,`linear`),n(.3,5,`flat`),n(.6,0,`linear`),n(.9,3,`flat`),n(1.2,0,`linear`)]},{name:`translateZ`,color:`#6090e0`,keyframes:[n(0,-2),n(1,2),n(2,-2)]}]},i=40;e.Graph=class{svg;config;state;constructor(e,n={},i=r){if(typeof e==`string`){let t=document.querySelector(`#${e}`);if(!t)throw Error(`Container not found: '${e}'`);this.svg=t}else this.svg=document.createElementNS(`http://www.w3.org/2000/svg`,`svg`),e.appendChild(this.svg),this.svg.style.cssText=`width:100%;height:100%;user-select:none;display:block;outline:none`,this.svg.setAttribute(`tabindex`,`0`);this.config={...t,...n},this.state={svgWidth:this.svg.clientWidth,svgHeight:this.svg.clientHeight,timeScale:200,timeOffset:-.1,valueScale:50,valueOffset:-6,playHead:0,selection:[],hiddenCurves:new Set,data:i},this._setupEvents(),new ResizeObserver(()=>{this.state.svgWidth=this.svg.clientWidth,this.state.svgHeight=this.svg.clientHeight,this.redraw()}).observe(this.svg),requestAnimationFrame(()=>{this.state.svgWidth=this.svg.clientWidth,this.state.svgHeight=this.svg.clientHeight,this.autoFit(),this.redraw()})}timeToX(e){return this._chartLeft()+(e-this.state.timeOffset)*this.state.timeScale}xToTime(e){return(e-this._chartLeft())/this.state.timeScale+this.state.timeOffset}valueToY(e){return this._chartBottom()-(e-this.state.valueOffset)*this.state.valueScale}yToValue(e){return(this._chartBottom()-e)/this.state.valueScale+this.state.valueOffset}_chartBottom(){return this.state.svgHeight-this.config.rulerHeight}_chartLeft(){return(this.config.showSidebar?this.config.sidebarWidth:0)+(this.config.showYAxis?this.config.yAxisWidth:0)}_chartArea(){return{left:this._chartLeft(),right:this.state.svgWidth,top:0,bottom:this._chartBottom()}}_rulerDims(){let{rulerHeight:e}=this.config,{svgWidth:t,svgHeight:n}=this.state;return{top:n-e,bottom:n,left:this._chartLeft(),right:t,height:e}}_computeTangent(e,t,n){let r=this.state.data.curves[e].keyframes,i=r[t],a=n===`in`?i.inTangent:i.outTangent,o=t>0?r[t-1]:null,s=t<r.length-1?r[t+1]:null;if(a.type===`fixed`){let e=n===`out`?s:o;if(!e)return{dx:0,dy:0};let t=n===`out`?(e.time-i.time)/3:-(i.time-e.time)/3;return{dx:t,dy:a.slope*t}}if(a.type===`flat`)return{dx:n===`out`?s?(s.time-i.time)/3:.3:o?-(i.time-o.time)/3:-.3,dy:0};if(a.type===`linear`)return n===`out`&&s?{dx:(s.time-i.time)/3,dy:(s.value-i.value)/3}:n===`in`&&o?{dx:(o.time-i.time)/3,dy:(o.value-i.value)/3}:{dx:0,dy:0};let c=0;if(o&&s?c=(s.value-o.value)/(s.time-o.time):s?c=(s.value-i.value)/(s.time-i.time):o&&(c=(i.value-o.value)/(i.time-o.time)),n===`out`&&s){let e=(s.time-i.time)/3;return{dx:e,dy:c*e}}if(n===`in`&&o){let e=-(i.time-o.time)/3;return{dx:e,dy:c*e}}return{dx:0,dy:0}}_tangentHandlePos(e,t,n){let r=this.state.data.curves[e].keyframes[t],a=this.timeToX(r.time),o=this.valueToY(r.value),s=this._computeTangent(e,t,n),c=s.dx*this.state.timeScale,l=-s.dy*this.state.valueScale,u=Math.hypot(c,l);return u<1e-6?{x:a+(n===`out`?i:-i),y:o}:{x:a+c/u*i,y:o+l/u*i}}_isKfSelected(e,t){return this.state.selection.some(n=>n.kind===`keyframe`&&n.curveIdx===e&&n.kfIdx===t)}_isTangentSelected(e,t,n){return this.state.selection.some(r=>r.kind===`tangent`&&r.curveIdx===e&&r.kfIdx===t&&r.side===n)}_hasHandleVisible(e,t){return this._isKfSelected(e,t)||this._isTangentSelected(e,t,`in`)||this._isTangentSelected(e,t,`out`)}redraw(){this.svg.innerHTML=``,this._addClipPath(),this._drawBackground(),this._drawGrid(),this.config.showYAxis&&this._drawYAxis(),this._drawCurves(),this._drawKeyframes(),this._drawTangentHandles(),this.config.showRuler&&this._drawRuler(),this.state.drag?.type===`marquee`&&this._drawMarquee(),this._drawPlayhead(),this.config.showSidebar&&this._drawSidebar()}_addClipPath(){let e=this._chartArea(),t=`http://www.w3.org/2000/svg`,n=document.createElementNS(t,`defs`),r=document.createElementNS(t,`clipPath`);r.setAttribute(`id`,`chart-clip`);let i=document.createElementNS(t,`rect`);i.setAttribute(`x`,String(e.left)),i.setAttribute(`y`,String(e.top)),i.setAttribute(`width`,String(e.right-e.left)),i.setAttribute(`height`,String(e.bottom-e.top)),r.appendChild(i),n.appendChild(r),this.svg.appendChild(n)}_drawBackground(){let{svgWidth:e,svgHeight:t}=this.state;this._el(`rect`,{x:0,y:0,width:e,height:t,fill:`#1c2230`})}_drawGrid(){let e=this._chartArea(),t=this.yToValue(e.top),n=this.yToValue(e.bottom),r=this._niceValueStep((t-n)/8);for(let i=Math.ceil(n/r)*r;i<=t+1e-9;i+=r){let t=this.valueToY(i),n=Math.abs(i)<r*.01;this._el(`line`,{x1:e.left,y1:t,x2:e.right,y2:t,stroke:n?`#2e4a6e`:`#1e2d40`,"stroke-width":n?1.5:1})}let i=this.xToTime(e.right)-this.xToTime(e.left),a=this._niceTimeStep(i/8);for(let t=Math.ceil(this.xToTime(e.left)/a)*a;t<=this.xToTime(e.right)+1e-9;t+=a){let n=this.timeToX(t);this._el(`line`,{x1:n,y1:e.top,x2:n,y2:e.bottom,stroke:`#1e2d40`,"stroke-width":1})}}_drawYAxis(){let{yAxisWidth:e}=this.config,t=this.config.showSidebar?this.config.sidebarWidth:0,n=this._chartArea();this._el(`rect`,{x:t,y:n.top,width:e,height:n.bottom-n.top,fill:`#141b26`});let r=this.yToValue(n.top),i=this.yToValue(n.bottom),a=this._niceValueStep((r-i)/8);for(let o=Math.ceil(i/a)*a;o<=r+1e-9;o+=a){let r=this.valueToY(o);if(r<n.top+5||r>n.bottom-3)continue;this._el(`line`,{x1:t+e-3,y1:r,x2:t+e,y2:r,stroke:`#3a4a60`,"stroke-width":1});let i=Math.abs(o)<a*.001?`0`:Math.abs(o)>=10?o.toFixed(0):o.toPrecision(2).replace(/\.?0+$/,``);this._el(`text`,{x:t+e-5,y:r+3.5,fill:`#4a5a70`,"font-size":9,"text-anchor":`end`,"font-family":`system-ui,sans-serif`},i)}this._el(`line`,{x1:t+e,y1:n.top,x2:t+e,y2:n.bottom,stroke:`#263040`,"stroke-width":1})}_drawSidebar(){let{sidebarWidth:e,data:t}={...this.config,data:this.state.data},{svgHeight:n}=this.state;this._el(`rect`,{x:0,y:0,width:e,height:n,fill:`#111720`}),this._el(`line`,{x1:e,y1:0,x2:e,y2:n,stroke:`#1e2d40`,"stroke-width":1}),this._el(`text`,{x:10,y:16,fill:`#3a4a5a`,"font-size":9,"font-family":`system-ui,sans-serif`,"font-weight":`600`,"letter-spacing":`1`},`CURVES`),this._el(`line`,{x1:0,y1:22,x2:e,y2:22,stroke:`#1a2535`,"stroke-width":1});for(let n=0;n<t.curves.length;n++){let r=t.curves[n],i=!this.state.hiddenCurves.has(n),a=this.state.selection.some(e=>e.curveIdx===n),o=30+n*24;a&&this._el(`rect`,{x:0,y:o-1,width:e,height:24,fill:`#1a2840`}),this._el(`rect`,{x:8,y:o+7,width:10,height:10,rx:2,fill:i?r.color:`#2a3040`}),this._el(`text`,{x:24,y:o+16,fill:i?`#b0bcc8`:`#3a4a5a`,"font-size":11,"font-family":`system-ui,sans-serif`},r.name);let s=e-14,c=o+12;i?(this._el(`ellipse`,{cx:s,cy:c,rx:5,ry:3.5,fill:`none`,stroke:`#4a5a70`,"stroke-width":1.2}),this._el(`circle`,{cx:s,cy:c,r:1.8,fill:`#4a5a70`})):(this._el(`ellipse`,{cx:s,cy:c,rx:5,ry:3.5,fill:`none`,stroke:`#2a3a4a`,"stroke-width":1.2}),this._el(`line`,{x1:s-6,y1:c-4,x2:s+6,y2:c+4,stroke:`#2a3a4a`,"stroke-width":1.5}))}}_drawCurves(){let{data:e}=this.state,t=this._chartArea();for(let n=0;n<e.curves.length;n++){if(this.state.hiddenCurves.has(n))continue;let r=e.curves[n],i=r.keyframes;if(i.length===0)continue;let a=this.state.selection.some(e=>e.curveIdx===n);if(i.length===1){let e=this.valueToY(i[0].value);this._el(`line`,{x1:t.left,y1:e,x2:t.right,y2:e,stroke:r.color,"stroke-width":a?2:1.5,opacity:.7,"clip-path":`url(#chart-clip)`});continue}let o=``;for(let e=0;e<i.length-1;e++){let t=i[e],r=i[e+1],a=this.timeToX(t.time),s=this.valueToY(t.value),c=this.timeToX(r.time),l=this.valueToY(r.value);if(e===0&&(o+=`M ${a} ${s} `),t.outTangent.type===`stepped`)o+=`H ${c} V ${l} `;else{let i=this._computeTangent(n,e,`out`),a=this._computeTangent(n,e+1,`in`),s=this.timeToX(t.time+i.dx),u=this.valueToY(t.value+i.dy),d=this.timeToX(r.time+a.dx),f=this.valueToY(r.value+a.dy);o+=`C ${s} ${u} ${d} ${f} ${c} ${l} `}}this._el(`path`,{d:o,stroke:r.color,"stroke-width":a?2:1.5,fill:`none`,"clip-path":`url(#chart-clip)`})}}_drawKeyframes(){let{data:e}=this.state,t=this._chartArea(),n=4.5;for(let r=0;r<e.curves.length;r++){if(this.state.hiddenCurves.has(r))continue;let i=e.curves[r];for(let e=0;e<i.keyframes.length;e++){let a=i.keyframes[e],o=this.timeToX(a.time),s=this.valueToY(a.value);if(o<t.left-n||o>t.right+n||s<t.top-n||s>t.bottom+n)continue;let c=this._isKfSelected(r,e),l=this.state.hover?.type===`keyframe`&&this.state.hover.curveIdx===r&&this.state.hover.kfIdx===e,u=`${o},${s-n} ${o+n},${s} ${o},${s+n} ${o-n},${s}`;c?this._el(`polygon`,{points:u,fill:`#ffe060`,stroke:`#fffb`,"stroke-width":.75}):this._el(`polygon`,{points:u,fill:l?i.color:`#1c222e`,stroke:i.color,"stroke-width":1.5})}}}_drawTangentHandles(){let{data:e}=this.state;for(let t=0;t<e.curves.length;t++){if(this.state.hiddenCurves.has(t))continue;let n=e.curves[t];for(let e=0;e<n.keyframes.length;e++){if(!this._hasHandleVisible(t,e))continue;let r=n.keyframes[e],i=this.timeToX(r.time),a=this.valueToY(r.value);for(let o of[`in`,`out`]){if(o===`in`&&e===0||o===`out`&&e===n.keyframes.length-1||o===`out`&&r.outTangent.type===`stepped`)continue;let s=this._tangentHandlePos(t,e,o),c=this._isTangentSelected(t,e,o),l=this.state.hover?.type===`tangent`&&this.state.hover.curveIdx===t&&this.state.hover.kfIdx===e&&this.state.hover.side===o;this._el(`line`,{x1:i,y1:a,x2:s.x,y2:s.y,stroke:n.color,"stroke-width":1,opacity:.5}),this._el(`circle`,{cx:s.x,cy:s.y,r:3.5,fill:c?`#ffe060`:l?`#fff`:n.color,stroke:`#fff5`,"stroke-width":.5})}}}}_drawRuler(){let e=this._rulerDims(),t=this._chartArea(),{top:n,left:r,right:i,height:a}=e;this._el(`rect`,{x:r,y:n,width:i-r,height:a,fill:`#141b26`}),this._el(`line`,{x1:r,y1:n,x2:i,y2:n,stroke:`#263040`,"stroke-width":1});let o=this.xToTime(t.right)-this.xToTime(t.left),s=this._niceTimeStep(o/8);for(let e=Math.ceil(this.xToTime(t.left)/s)*s;e<=this.xToTime(i)+1e-9;e+=s){let t=this.timeToX(e);t<r||(this._el(`line`,{x1:t,y1:n,x2:t,y2:n+4,stroke:`#3a4a60`,"stroke-width":1}),this._el(`text`,{x:t+2,y:n+13,fill:`#4a5a70`,"font-size":9,"font-family":`system-ui,sans-serif`},this._formatTime(e,s)))}}_drawMarquee(){let e=this.state.drag;if(e?.type!==`marquee`)return;let t=Math.min(e.x0,e.x1),n=Math.min(e.y0,e.y1),r=Math.abs(e.x1-e.x0),i=Math.abs(e.y1-e.y0);this._el(`rect`,{x:t,y:n,width:r,height:i,fill:`rgba(100,150,255,0.06)`,stroke:`#6496ff`,"stroke-width":1,"stroke-dasharray":`3 2`})}_drawPlayhead(){let{playHead:e,svgWidth:t}=this.state,n=this._chartArea(),r=this._rulerDims(),i=this.timeToX(e);i<this.config.yAxisWidth||i>t||(this._el(`line`,{x1:i,y1:n.top,x2:i,y2:n.bottom,stroke:`#50e880`,"stroke-width":1,opacity:.45}),this._el(`polygon`,{points:`${i-5},${r.top} ${i+5},${r.top} ${i},${r.top+7}`,fill:`#50e880`,opacity:.9}))}_setupEvents(){this.svg.addEventListener(`contextmenu`,e=>e.preventDefault()),this.svg.addEventListener(`wheel`,e=>{e.preventDefault();let{x:t,y:n}=this._svgPoint(e),r=e.deltaY<0?1.12:1/1.12;if(e.altKey){let e=this.yToValue(n);this.state.valueScale=Math.max(2,Math.min(1e4,this.state.valueScale*r)),this.state.valueOffset=e-(this._chartBottom()-n)/this.state.valueScale}else{let e=this.xToTime(t);this.state.timeScale=Math.max(5,Math.min(2e4,this.state.timeScale*r)),this.state.timeOffset=e-(t-this.config.yAxisWidth)/this.state.timeScale}this.redraw()},{passive:!1}),this.svg.addEventListener(`mousedown`,e=>{let{x:t,y:n}=this._svgPoint(e),r=this._chartArea(),i=this._rulerDims();if(e.altKey&&e.button===2){e.preventDefault(),this.state.drag={type:`zoom`,startX:t,startY:n,startTimeScale:this.state.timeScale,startValueScale:this.state.valueScale,startTimeOffset:this.state.timeOffset,startValueOffset:this.state.valueOffset,pivotTime:this.xToTime(t),pivotValue:this.yToValue(n)},this.svg.style.cursor=`zoom-in`;return}if(e.altKey&&(e.button===0||e.button===1)||e.button===2){e.preventDefault(),this.state.drag={type:`pan`,startX:t,startY:n,startTimeOffset:this.state.timeOffset,startValueOffset:this.state.valueOffset},this.svg.style.cursor=`grabbing`;return}if(this.config.showSidebar&&t>=0&&t<=this.config.sidebarWidth&&e.button===0){let e=Math.floor((n-30)/24);e>=0&&e<this.state.data.curves.length&&(this.state.hiddenCurves.has(e)?this.state.hiddenCurves.delete(e):this.state.hiddenCurves.add(e),this.redraw());return}if(this._inside({x:t,y:n},i)){this.state.drag={type:`scrubPlayhead`},this.state.playHead=Math.max(0,this.xToTime(t)),this.redraw();return}if(e.button===1&&this._inside({x:t,y:n},r)){e.preventDefault();let r=this.state.selection.filter(e=>e.kind===`keyframe`);r.length>0&&(this.state.drag={type:`moveKeyframes`,startX:t,startY:n,entries:r.map(e=>({curveIdx:e.curveIdx,kfIdx:e.kfIdx,kfRef:this.state.data.curves[e.curveIdx].keyframes[e.kfIdx],origTime:this.state.data.curves[e.curveIdx].keyframes[e.kfIdx].time,origValue:this.state.data.curves[e.curveIdx].keyframes[e.kfIdx].value}))},this.redraw());return}if(!this._inside({x:t,y:n},r)||e.button!==0)return;let a=this._hitTangent(t,n);if(a){let{curveIdx:t,kfIdx:n,side:r}=a,i=this._isKfSelected(t,n);if(!e.shiftKey)this.state.selection=[{kind:`tangent`,curveIdx:t,kfIdx:n,side:r}];else{let e=this._isTangentSelected(t,n,r);this.state.selection=e?this.state.selection.filter(e=>!(e.kind===`tangent`&&e.curveIdx===t&&e.kfIdx===n&&e.side===r)):[...this.state.selection,{kind:`tangent`,curveIdx:t,kfIdx:n,side:r}]}this.state.drag={type:`moveTangent`,curveIdx:t,kfIdx:n,side:r,unified:i},this.redraw();return}let o=this._hitKeyframe(t,n);if(o){let{curveIdx:r,kfIdx:i}=o,a=this._isKfSelected(r,i);e.shiftKey?this.state.selection=a?this.state.selection.filter(e=>!(e.kind===`keyframe`&&e.curveIdx===r&&e.kfIdx===i)):[...this.state.selection,{kind:`keyframe`,curveIdx:r,kfIdx:i}]:a||(this.state.selection=[{kind:`keyframe`,curveIdx:r,kfIdx:i}]),this._isKfSelected(r,i)&&(this.state.drag={type:`moveKeyframes`,startX:t,startY:n,entries:this.state.selection.filter(e=>e.kind===`keyframe`).map(e=>({curveIdx:e.curveIdx,kfIdx:e.kfIdx,kfRef:this.state.data.curves[e.curveIdx].keyframes[e.kfIdx],origTime:this.state.data.curves[e.curveIdx].keyframes[e.kfIdx].time,origValue:this.state.data.curves[e.curveIdx].keyframes[e.kfIdx].value}))}),this.redraw();return}if(Math.abs(this.timeToX(this.state.playHead)-t)<6){this.state.drag={type:`scrubPlayhead`},this.redraw();return}e.shiftKey||(this.state.selection=[]),this.state.drag={type:`marquee`,x0:t,y0:n,x1:t,y1:n,additive:e.shiftKey},this.redraw()}),window.addEventListener(`mousemove`,e=>{let{x:t,y:n}=this._svgPoint(e),{drag:r}=this.state,i=this._chartArea(),a=this._rulerDims();if(!r){let e=this._hitTangent(t,n),r=e?null:this._hitKeyframe(t,n),i=Math.abs(this.timeToX(this.state.playHead)-t)<6,o=this._inside({x:t,y:n},a);e?(this.state.hover={type:`tangent`,...e},this.svg.style.cursor=`crosshair`):r?(this.state.hover={type:`keyframe`,...r},this.svg.style.cursor=`pointer`):i||o?(this.state.hover={type:`playhead`},this.svg.style.cursor=`col-resize`):(this.state.hover=void 0,this.svg.style.cursor=`default`),this.redraw();return}if(r.type===`pan`){this.state.timeOffset=r.startTimeOffset-(t-r.startX)/this.state.timeScale,this.state.valueOffset=r.startValueOffset+(n-r.startY)/this.state.valueScale,this.redraw();return}if(r.type===`zoom`){let e=.007,i=Math.exp((t-r.startX)*e),a=Math.exp((n-r.startY)*-e);this.state.timeScale=Math.max(5,Math.min(2e4,r.startTimeScale*i)),this.state.valueScale=Math.max(2,Math.min(1e4,r.startValueScale*a)),this.state.timeOffset=r.pivotTime-(r.startX-this.config.yAxisWidth)/this.state.timeScale,this.state.valueOffset=r.pivotValue-(this._chartBottom()-r.startY)/this.state.valueScale,this.redraw();return}if(r.type===`scrubPlayhead`){this.state.playHead=Math.max(0,this.xToTime(t)),this.redraw(),this._firePlayheadChange();return}if(r.type===`marquee`){r.x1=Math.max(i.left,Math.min(t,i.right)),r.y1=Math.max(i.top,Math.min(n,i.bottom)),this._applyMarqueeSelection(r),this.redraw();return}if(r.type===`moveKeyframes`){let i=t-r.startX,a=n-r.startY;e.shiftKey&&!r.axisLock&&(Math.abs(i)>5||Math.abs(a)>5)&&(r.axisLock=Math.abs(i)>=Math.abs(a)?`x`:`y`),e.shiftKey||(r.axisLock=void 0);let o=r.axisLock===`y`?0:i/this.state.timeScale,s=r.axisLock===`x`?0:-a/this.state.valueScale;for(let e of r.entries)e.kfRef.time=e.origTime+o,e.kfRef.value=e.origValue+s;let c=new Set(r.entries.map(e=>e.curveIdx));for(let e of c)this.state.data.curves[e].keyframes.sort((e,t)=>e.time-t.time);for(let e of r.entries){let t=this.state.data.curves[e.curveIdx].keyframes.indexOf(e.kfRef);t>=0&&(e.kfIdx=t)}let l=r.entries.map(e=>({kind:`keyframe`,curveIdx:e.curveIdx,kfIdx:e.kfIdx}));this.state.selection=[...this.state.selection.filter(e=>e.kind!==`keyframe`),...l],this.redraw();return}if(r.type===`moveTangent`){let{curveIdx:e,kfIdx:i,side:a,unified:o}=r,s=this.state.data.curves[e].keyframes[i],c=a===`in`?s.inTangent:s.outTangent,l=this.timeToX(s.time),u=this.valueToY(s.value),d=t-l,f=n-u,p=a===`out`?Math.max(d,1):Math.min(d,-1),m=-(f*this.state.timeScale)/(p*this.state.valueScale);if(c.type=`fixed`,c.slope=m,o){let e=a===`in`?s.outTangent:s.inTangent;e.type!==`stepped`&&(e.type=`fixed`,e.slope=m)}this.redraw();return}}),window.addEventListener(`mouseup`,()=>{this.state.drag=void 0,this.svg.style.cursor=`default`,this.redraw()}),window.addEventListener(`keydown`,e=>{let t=e.target;if(!(t.tagName===`INPUT`||t.tagName===`TEXTAREA`))switch(e.key){case`a`:case`A`:this.autoFit(),this.redraw();break;case`f`:case`F`:this.frameSelection(),this.redraw();break;case`s`:case`S`:{let e=this.state.playHead;for(let t=0;t<this.state.data.curves.length;t++){let r=this.state.data.curves[t];if(r.keyframes.some(t=>Math.abs(t.time-e)<1e-6))continue;let i=this._evalCurveAt(t,e);r.keyframes.push(n(e,i)),r.keyframes.sort((e,t)=>e.time-t.time)}this.redraw();break}case`d`:case`D`:{let{data:e,selection:t}=this.state,n=t.filter(e=>e.kind===`keyframe`),r=n.length>0?n.map(t=>e.curves[t.curveIdx].keyframes[t.kfIdx]):e.curves.flatMap(e=>e.keyframes);for(let e of r)e.inTangent={type:`spline`,slope:0},e.outTangent={type:`spline`,slope:0};this.redraw();break}case`Delete`:case`Backspace`:this.deleteSelected(),this.redraw();break}})}_hitKeyframe(e,t){let{data:n}=this.state,r=null;for(let i=0;i<n.curves.length;i++)for(let a=0;a<n.curves[i].keyframes.length;a++){let o=n.curves[i].keyframes[a],s=Math.hypot(this.timeToX(o.time)-e,this.valueToY(o.value)-t);s<9&&(!r||s<r.dist)&&(r={curveIdx:i,kfIdx:a,dist:s})}return r?{curveIdx:r.curveIdx,kfIdx:r.kfIdx}:null}_hitTangent(e,t){let{data:n}=this.state;for(let r=0;r<n.curves.length;r++)for(let i=0;i<n.curves[r].keyframes.length;i++){if(!this._hasHandleVisible(r,i))continue;let a=n.curves[r].keyframes[i];for(let o of[`in`,`out`]){if(o===`in`&&i===0||o===`out`&&i===n.curves[r].keyframes.length-1||o===`out`&&a.outTangent.type===`stepped`)continue;let s=this._tangentHandlePos(r,i,o);if(Math.hypot(s.x-e,s.y-t)<8)return{curveIdx:r,kfIdx:i,side:o}}}return null}_applyMarqueeSelection(e){let{data:t}=this.state,n=Math.min(e.x0,e.x1),r=Math.max(e.x0,e.x1),i=Math.min(e.y0,e.y1),a=Math.max(e.y0,e.y1),o=[];for(let e=0;e<t.curves.length;e++)for(let s=0;s<t.curves[e].keyframes.length;s++){let c=t.curves[e].keyframes[s],l=this.timeToX(c.time),u=this.valueToY(c.value);l>=n&&l<=r&&u>=i&&u<=a&&o.push({kind:`keyframe`,curveIdx:e,kfIdx:s})}if(e.additive){let e=this.state.selection.filter(e=>!o.some(t=>t.kind===e.kind&&t.curveIdx===e.curveIdx&&t.kfIdx===e.kfIdx));this.state.selection=[...e,...o]}else this.state.selection=o}autoFit(){let{data:e}=this.state,t=this._chartArea(),n=1/0,r=-1/0,i=1/0,a=-1/0;for(let t=0;t<e.curves.length;t++)if(!this.state.hiddenCurves.has(t))for(let o of e.curves[t].keyframes)n=Math.min(n,o.time),r=Math.max(r,o.time),i=Math.min(i,o.value),a=Math.max(a,o.value);if(!isFinite(n))return;let o=(r-n)*.15||.5,s=(a-i)*.2||2;this.state.timeScale=(t.right-t.left)/(r-n+2*o),this.state.timeOffset=n-o,this.state.valueScale=(t.bottom-t.top)/(a-i+2*s),this.state.valueOffset=i-s}frameSelection(){let{data:e,selection:t}=this.state;if(t.length===0){this.autoFit();return}let n=this._chartArea(),r=1/0,i=-1/0,a=1/0,o=-1/0;for(let n of t){if(n.kind!==`keyframe`)continue;let t=e.curves[n.curveIdx].keyframes[n.kfIdx];r=Math.min(r,t.time),i=Math.max(i,t.time),a=Math.min(a,t.value),o=Math.max(o,t.value)}if(!isFinite(r))return;let s=(i-r)*.25||.5,c=(o-a)*.3||2;this.state.timeScale=(n.right-n.left)/(i-r+2*s),this.state.timeOffset=r-s,this.state.valueScale=(n.bottom-n.top)/(o-a+2*c),this.state.valueOffset=a-c}deleteSelected(){let{data:e}=this.state,t=new Map;for(let e of this.state.selection)e.kind===`keyframe`&&(t.has(e.curveIdx)||t.set(e.curveIdx,[]),t.get(e.curveIdx).push(e.kfIdx));for(let[n,r]of t)for(let t of r.sort((e,t)=>t-e))e.curves[n].keyframes.splice(t,1);this.state.selection=[]}_evalCurveAt(e,t){let n=this.state.data.curves[e].keyframes;if(n.length===0)return 0;if(t<=n[0].time)return n[0].value;if(t>=n[n.length-1].time)return n[n.length-1].value;let r=0;for(let e=0;e<n.length-1;e++)if(t<=n[e+1].time){r=e;break}let i=n[r],a=n[r+1];if(i.outTangent.type===`stepped`)return i.value;let o=this._computeTangent(e,r,`out`),s=this._computeTangent(e,r+1,`in`),c=i.time,l=i.value,u=a.time,d=a.value,f=c+o.dx,p=l+o.dy,m=u+s.dx,h=d+s.dy,g=0,_=1;for(let e=0;e<32;e++){let e=(g+_)/2,n=1-e;n*n*n*c+3*n*n*e*f+3*n*e*e*m+e*e*e*u<t?g=e:_=e}let v=(g+_)/2,y=1-v;return y*y*y*l+3*y*y*v*p+3*y*v*v*h+v*v*v*d}totalDuration(){let e=0;for(let t of this.state.data.curves)for(let n of t.keyframes)e=Math.max(e,n.time);return e||1}getPlayhead(){return this.state.playHead}setPlayhead(e,t=!0){this.state.playHead=Math.max(0,e),t&&this.redraw(),this._firePlayheadChange()}stepPlayhead(e,t=!0){this.setPlayhead(this.state.playHead+e,t)}getValuesAt(e){let t={},{curves:n}=this.state.data;for(let r=0;r<n.length;r++)t[n[r].name]=this._evalCurveAt(r,e);return t}getValuesAtPlayhead(){return this.getValuesAt(this.state.playHead)}_onPlayheadChange;onPlayheadChange(e){this._onPlayheadChange=e??void 0}_firePlayheadChange(){this._onPlayheadChange?.(this.state.playHead,this.getValuesAtPlayhead())}_niceTimeStep(e){return[.001,.002,.005,.01,.02,.05,.1,.2,.25,.5,1,2,5,10,20,50].find(t=>t>=e)??50}_niceValueStep(e){if(e<=0)return 1;let t=10**Math.floor(Math.log10(e)),n=e/t;return(n<1.5?1:n<3.5?2:n<7.5?5:10)*t}_formatTime(e,t){return t>=1?`${e.toFixed(0)}s`:t>=.1?`${e.toFixed(1)}s`:t>=.01?`${e.toFixed(2)}s`:`${e.toFixed(3)}s`}_el(e,t,n){let r=document.createElementNS(`http://www.w3.org/2000/svg`,e);for(let[e,n]of Object.entries(t))r.setAttribute(e,String(n));return n!=null&&(r.textContent=n),this.svg.appendChild(r),r}_svgPoint(e){let t=this.svg.getBoundingClientRect();return{x:e.clientX-t.left,y:e.clientY-t.top}}_inside(e,t){return e.x>=t.left&&e.x<=t.right&&e.y>=t.top&&e.y<=t.bottom}}});
|
|
File without changes
|