@ax-design/reveal-highlight
Advanced tools
Comparing version 0.1.10-beta.0 to 0.1.10
@@ -1,2 +0,3 @@ | ||
import RevealStateManager, { RevealBoundaryStore } from './RevealStateManager.js'; | ||
import { RevealStateManager } from './RevealStateManager.js'; | ||
import { RevealBoundaryStore } from './RevealBoundryStore.js'; | ||
export declare class AxRevealProvider extends HTMLElement { | ||
@@ -7,8 +8,8 @@ static readonly ElementName = "ax-reveal-provider"; | ||
export declare class AxRevealBoundary extends HTMLElement { | ||
static readonly storage: RevealStateManager; | ||
static readonly ElementName = "ax-reveal-bound"; | ||
private _storage; | ||
static readonly removeStorageEvent = "removeStorage"; | ||
static readonly attachStorageEvent = "attachStorage"; | ||
static readonly replaceStorageEvent = "replaceStorage"; | ||
static readonly stateManager: RevealStateManager; | ||
private _storage; | ||
private storage; | ||
@@ -21,4 +22,4 @@ waitForStorage(f: (storage: RevealBoundaryStore) => void): void; | ||
handlePointerMove: (ev: MouseEvent) => void; | ||
handlePointerUp: () => void; | ||
handlePointerDown: (ev: MouseEvent) => void; | ||
handlePointerUp: () => void; | ||
connectedCallback(): void; | ||
@@ -25,0 +26,0 @@ disconnectedCallback(): void; |
@@ -1,7 +0,8 @@ | ||
(function(a,b){"object"==typeof exports&&"undefined"!=typeof module?b(exports):"function"==typeof define&&define.amd?define(["exports"],b):(a=a||self,b(a.AxRevealHighlight={}))})(this,function(a){'use strict';function b(a,b){return b?new d(a):new e(a)}// interface RevealStyle { | ||
function c(){customElements.define(h.ElementName,h),customElements.define(i.ElementName,i),customElements.define(j.ElementName,j)}class d{constructor(a){if(!d.colorParser){const a=document.createElement("div");a.style.display="none !important",document.body.appendChild(a),d.colorParser=a,d.colorParserStyle=window.getComputedStyle(a)}this.el=a,this.style=window.getComputedStyle(a)}size(){return this.style.length}get(a){return this.style.getPropertyValue(a).trim()}getColor(a){return d.colorParser.style.color=this.get(a),d.colorParserStyle.color||"rgb(0, 0, 0)"}getNumber(a){// Length values in computed style will always in the unit of px | ||
(function(a,b){"object"==typeof exports&&"undefined"!=typeof module?b(exports):"function"==typeof define&&define.amd?define(["exports"],b):(a=a||self,b(a.AxRevealHighlight={}))})(this,function(a){'use strict';function b(a,b){return b?new d(a):new e(a)}function c(){customElements.define(i.ElementName,i),customElements.define(j.ElementName,j),customElements.define(k.ElementName,k)}class d{constructor(a){if(!d.colorParser){const a=document.createElement("div");a.style.display="none !important",document.body.appendChild(a),d.colorParser=a,d.colorParserStyle=window.getComputedStyle(a)}this.el=a,this.style=window.getComputedStyle(a)}size(){return this.style.length}get(a){return this.style.getPropertyValue(a).trim()}getColor(a){return d.colorParser.style.color=this.get(a),d.colorParserStyle.color||"rgb(0, 0, 0)"}getNumber(a){// Length values in computed style will always in the unit of px | ||
// Maybe NaN | ||
return parseFloat(this.get(a))}}class e{constructor(a){this.el=a,this.style=a.computedStyleMap()}size(){return this.style.size}get(a){return this.style.get(a).toString()}getColor(a){return this.get(a)}getNumber(a){const b=this.style.get(a).value;return"number"==typeof b?b:parseFloat(b);// Maybe NaN | ||
}}class f{constructor(){this._currentHashId=0,this._storage=[],this.newBoundary=()=>{const a=this._currentHashId++,c={_currentHashId:0,id:a,clientX:-1e3,clientY:-1e3,paintedClientX:-1e3,paintedClientY:-1e3,mouseInBoundary:!1,canvasList:[],dirty:!1,raf:null,destroy:()=>{this._storage.find((a,b)=>{return this._storage.splice(b,1),a===c})},addReveal:a=>{const d={canvas:a,ctx:a.getContext("2d"),cachedRevealBitmap:{width:0,height:0,color:"",opacity:0,bitmaps:[]},width:0,height:0,cachedStyle:{top:-1,left:-1,width:-1,height:-1,trueFillRadius:[0,0],cacheCanvasSize:-1,color:"",opacity:1,borderStyle:"",borderWidth:1,fillMode:"",fillRadius:0,diffuse:!0,revealAnimateSpeed:0,revealReleasedAccelerateRate:0},currentFrameId:-1,cachedFrameId:-2,cacheCanvasPaintingStyle:()=>{var c=Math.round;if(d.currentFrameId===d.cachedFrameId)return d.cachedStyle;const e=!!document.documentElement.dataset.revealCompat,f=b(a,e);if(0===f.size())return d.cachedStyle;const g=a.getBoundingClientRect(),h=f.getColor("--reveal-color"),i=h.match(/\((\d+,\s*\d+,\s*\d+)[\s\S]*?\)/),j={top:c(g.top),left:c(g.left),width:c(g.width),height:c(g.height),color:i&&1<i.length?i[1]:"0, 0, 0",opacity:f.getNumber("--reveal-opacity"),borderStyle:f.get("--reveal-border-style"),borderWidth:f.getNumber("--reveal-border-width"),fillMode:f.get("--reveal-fill-mode"),fillRadius:f.getNumber("--reveal-fill-radius"),diffuse:"true"===f.get("--reveal-diffuse"),revealAnimateSpeed:f.getNumber("--reveal-animate-speed"),revealReleasedAccelerateRate:f.getNumber("--reveal-released-accelerate-rate")},{width:k,height:l,fillMode:m,fillRadius:n}=j;let o=[0,0];switch(m){case"none":break;case"relative":o=[k,l].sort((c,a)=>c-a).map(a=>a*n);break;case"absolute":o=[n,n];break;default:throw new SyntaxError("The value of `--reveal-border-style` must be `relative`, `absolute` or `none`!");}const p=2*o[1];d.cachedStyle=Object.assign({},j,{trueFillRadius:o,cacheCanvasSize:p}),d.cachedFrameId=d.currentFrameId},cacheRevealBitmaps:()=>{if(!d.ctx)return;const{width:a,height:b,color:c,opacity:e,trueFillRadius:f,cacheCanvasSize:g}=d.cachedStyle,h=d.cachedRevealBitmap;if(a!==h.width||b!=h.height||c!=h.color||e!=h.opacity){d.width=a,d.height=b,d.canvas.width=a,d.canvas.height=b,d.cachedRevealBitmap={width:a,height:b,color:c,opacity:e,bitmaps:[]};for(const a of[0,1]){// 0 means border, 1 means fill. | ||
const b=document.createElement("canvas");b.width=g,b.height=g;const h=b.getContext("2d");if(!h)return;const i=0===a?e:.5*e,j=h.createRadialGradient(g/2,g/2,0,g/2,g/2,f[a]);j.addColorStop(0,"rgba("+c+", "+i+")"),j.addColorStop(1,"rgba("+c+", 0.0)"),h.fillStyle=j,h.fillRect(0,0,g,g);const k=h.getImageData(0,0,g,g);d.cachedRevealBitmap.bitmaps.push(k)}}},mouseInCanvas:()=>{d.cacheCanvasPaintingStyle();const{top:a,left:b,width:e,height:f}=d.cachedStyle,g=c.clientX-b,h=c.clientY-a;return!(0>g||g>e)&&!(0>h||h>f)},getAnimateGrd:(a,b)=>{if(!d.ctx)return null;const{color:c,opacity:e}=d.cachedStyle,f=e*(.2-a),g=e*(.1-.07*a),h=.1+.8*a,i=0>f?0:f,j=0>g?0:g,k=1<h?1:h;return b.addColorStop(0,`rgba(${c},${i})`),b.addColorStop(.55*k,`rgba(${c},${j})`),b.addColorStop(k,`rgba(${c}, 0)`),b},mouseUpClientX:null,mouseUpClientY:null,mouseDownAnimateStartFrame:null,mouseDownAnimateCurrentFrame:null,mouseDownAnimateReleasedFrame:null,mouseDownAnimateLogicFrame:null,mousePressed:!1,mouseReleased:!1};d.cacheCanvasPaintingStyle(),d.cacheRevealBitmaps(),c.canvasList.push(d)},removeReveal:a=>{c.canvasList.find((b,d)=>!!(b&&a===b.canvas)&&(c.canvasList.splice(d,1),!0))},onPointerEnterBoundary:()=>{c.mouseInBoundary=!0,c.raf||(c.raf=window.requestAnimationFrame(a=>c.paintAll(a)))},onPointerLeaveBoundary:()=>{c.mouseInBoundary=!1,c.paintAll(0,!0)},paintAll:(a,b)=>{(b||c.mouseInBoundary||0!==c.animationQueue.size)&&(c.animationQueue.forEach(b=>{if(!a)return;if(b.currentFrameId===a)return;b.currentFrameId=a,null===b.mouseDownAnimateStartFrame&&(b.mouseDownAnimateStartFrame=a);const d=a-b.mouseDownAnimateStartFrame;b.mouseDownAnimateCurrentFrame=d;const e=b.cachedStyle.revealAnimateSpeed,f=b.cachedStyle.revealReleasedAccelerateRate;b.mouseDownAnimateLogicFrame=b.mouseReleased&&b.mouseDownAnimateReleasedFrame?d/e+(d-b.mouseDownAnimateReleasedFrame)/e*f:d/e,1<b.mouseDownAnimateLogicFrame&&c.cleanUpAnimation(b)}),c.canvasList.forEach(d=>{d.currentFrameId=a,g(d,c,b)}),c.dirty=!0,c.paintedClientX=c.clientX,c.paintedClientY=c.clientY,c.mouseInBoundary||0!==c.animationQueue.size?window.requestAnimationFrame(a=>{c.paintAll(a)}):c.raf=null)},resetAll:()=>{c.canvasList.forEach(a=>{g(a,c)})},initializeAnimation:()=>{const a=c.canvasList.find(a=>a.mouseInCanvas());a&&(c.animationQueue.add(a),a.mouseDownAnimateStartFrame=null,a.mousePressed=!0,a.mouseReleased=!1)},switchAnimation:()=>{c.animationQueue.forEach(a=>{a.mouseReleased||(a.mouseReleased=!0,a.mouseDownAnimateReleasedFrame=a.mouseDownAnimateCurrentFrame,a.mouseUpClientX=c.clientX,a.mouseUpClientY=c.clientY)})},cleanUpAnimation:a=>{a.mouseUpClientX=null,a.mouseUpClientY=null,a.mouseDownAnimateStartFrame=null,a.mouseDownAnimateCurrentFrame=null,a.mouseDownAnimateReleasedFrame=null,a.mouseDownAnimateLogicFrame=null,a.mousePressed=!1,a.mouseReleased=!1,c.animationQueue.delete(a),g(a,c,!0)},animationQueue:new Set,getRevealAnimationConfig:()=>c.canvasList.find(a=>a.mouseInCanvas())||null,revealAnimationConfig:null,mouseUpClientX:null,mouseUpClientY:null,mouseDownAnimateStartFrame:null,mouseDownAnimateCurrentFrame:null,mouseDownAnimateReleasedFrame:null,mouseDownAnimateLogicFrame:null,mousePressed:!1,mouseReleased:!1};return this._storage.push(c),c}}}const g=(a,b,c)=>{const d=b.animationQueue.has(a);if(b.clientX===b.paintedClientX&&b.clientY===b.paintedClientY&&!d&&!c)return;if(!a.ctx)return;if(a.ctx.clearRect(0,0,a.width,a.height),b.dirty=!1,!b.mouseInBoundary&&!d)return;if(2>a.cachedRevealBitmap.bitmaps.length)return;a.cacheCanvasPaintingStyle(),a.cacheRevealBitmaps();const{top:e,left:f,width:g,height:h,cacheCanvasSize:i,trueFillRadius:j}=a.cachedStyle,k=a.cachedStyle.borderStyle,l=a.cachedStyle.borderWidth,m=a.cachedStyle.fillMode,n=b.clientX-f,o=b.clientY-e,p=0<n&&n<g&&0<o&&o<h;if(!p&&!a.cachedStyle.diffuse&&!d)return;let q=0,r=0,s=0,t=0;switch(k){case"full":q=l,r=l,s=g-2*l,t=h-2*l;break;case"half":q=0,r=l,s=g,t=h-2*l;break;case"none":q=0,r=0,s=g,t=h;break;default:throw new SyntaxError("The value of `--reveal-border-style` must be `full`, `half` or `none`!");}const u=n-i/2,v=o-i/2;if(isNaN(n)||isNaN(o))return;if(b.mouseInBoundary&&("none"!==k&&(a.ctx.putImageData(a.cachedRevealBitmap.bitmaps[0],u,v,-u,-v,g,h),a.ctx.clearRect(q,r,s,t)),"none"!=m&&p&&a.ctx.putImageData(a.cachedRevealBitmap.bitmaps[1],u,v,q-u,r-v,s,t)),!a.mousePressed||!a.mouseDownAnimateLogicFrame)return;let w;w=a.mouseReleased&&a.mouseUpClientX&&a.mouseUpClientY?a.ctx.createRadialGradient(a.mouseUpClientX-f,a.mouseUpClientY-e,0,a.mouseUpClientX-f,a.mouseUpClientY-e,j[1]):a.ctx.createRadialGradient(n,o,0,n,o,j[1]),a.getAnimateGrd(a.mouseDownAnimateLogicFrame,w),a.ctx.fillStyle=w,a.ctx.fillRect(q,r,1.5*s,1.5*t)};class h extends HTMLElement{constructor(){super(...arguments),this.stateManager=new f}}h.ElementName="ax-reveal-provider";class i extends HTMLElement{constructor(){super(...arguments),this.updatePointerPosition=a=>{this.waitForStorage(b=>{b.clientX=a.clientX,b.clientY=a.clientY})},this.handlePointerEnter=()=>this.waitForStorage(a=>a.onPointerEnterBoundary()),this.handlePointerLeave=()=>this.waitForStorage(a=>a.onPointerLeaveBoundary()),this.handlePointerMove=a=>this.updatePointerPosition(a),this.handlePointerDown=a=>this.waitForStorage(b=>{this.updatePointerPosition(a),b.initializeAnimation()}),this.handlePointerUp=()=>this.waitForStorage(a=>a.switchAnimation())}get storage(){return this._storage}set storage(a){const b=this._storage;b&&this.dispatchEvent(new CustomEvent(i.removeStorageEvent,{detail:b})),a&&(this._storage=a,this.dispatchEvent(new CustomEvent(i.attachStorageEvent,{detail:this._storage})),b&&this.dispatchEvent(new CustomEvent(i.replaceStorageEvent,{detail:{old:b,new:a}})))}waitForStorage(a){this.storage===void 0?this.addEventListener(i.attachStorageEvent,()=>a(this.storage),{once:!0}):a(this.storage)}appendStorage(a=!1){if(!a&&this.storage)return;const b=this.closest(h.ElementName),c=b?b.stateManager:i.storage;this.storage=c.newBoundary()}connectedCallback(){this.appendStorage(!0),this.addEventListener("pointerenter",this.handlePointerEnter),this.addEventListener("pointerleave",this.handlePointerLeave),this.addEventListener("pointermove",this.handlePointerMove),this.addEventListener("pointerdown",this.handlePointerDown),this.addEventListener("pointerup",this.handlePointerUp)}disconnectedCallback(){this.storage=void 0}}i.storage=new f,i.ElementName="ax-reveal-bound",i.removeStorageEvent="removeStorage",i.attachStorageEvent="attachStorage",i.replaceStorageEvent="replaceStorage";class j extends HTMLElement{constructor(){super(),this.root=this.attachShadow({mode:"open"}),this.root.innerHTML=` | ||
}}class f{constructor(a,c){this.pxRatio=window.devicePixelRatio||1,this.paintedWidth=0,this.paintedHeight=0,this.cachedRevealBitmap={radius:0,color:"",opacity:0,bitmaps:[]},this.cachedStyle={top:-1,left:-1,width:-1,height:-1,trueFillRadius:[0,0],color:"",opacity:1,borderStyle:"",borderWidth:1,fillMode:"",fillRadius:0,diffuse:!0,revealAnimateSpeed:0,revealReleasedAccelerateRate:0},this.mouseUpClientX=null,this.mouseUpClientY=null,this.mouseDownAnimateStartFrame=null,this.mouseDownAnimateCurrentFrame=null,this.mouseDownAnimateReleasedFrame=null,this.mouseDownAnimateLogicFrame=null,this.mousePressed=!1,this.mouseReleased=!1,this.currentFrameId=-1,this.cachedFrameId=-2,this.cacheCanvasPaintingStyle=()=>{var a=Math.round;if(this.currentFrameId===this.cachedFrameId)return;const c=!!document.documentElement.dataset.revealCompat,d=b(this.canvas,c);if(0===d.size())return;const e=this.canvas.getBoundingClientRect(),f=d.getColor("--reveal-color"),g=f.match(/\((\d+,\s*\d+,\s*\d+)[\s\S]*?\)/),h={top:a(e.top),left:a(e.left),width:a(e.width),height:a(e.height),color:g&&1<g.length?g[1]:"0, 0, 0",opacity:d.getNumber("--reveal-opacity"),borderStyle:d.get("--reveal-border-style"),borderWidth:d.getNumber("--reveal-border-width"),fillMode:d.get("--reveal-fill-mode"),fillRadius:d.getNumber("--reveal-fill-radius"),diffuse:"true"===d.get("--reveal-diffuse"),revealAnimateSpeed:d.getNumber("--reveal-animate-speed"),revealReleasedAccelerateRate:d.getNumber("--reveal-released-accelerate-rate")},{width:i,height:j,fillMode:k,fillRadius:l}=h;let m=[0,0];switch(k){case"none":break;case"relative":m=[i,j].sort((c,a)=>c-a).map(a=>a*l);break;case"absolute":m=[l,l];break;default:throw new SyntaxError("The value of `--reveal-border-style` must be `relative`, `absolute` or `none`!");}this.cachedStyle=Object.assign({},h,{trueFillRadius:m}),this.cachedFrameId=this.currentFrameId},this.cacheRevealBitmaps=()=>{if(!this.ctx)return;const{color:a,opacity:b,trueFillRadius:c}=this.cachedStyle,d=c[1],e=2*d,f=this.cachedRevealBitmap;if(d!==f.radius||a!=f.color||b!=f.opacity){this.cachedRevealBitmap={radius:d,color:a,opacity:b,bitmaps:[]},this.revealCanvas.width=e*this.pxRatio,this.revealCanvas.height=e*this.pxRatio;for(const f of[0,1]){// 0 means border, 1 means fill. | ||
const g=this.revealCanvas.getContext("2d");if(!g)return;g.setTransform(this.pxRatio,0,0,this.pxRatio,0,0);const h=0===f?b:.5*b,i=g.createRadialGradient(d,d,0,d,d,c[f]);console.log(d,c[f]),i.addColorStop(0,`rgba(${a}, ${h})`),i.addColorStop(1,`rgba(${a}, 0.0)`),g.fillStyle=i,g.clearRect(0,0,e,e),g.fillRect(0,0,e,e);const j=g.getImageData(0,0,e*this.pxRatio,e*this.pxRatio);this.cachedRevealBitmap.bitmaps.push(j)}}},this.mouseInCanvas=()=>{this.cacheCanvasPaintingStyle();const{top:a,left:b,width:c,height:d}=this.cachedStyle,e=this._store.clientX-b,f=this._store.clientY-a;return 0<e&&0<f&&e<c&&f<d},this.getAnimateGrd=(a,b)=>{if(!this.ctx)return;const{color:c,opacity:d}=this.cachedStyle,e=d*(.2-a),f=d*(.1-.07*a),g=.1+.8*a,h=0>e?0:e,i=0>f?0:f,j=1<g?1:g;b.addColorStop(0,`rgba(${c},${h})`),b.addColorStop(.55*j,`rgba(${c},${i})`),b.addColorStop(j,`rgba(${c}, 0)`)},this.paint=a=>{const b=this._store,c=b.animationQueue.has(this),d=b.clientX===b.paintedClientX&&b.clientY===b.paintedClientY;if(d&&!c&&!a)return;if(!this.ctx)return;if(this.ctx.clearRect(0,0,this.paintedWidth,this.paintedHeight),b.dirty=!1,!b.mouseInBoundary&&!c)return;if(2>this.cachedRevealBitmap.bitmaps.length)return;this.cacheCanvasPaintingStyle(),this.cacheRevealBitmaps();const{top:e,left:f,width:g,height:h,trueFillRadius:i,borderStyle:j,borderWidth:k,fillMode:l}=this.cachedStyle;this.canvas.width=g*this.pxRatio,this.canvas.height=h*this.pxRatio,this.canvas.style.width=`${g}px`,this.canvas.style.height=`${h}px`,this.paintedWidth=g,this.paintedHeight=h;const m=this.mouseInCanvas();if(!m&&!this.cachedStyle.diffuse&&!c)return;let n=0,o=0,p=0,q=0;switch(j){case"full":n=k,o=k,p=g-2*k,q=h-2*k;break;case"half":n=0,o=k,p=g,q=h-2*k;break;case"none":n=0,o=0,p=g,q=h;break;default:throw new SyntaxError("The value of `--reveal-border-style` must be `full`, `half` or `none`!");}const r=b.clientX-f,s=b.clientY-e,t=this.cachedRevealBitmap.radius,u=r-t,v=s-t;if(isNaN(r)||isNaN(s))return;if(this.ctx.setTransform(this.pxRatio,0,0,this.pxRatio,0,0),b.mouseInBoundary&&("none"!==j&&(this.ctx.putImageData(this.cachedRevealBitmap.bitmaps[0],u*this.pxRatio,v*this.pxRatio),this.ctx.clearRect(n,o,p,q)),"none"!==l&&m&&this.ctx.putImageData(this.cachedRevealBitmap.bitmaps[1],u*this.pxRatio,v*this.pxRatio,(n-u)*this.pxRatio,(o-v)*this.pxRatio,p*this.pxRatio,q*this.pxRatio)),!this.mousePressed||!this.mouseDownAnimateLogicFrame)return;let w;w=this.mouseReleased&&this.mouseUpClientX&&this.mouseUpClientY?this.ctx.createRadialGradient(this.mouseUpClientX-f,this.mouseUpClientY-e,0,this.mouseUpClientX-f,this.mouseUpClientY-e,i[1]):this.ctx.createRadialGradient(r,s,0,r,s,i[1]),this.getAnimateGrd(this.mouseDownAnimateLogicFrame,w),this.ctx.fillStyle=w,this.ctx.fillRect(n,o,1.5*p,1.5*q)},this._store=a,this.canvas=c,this.ctx=c.getContext("2d"),this.revealCanvas=document.createElement("canvas")}}class g{constructor(a){// The current cursor position relative to window. | ||
// The cursor position of painted reveal effect. | ||
this.clientX=-1e3,this.clientY=-1e3,this.paintedClientX=-1e3,this.paintedClientY=-1e3,this.mouseInBoundary=!1,this.canvasList=[],this.animationQueue=new Set,this.animationFrame=null,this.dirty=!1,this.mouseUpClientX=null,this.mouseUpClientY=null,this.mouseDownAnimateStartFrame=null,this.mouseDownAnimateCurrentFrame=null,this.mouseDownAnimateReleasedFrame=null,this.mouseDownAnimateLogicFrame=null,this.mousePressed=!1,this.mouseReleased=!1,this.addReveal=a=>{const b=new f(this,a);b.cacheCanvasPaintingStyle(),b.cacheRevealBitmaps(),this.canvasList.push(b)},this.removeReveal=a=>{this.canvasList=this.canvasList.filter(b=>b&&b.canvas===a)},this.onPointerEnterBoundary=()=>{this.mouseInBoundary=!0,this.animationFrame||(this.animationFrame=window.requestAnimationFrame(this.paintAll))},this.onPointerLeaveBoundary=()=>{this.mouseInBoundary=!1,this.paintAll(0,!0)},this.paintAll=(a,b)=>{(b||this.mouseInBoundary||0!==this.animationQueue.size)&&(this.animationQueue.forEach(b=>{if(!a||b.currentFrameId===a)return;b.currentFrameId=a,null===b.mouseDownAnimateStartFrame&&(b.mouseDownAnimateStartFrame=a);const c=a-b.mouseDownAnimateStartFrame;b.mouseDownAnimateCurrentFrame=c;const d=b.cachedStyle.revealAnimateSpeed,e=b.cachedStyle.revealReleasedAccelerateRate;let f=c;b.mouseReleased&&b.mouseDownAnimateReleasedFrame&&(f+=(c-b.mouseDownAnimateReleasedFrame)*e),b.mouseDownAnimateLogicFrame=f/d,1<b.mouseDownAnimateLogicFrame&&this.cleanUpAnimation(b)}),this.canvasList.forEach(c=>{c.currentFrameId=a,c.paint(b)}),this.dirty=!0,this.paintedClientX=this.clientX,this.paintedClientY=this.clientY,this.animationFrame=this.mouseInBoundary||0!==this.animationQueue.size?window.requestAnimationFrame(this.paintAll):null)},this.resetAll=()=>{this.canvasList.forEach(a=>{a.paint()})},this.initializeAnimation=()=>{const a=this.canvasList.find(a=>a.mouseInCanvas());a&&(this.animationQueue.add(a),a.mouseDownAnimateStartFrame=null,a.mousePressed=!0,a.mouseReleased=!1)},this.switchAnimation=()=>{this.animationQueue.forEach(a=>{a.mouseReleased||(a.mouseReleased=!0,a.mouseDownAnimateReleasedFrame=a.mouseDownAnimateCurrentFrame,a.mouseUpClientX=this.clientX,a.mouseUpClientY=this.clientY)})},this.cleanUpAnimation=a=>{a.mouseUpClientX=null,a.mouseUpClientY=null,a.mouseDownAnimateStartFrame=null,a.mouseDownAnimateCurrentFrame=null,a.mouseDownAnimateReleasedFrame=null,a.mouseDownAnimateLogicFrame=null,a.mousePressed=!1,a.mouseReleased=!1,this.animationQueue.delete(a),a.paint(!0)},this.getRevealAnimationConfig=()=>this.canvasList.find(a=>a.mouseInCanvas())||null,this.id=a}}class h{constructor(){this._currentHashId=0,this._storage=[],this.newBoundary=()=>{const a=this._currentHashId++,b=new g(a);return this._storage.push(b),b},this.removeBoundary=a=>{this._storage=this._storage.filter(b=>b===a)}}}class i extends HTMLElement{constructor(){super(...arguments),this.stateManager=new h}}i.ElementName="ax-reveal-provider";class j extends HTMLElement{constructor(){super(...arguments),this.updatePointerPosition=a=>{this.waitForStorage(b=>{b.clientX=a.clientX,b.clientY=a.clientY})},this.handlePointerEnter=()=>this.waitForStorage(a=>a.onPointerEnterBoundary()),this.handlePointerLeave=()=>this.waitForStorage(a=>a.onPointerLeaveBoundary()),this.handlePointerMove=a=>this.updatePointerPosition(a),this.handlePointerUp=()=>this.waitForStorage(a=>a.switchAnimation()),this.handlePointerDown=a=>this.waitForStorage(b=>{this.updatePointerPosition(a),b.initializeAnimation()})}get storage(){return this._storage}set storage(a){const b=this._storage;b&&(this.dispatchEvent(new CustomEvent(j.removeStorageEvent,{detail:b})),j.stateManager.removeBoundary(b)),a&&(this._storage=a,this.dispatchEvent(new CustomEvent(j.attachStorageEvent,{detail:a}))),b&&a&&this.dispatchEvent(new CustomEvent(j.replaceStorageEvent,{detail:{old:b,new:a}}))}waitForStorage(a){this.storage===void 0?this.addEventListener(j.attachStorageEvent,()=>a(this.storage),{once:!0}):a(this.storage)}appendStorage(a=!1){if(!a&&this.storage)return;const b=this.closest(i.ElementName),c=b?b.stateManager:j.stateManager;this.storage=c.newBoundary()}connectedCallback(){this.appendStorage(!0),this.addEventListener("pointerenter",this.handlePointerEnter),this.addEventListener("pointerleave",this.handlePointerLeave),this.addEventListener("pointermove",this.handlePointerMove),this.addEventListener("pointerdown",this.handlePointerDown),this.addEventListener("pointerup",this.handlePointerUp)}disconnectedCallback(){this.storage=void 0}}j.ElementName="ax-reveal-bound",j.removeStorageEvent="removeStorage",j.attachStorageEvent="attachStorage",j.replaceStorageEvent="replaceStorage",j.stateManager=new h;class k extends HTMLElement{constructor(){super(),this.root=this.attachShadow({mode:"open"}),this.root.innerHTML=` | ||
<div> | ||
@@ -22,2 +23,2 @@ <slot></slot> | ||
::slotted(button) { outline:none; } | ||
</style>`,this.canvas=this.root.querySelector("canvas")}adoptedCallback(){this.disconnectedCallback(),this.connectedCallback()}disconnectedCallback(){this.boundary&&this.boundary.waitForStorage(a=>a.removeReveal(this.canvas))}connectedCallback(){if(this.boundary=this.closest(i.ElementName),!this.boundary)throw new SyntaxError("You must use "+i.ElementName+" as the boundary of acrylic!");this.boundary.waitForStorage(a=>setTimeout(()=>a.addReveal(this.canvas),0))}}j.ElementName="ax-reveal",a.register=function(a=!1){if(window.CSS&&CSS.registerProperty)CSS.registerProperty({name:"--reveal-color",syntax:"<color>",initialValue:"rgb(0, 0, 0)",inherits:!0}),CSS.registerProperty({name:"--reveal-opacity",syntax:"<number>",initialValue:"0.26",inherits:!0}),CSS.registerProperty({name:"--reveal-border-style",syntax:"<custom-ident>",initialValue:"full",inherits:!0}),CSS.registerProperty({name:"--reveal-border-width",syntax:"<length>",initialValue:"1px",inherits:!0}),CSS.registerProperty({name:"--reveal-fill-mode",syntax:"<custom-ident>",initialValue:"relative",inherits:!0}),CSS.registerProperty({name:"--reveal-fill-radius",syntax:"<number>",initialValue:"1.5",inherits:!0}),CSS.registerProperty({name:"--reveal-diffuse",syntax:"<custom-ident>",initialValue:"true",inherits:!0}),CSS.registerProperty({name:"--reveal-animate-speed",syntax:"<number>",initialValue:"2000",inherits:!0}),CSS.registerProperty({name:"--reveal-released-accelerate-rate",syntax:"<number>",initialValue:"6",inherits:!0}),c();else if(a){const a=document.documentElement;a.dataset.revealCompat="true",a.style.setProperty("--reveal-color","rgb(0, 0, 0)"),a.style.setProperty("--reveal-opacity","0.26"),a.style.setProperty("--reveal-border-style","full"),a.style.setProperty("--reveal-border-width","1px"),a.style.setProperty("--reveal-fill-mode","relative"),a.style.setProperty("--reveal-fill-radius","1.5"),a.style.setProperty("--reveal-diffuse","true"),a.style.setProperty("--reveal-animate-speed","2000"),a.style.setProperty("--reveal-released-accelerate-rate","6"),c()}else console.warn("Your browser do NOT support `CSS.registerProperty` method, registration failed!"),console.warn("If you are the developer, try using `register(true)` to support old browsers.")},Object.defineProperty(a,"__esModule",{value:!0})}); | ||
</style>`,this.canvas=this.root.querySelector("canvas")}adoptedCallback(){this.disconnectedCallback(),this.connectedCallback()}disconnectedCallback(){this.boundary&&this.boundary.waitForStorage(a=>a.removeReveal(this.canvas))}connectedCallback(){if(this.boundary=this.closest(j.ElementName),!this.boundary)throw new SyntaxError("You must use "+j.ElementName+" as the boundary of acrylic!");this.boundary.waitForStorage(a=>setTimeout(()=>a.addReveal(this.canvas),0))}}k.ElementName="ax-reveal",a.register=function(a=!1){if(window.CSS&&CSS.registerProperty)CSS.registerProperty({name:"--reveal-color",syntax:"<color>",initialValue:"rgb(0, 0, 0)",inherits:!0}),CSS.registerProperty({name:"--reveal-opacity",syntax:"<number>",initialValue:"0.26",inherits:!0}),CSS.registerProperty({name:"--reveal-border-style",syntax:"<custom-ident>",initialValue:"full",inherits:!0}),CSS.registerProperty({name:"--reveal-border-width",syntax:"<length>",initialValue:"1px",inherits:!0}),CSS.registerProperty({name:"--reveal-fill-mode",syntax:"<custom-ident>",initialValue:"relative",inherits:!0}),CSS.registerProperty({name:"--reveal-fill-radius",syntax:"<number>",initialValue:"1.5",inherits:!0}),CSS.registerProperty({name:"--reveal-diffuse",syntax:"<custom-ident>",initialValue:"true",inherits:!0}),CSS.registerProperty({name:"--reveal-animate-speed",syntax:"<number>",initialValue:"2000",inherits:!0}),CSS.registerProperty({name:"--reveal-released-accelerate-rate",syntax:"<number>",initialValue:"6",inherits:!0}),c();else if(a){const a=document.documentElement;a.dataset.revealCompat="true",a.style.setProperty("--reveal-color","rgb(0, 0, 0)"),a.style.setProperty("--reveal-opacity","0.26"),a.style.setProperty("--reveal-border-style","full"),a.style.setProperty("--reveal-border-width","1px"),a.style.setProperty("--reveal-fill-mode","relative"),a.style.setProperty("--reveal-fill-radius","1.5"),a.style.setProperty("--reveal-diffuse","true"),a.style.setProperty("--reveal-animate-speed","2000"),a.style.setProperty("--reveal-released-accelerate-rate","6"),c()}else console.warn("Your browser do NOT support `CSS.registerProperty` method, registration failed!"),console.warn("If you are the developer, try using `register(true)` to support old browsers.")},Object.defineProperty(a,"__esModule",{value:!0})}); |
@@ -1,78 +0,8 @@ | ||
interface CachedStyle { | ||
top: number; | ||
left: number; | ||
width: number; | ||
height: number; | ||
color: string; | ||
opacity: number; | ||
trueFillRadius: number[]; | ||
cacheCanvasSize: number; | ||
borderStyle: string; | ||
borderWidth: number; | ||
fillMode: string; | ||
fillRadius: number; | ||
diffuse: boolean; | ||
revealAnimateSpeed: number; | ||
revealReleasedAccelerateRate: number; | ||
} | ||
interface CanvasConfig { | ||
canvas: HTMLCanvasElement; | ||
ctx: CanvasRenderingContext2D | null; | ||
width: number; | ||
height: number; | ||
cachedRevealBitmap: CachedRevealBitmap; | ||
mouseUpClientX: number | null; | ||
mouseUpClientY: number | null; | ||
mouseDownAnimateStartFrame: number | null; | ||
mouseDownAnimateCurrentFrame: number | null; | ||
mouseDownAnimateReleasedFrame: number | null; | ||
mouseDownAnimateLogicFrame: number | null; | ||
mousePressed: boolean; | ||
mouseReleased: boolean; | ||
cachedStyle: CachedStyle; | ||
currentFrameId: any; | ||
cachedFrameId: any; | ||
cacheCanvasPaintingStyle(): void; | ||
cacheRevealBitmaps(): void; | ||
mouseInCanvas(): boolean; | ||
getAnimateGrd(frame: number, grd: CanvasGradient): void; | ||
} | ||
export interface RevealBoundaryStore extends Partial<CanvasConfig> { | ||
_currentHashId: number; | ||
id: number; | ||
clientX: number; | ||
clientY: number; | ||
paintedClientX: number; | ||
paintedClientY: number; | ||
destroy(): void; | ||
addReveal($el: HTMLCanvasElement): void; | ||
removeReveal($el: HTMLCanvasElement): void; | ||
mouseInBoundary: boolean; | ||
canvasList: CanvasConfig[]; | ||
onPointerEnterBoundary(): void; | ||
onPointerLeaveBoundary(): void; | ||
paintAll(frame?: number, force?: boolean): void; | ||
resetAll(): void; | ||
initializeAnimation(): void; | ||
switchAnimation(): void; | ||
cleanUpAnimation(config: CanvasConfig): void; | ||
animationQueue: Set<CanvasConfig>; | ||
dirty: boolean; | ||
raf: number | null; | ||
getRevealAnimationConfig(): CanvasConfig | null; | ||
revealAnimationConfig: CanvasConfig | null; | ||
} | ||
interface CachedRevealBitmap { | ||
width: number; | ||
height: number; | ||
color: string; | ||
opacity: number; | ||
bitmaps: ImageData[]; | ||
} | ||
declare class RevealStateManager { | ||
import { RevealBoundaryStore } from './RevealBoundryStore.js'; | ||
export declare class RevealStateManager { | ||
private _currentHashId; | ||
private _storage; | ||
newBoundary: () => RevealBoundaryStore; | ||
removeBoundary: (store: RevealBoundaryStore) => void; | ||
} | ||
export default RevealStateManager; | ||
//# sourceMappingURL=RevealStateManager.d.ts.map |
@@ -1,2 +0,3 @@ | ||
import RevealStateManager, { RevealBoundaryStore } from './RevealStateManager.js'; | ||
import { RevealStateManager } from './RevealStateManager.js'; | ||
import { RevealBoundaryStore } from './RevealBoundryStore.js'; | ||
export declare class AxRevealProvider extends HTMLElement { | ||
@@ -7,8 +8,8 @@ static readonly ElementName = "ax-reveal-provider"; | ||
export declare class AxRevealBoundary extends HTMLElement { | ||
static readonly storage: RevealStateManager; | ||
static readonly ElementName = "ax-reveal-bound"; | ||
private _storage; | ||
static readonly removeStorageEvent = "removeStorage"; | ||
static readonly attachStorageEvent = "attachStorage"; | ||
static readonly replaceStorageEvent = "replaceStorage"; | ||
static readonly stateManager: RevealStateManager; | ||
private _storage; | ||
private storage; | ||
@@ -21,4 +22,4 @@ waitForStorage(f: (storage: RevealBoundaryStore) => void): void; | ||
handlePointerMove: (ev: MouseEvent) => void; | ||
handlePointerUp: () => void; | ||
handlePointerDown: (ev: MouseEvent) => void; | ||
handlePointerUp: () => void; | ||
connectedCallback(): void; | ||
@@ -25,0 +26,0 @@ disconnectedCallback(): void; |
@@ -1,2 +0,2 @@ | ||
import RevealStateManager from './RevealStateManager.js'; | ||
import { RevealStateManager } from './RevealStateManager.js'; | ||
export class AxRevealProvider extends HTMLElement { | ||
@@ -21,2 +21,3 @@ constructor() { | ||
this.handlePointerMove = (ev) => this.updatePointerPosition(ev); | ||
this.handlePointerUp = () => this.waitForStorage(storage => storage.switchAnimation()); | ||
this.handlePointerDown = (ev) => this.waitForStorage(storage => { | ||
@@ -26,3 +27,2 @@ this.updatePointerPosition(ev); | ||
}); | ||
this.handlePointerUp = () => this.waitForStorage(storage => storage.switchAnimation()); | ||
} | ||
@@ -32,27 +32,32 @@ get storage() { | ||
} | ||
set storage(newS) { | ||
const old = this._storage; | ||
if (old) | ||
this.dispatchEvent(new CustomEvent(AxRevealBoundary.removeStorageEvent, { detail: old })); | ||
if (newS) { | ||
this._storage = newS; | ||
this.dispatchEvent(new CustomEvent(AxRevealBoundary.attachStorageEvent, { detail: this._storage })); | ||
if (old) | ||
this.dispatchEvent(new CustomEvent(AxRevealBoundary.replaceStorageEvent, { detail: { old, new: newS } })); | ||
set storage(newStorage) { | ||
const oldStorage = this._storage; | ||
if (oldStorage) { | ||
this.dispatchEvent(new CustomEvent(AxRevealBoundary.removeStorageEvent, { detail: oldStorage })); | ||
AxRevealBoundary.stateManager.removeBoundary(oldStorage); | ||
} | ||
if (newStorage) { | ||
this._storage = newStorage; | ||
this.dispatchEvent(new CustomEvent(AxRevealBoundary.attachStorageEvent, { detail: newStorage })); | ||
} | ||
if (oldStorage && newStorage) { | ||
this.dispatchEvent(new CustomEvent(AxRevealBoundary.replaceStorageEvent, { | ||
detail: { old: oldStorage, new: newStorage }, | ||
})); | ||
} | ||
} | ||
waitForStorage(f) { | ||
if (this.storage === undefined) | ||
this.addEventListener(AxRevealBoundary.attachStorageEvent, () => f(this.storage), { | ||
once: true | ||
}); | ||
else | ||
if (this.storage === undefined) { | ||
this.addEventListener(AxRevealBoundary.attachStorageEvent, () => f(this.storage), { once: true }); | ||
} | ||
else { | ||
f(this.storage); | ||
} | ||
} | ||
appendStorage(force = false) { | ||
if (!force) | ||
if (this.storage) | ||
return; | ||
if (!force && this.storage) { | ||
return; | ||
} | ||
const parent = this.closest(AxRevealProvider.ElementName); | ||
const stateManager = parent ? parent.stateManager : AxRevealBoundary.storage; | ||
const stateManager = parent ? parent.stateManager : AxRevealBoundary.stateManager; | ||
this.storage = stateManager.newBoundary(); | ||
@@ -72,3 +77,2 @@ } | ||
} | ||
AxRevealBoundary.storage = new RevealStateManager(); | ||
AxRevealBoundary.ElementName = 'ax-reveal-bound'; | ||
@@ -78,2 +82,3 @@ AxRevealBoundary.removeStorageEvent = 'removeStorage'; | ||
AxRevealBoundary.replaceStorageEvent = 'replaceStorage'; | ||
AxRevealBoundary.stateManager = new RevealStateManager(); | ||
export class AxReveal extends HTMLElement { | ||
@@ -80,0 +85,0 @@ constructor() { |
@@ -1,78 +0,8 @@ | ||
interface CachedStyle { | ||
top: number; | ||
left: number; | ||
width: number; | ||
height: number; | ||
color: string; | ||
opacity: number; | ||
trueFillRadius: number[]; | ||
cacheCanvasSize: number; | ||
borderStyle: string; | ||
borderWidth: number; | ||
fillMode: string; | ||
fillRadius: number; | ||
diffuse: boolean; | ||
revealAnimateSpeed: number; | ||
revealReleasedAccelerateRate: number; | ||
} | ||
interface CanvasConfig { | ||
canvas: HTMLCanvasElement; | ||
ctx: CanvasRenderingContext2D | null; | ||
width: number; | ||
height: number; | ||
cachedRevealBitmap: CachedRevealBitmap; | ||
mouseUpClientX: number | null; | ||
mouseUpClientY: number | null; | ||
mouseDownAnimateStartFrame: number | null; | ||
mouseDownAnimateCurrentFrame: number | null; | ||
mouseDownAnimateReleasedFrame: number | null; | ||
mouseDownAnimateLogicFrame: number | null; | ||
mousePressed: boolean; | ||
mouseReleased: boolean; | ||
cachedStyle: CachedStyle; | ||
currentFrameId: any; | ||
cachedFrameId: any; | ||
cacheCanvasPaintingStyle(): void; | ||
cacheRevealBitmaps(): void; | ||
mouseInCanvas(): boolean; | ||
getAnimateGrd(frame: number, grd: CanvasGradient): void; | ||
} | ||
export interface RevealBoundaryStore extends Partial<CanvasConfig> { | ||
_currentHashId: number; | ||
id: number; | ||
clientX: number; | ||
clientY: number; | ||
paintedClientX: number; | ||
paintedClientY: number; | ||
destroy(): void; | ||
addReveal($el: HTMLCanvasElement): void; | ||
removeReveal($el: HTMLCanvasElement): void; | ||
mouseInBoundary: boolean; | ||
canvasList: CanvasConfig[]; | ||
onPointerEnterBoundary(): void; | ||
onPointerLeaveBoundary(): void; | ||
paintAll(frame?: number, force?: boolean): void; | ||
resetAll(): void; | ||
initializeAnimation(): void; | ||
switchAnimation(): void; | ||
cleanUpAnimation(config: CanvasConfig): void; | ||
animationQueue: Set<CanvasConfig>; | ||
dirty: boolean; | ||
raf: number | null; | ||
getRevealAnimationConfig(): CanvasConfig | null; | ||
revealAnimationConfig: CanvasConfig | null; | ||
} | ||
interface CachedRevealBitmap { | ||
width: number; | ||
height: number; | ||
color: string; | ||
opacity: number; | ||
bitmaps: ImageData[]; | ||
} | ||
declare class RevealStateManager { | ||
import { RevealBoundaryStore } from './RevealBoundryStore.js'; | ||
export declare class RevealStateManager { | ||
private _currentHashId; | ||
private _storage; | ||
newBoundary: () => RevealBoundaryStore; | ||
removeBoundary: (store: RevealBoundaryStore) => void; | ||
} | ||
export default RevealStateManager; | ||
//# sourceMappingURL=RevealStateManager.d.ts.map |
@@ -1,13 +0,3 @@ | ||
// interface RevealStyle { | ||
// color: string; | ||
// borderStyle: 'full' | 'half' | 'none'; | ||
// borderWidth: number; | ||
// fillMode: 'relative' | 'absolute' | 'none'; | ||
// fillRadius: number; | ||
// diffuse: boolean; | ||
// revealAnimateSpeed: number; | ||
// revealReleasedAccelerateRate: number; | ||
// } | ||
import { createStorage } from './ComputedStyleStorage.js'; | ||
class RevealStateManager { | ||
import { RevealBoundaryStore } from './RevealBoundryStore.js'; | ||
export class RevealStateManager { | ||
constructor() { | ||
@@ -18,361 +8,11 @@ this._currentHashId = 0; | ||
const hashId = this._currentHashId++; | ||
const storage = { | ||
_currentHashId: 0, | ||
id: hashId, | ||
clientX: -1000, | ||
clientY: -1000, | ||
paintedClientX: -1000, | ||
paintedClientY: -1000, | ||
mouseInBoundary: false, | ||
canvasList: [], | ||
dirty: false, | ||
raf: null, | ||
destroy: () => { | ||
this._storage.find((sto, idx) => { | ||
const answer = sto === storage; | ||
this._storage.splice(idx, 1); | ||
return answer; | ||
}); | ||
}, | ||
addReveal: ($el) => { | ||
const canvasConfig = { | ||
canvas: $el, | ||
ctx: $el.getContext('2d'), | ||
cachedRevealBitmap: { | ||
width: 0, | ||
height: 0, | ||
color: '', | ||
opacity: 0, | ||
bitmaps: [], | ||
}, | ||
width: 0, | ||
height: 0, | ||
cachedStyle: { | ||
top: -1, | ||
left: -1, | ||
width: -1, | ||
height: -1, | ||
trueFillRadius: [0, 0], | ||
cacheCanvasSize: -1, | ||
color: '', | ||
opacity: 1, | ||
borderStyle: '', | ||
borderWidth: 1, | ||
fillMode: '', | ||
fillRadius: 0, | ||
diffuse: true, | ||
revealAnimateSpeed: 0, | ||
revealReleasedAccelerateRate: 0, | ||
}, | ||
currentFrameId: -1, | ||
cachedFrameId: -2, | ||
cacheCanvasPaintingStyle: () => { | ||
if (canvasConfig.currentFrameId === canvasConfig.cachedFrameId) { | ||
return canvasConfig.cachedStyle; | ||
} | ||
const compat = !!document.documentElement.dataset.revealCompat; | ||
const computedStyle = createStorage($el, compat); | ||
if (computedStyle.size() === 0) { | ||
return canvasConfig.cachedStyle; | ||
} | ||
const boundingRect = $el.getBoundingClientRect(); | ||
const colorString = computedStyle.getColor('--reveal-color'); | ||
const colorStringMatch = colorString.match(/\((\d+,\s*\d+,\s*\d+)[\s\S]*?\)/); | ||
const cachedStyle = { | ||
top: Math.round(boundingRect.top), | ||
left: Math.round(boundingRect.left), | ||
width: Math.round(boundingRect.width), | ||
height: Math.round(boundingRect.height), | ||
color: colorStringMatch && colorStringMatch.length > 1 ? colorStringMatch[1] : '0, 0, 0', | ||
opacity: computedStyle.getNumber('--reveal-opacity'), | ||
borderStyle: computedStyle.get('--reveal-border-style'), | ||
borderWidth: computedStyle.getNumber('--reveal-border-width'), | ||
fillMode: computedStyle.get('--reveal-fill-mode'), | ||
fillRadius: computedStyle.getNumber('--reveal-fill-radius'), | ||
diffuse: computedStyle.get('--reveal-diffuse') === 'true', | ||
revealAnimateSpeed: computedStyle.getNumber('--reveal-animate-speed'), | ||
revealReleasedAccelerateRate: computedStyle.getNumber('--reveal-released-accelerate-rate'), | ||
}; | ||
const { width, height, fillMode, fillRadius } = cachedStyle; | ||
let trueFillRadius = [0, 0]; | ||
switch (fillMode) { | ||
case 'none': | ||
break; | ||
case 'relative': | ||
trueFillRadius = [width, height].sort((a, b) => a - b).map(x => x * fillRadius); | ||
break; | ||
case 'absolute': | ||
trueFillRadius = [fillRadius, fillRadius]; | ||
break; | ||
default: | ||
throw new SyntaxError('The value of `--reveal-border-style` must be `relative`, `absolute` or `none`!'); | ||
} | ||
const cacheCanvasSize = trueFillRadius[1] * 2; | ||
canvasConfig.cachedStyle = Object.assign({}, cachedStyle, { trueFillRadius, | ||
cacheCanvasSize }); | ||
canvasConfig.cachedFrameId = canvasConfig.currentFrameId; | ||
}, | ||
cacheRevealBitmaps: () => { | ||
if (!canvasConfig.ctx) | ||
return; | ||
const { width, height, color, opacity, trueFillRadius, cacheCanvasSize, } = canvasConfig.cachedStyle; | ||
const last = canvasConfig.cachedRevealBitmap; | ||
if (width === last.width && height == last.height && color == last.color && opacity == last.opacity) { | ||
return; | ||
} | ||
canvasConfig.width = width; | ||
canvasConfig.height = height; | ||
canvasConfig.canvas.width = width; | ||
canvasConfig.canvas.height = height; | ||
canvasConfig.cachedRevealBitmap = { | ||
width, height, color, opacity, | ||
bitmaps: [], | ||
}; | ||
for (const i of [0, 1]) { | ||
// 0 means border, 1 means fill. | ||
const revealCanvas = document.createElement('canvas'); | ||
revealCanvas.width = cacheCanvasSize; | ||
revealCanvas.height = cacheCanvasSize; | ||
const revealCtx = revealCanvas.getContext('2d'); | ||
if (!revealCtx) | ||
return; | ||
const fillAlpha = i === 0 ? opacity : (opacity * 0.5); | ||
const grd = revealCtx.createRadialGradient(cacheCanvasSize / 2, cacheCanvasSize / 2, 0, cacheCanvasSize / 2, cacheCanvasSize / 2, trueFillRadius[i]); | ||
grd.addColorStop(0, 'rgba(' + color + ', ' + fillAlpha + ')'); | ||
grd.addColorStop(1, 'rgba(' + color + ', 0.0)'); | ||
revealCtx.fillStyle = grd; | ||
revealCtx.fillRect(0, 0, cacheCanvasSize, cacheCanvasSize); | ||
const bitmap = revealCtx.getImageData(0, 0, cacheCanvasSize, cacheCanvasSize); | ||
canvasConfig.cachedRevealBitmap.bitmaps.push(bitmap); | ||
} | ||
}, | ||
mouseInCanvas: () => { | ||
canvasConfig.cacheCanvasPaintingStyle(); | ||
const { top, left, width, height } = canvasConfig.cachedStyle; | ||
const relativeX = storage.clientX - left; | ||
const relativeY = storage.clientY - top; | ||
if (relativeX < 0 || relativeX > width) | ||
return false; | ||
if (relativeY < 0 || relativeY > height) | ||
return false; | ||
return true; | ||
}, | ||
getAnimateGrd: (frame, grd) => { | ||
if (!canvasConfig.ctx) | ||
return null; | ||
const { color, opacity } = canvasConfig.cachedStyle; | ||
const _innerAlpha = opacity * (0.2 - frame); | ||
const _outerAlpha = opacity * (0.1 - frame * 0.07); | ||
const _outerBorder = 0.1 + frame * 0.8; | ||
const innerAlpha = _innerAlpha < 0 ? 0 : _innerAlpha; | ||
const outerAlpha = _outerAlpha < 0 ? 0 : _outerAlpha; | ||
const outerBorder = _outerBorder > 1 ? 1 : _outerBorder; | ||
grd.addColorStop(0, `rgba(${color},${innerAlpha})`); | ||
grd.addColorStop(outerBorder * 0.55, `rgba(${color},${outerAlpha})`); | ||
grd.addColorStop(outerBorder, `rgba(${color}, 0)`); | ||
return grd; | ||
}, | ||
mouseUpClientX: null, | ||
mouseUpClientY: null, | ||
mouseDownAnimateStartFrame: null, | ||
mouseDownAnimateCurrentFrame: null, | ||
mouseDownAnimateReleasedFrame: null, | ||
mouseDownAnimateLogicFrame: null, | ||
mousePressed: false, | ||
mouseReleased: false, | ||
}; | ||
canvasConfig.cacheCanvasPaintingStyle(); | ||
canvasConfig.cacheRevealBitmaps(); | ||
storage.canvasList.push(canvasConfig); | ||
}, | ||
removeReveal: ($el) => { | ||
storage.canvasList.find((config, idx) => { | ||
if (config && $el === config.canvas) { | ||
storage.canvasList.splice(idx, 1); | ||
return true; | ||
} | ||
return false; | ||
}); | ||
}, | ||
onPointerEnterBoundary: () => { | ||
storage.mouseInBoundary = true; | ||
if (!storage.raf) | ||
storage.raf = window.requestAnimationFrame((frame) => storage.paintAll(frame)); | ||
}, | ||
onPointerLeaveBoundary: () => { | ||
storage.mouseInBoundary = false; | ||
storage.paintAll(0, true); | ||
}, | ||
paintAll: (frame, force) => { | ||
if (!force) { | ||
if (!storage.mouseInBoundary && storage.animationQueue.size === 0) { | ||
return; | ||
} | ||
} | ||
storage.animationQueue.forEach(config => { | ||
if (!frame) | ||
return; | ||
if (config.currentFrameId === frame) | ||
return; | ||
config.currentFrameId = frame; | ||
if (config.mouseDownAnimateStartFrame === null) | ||
config.mouseDownAnimateStartFrame = frame; | ||
const relativeFrame = frame - config.mouseDownAnimateStartFrame; | ||
config.mouseDownAnimateCurrentFrame = relativeFrame; | ||
const speed = config.cachedStyle.revealAnimateSpeed; | ||
const accelerateRate = config.cachedStyle.revealReleasedAccelerateRate; | ||
config.mouseDownAnimateLogicFrame = | ||
!config.mouseReleased || !config.mouseDownAnimateReleasedFrame | ||
? relativeFrame / speed | ||
: relativeFrame / speed + | ||
((relativeFrame - config.mouseDownAnimateReleasedFrame) / speed) * accelerateRate; | ||
if (config.mouseDownAnimateLogicFrame > 1) | ||
storage.cleanUpAnimation(config); | ||
}); | ||
storage.canvasList.forEach((config, index) => { | ||
config.currentFrameId = frame; | ||
paintCanvas(config, storage, force); | ||
}); | ||
storage.dirty = true; | ||
storage.paintedClientX = storage.clientX; | ||
storage.paintedClientY = storage.clientY; | ||
if (storage.mouseInBoundary || storage.animationQueue.size !== 0) { | ||
window.requestAnimationFrame(frame => { | ||
storage.paintAll(frame); | ||
}); | ||
} | ||
else { | ||
storage.raf = null; | ||
} | ||
}, | ||
resetAll: () => { | ||
storage.canvasList.forEach(config => { | ||
paintCanvas(config, storage); | ||
}); | ||
}, | ||
initializeAnimation: () => { | ||
const config = storage.canvasList.find(x => x.mouseInCanvas()); | ||
if (!config) | ||
return; | ||
storage.animationQueue.add(config); | ||
config.mouseDownAnimateStartFrame = null; | ||
config.mousePressed = true; | ||
config.mouseReleased = false; | ||
}, | ||
switchAnimation: () => { | ||
storage.animationQueue.forEach(config => { | ||
if (!config.mouseReleased) { | ||
config.mouseReleased = true; | ||
config.mouseDownAnimateReleasedFrame = config.mouseDownAnimateCurrentFrame; | ||
config.mouseUpClientX = storage.clientX; | ||
config.mouseUpClientY = storage.clientY; | ||
} | ||
}); | ||
}, | ||
cleanUpAnimation: (config) => { | ||
config.mouseUpClientX = null; | ||
config.mouseUpClientY = null; | ||
config.mouseDownAnimateStartFrame = null; | ||
config.mouseDownAnimateCurrentFrame = null; | ||
config.mouseDownAnimateReleasedFrame = null; | ||
config.mouseDownAnimateLogicFrame = null; | ||
config.mousePressed = false; | ||
config.mouseReleased = false; | ||
storage.animationQueue.delete(config); | ||
paintCanvas(config, storage, true); | ||
}, | ||
animationQueue: new Set(), | ||
getRevealAnimationConfig: () => { | ||
return storage.canvasList.find(x => x.mouseInCanvas()) || null; | ||
}, | ||
revealAnimationConfig: null, | ||
mouseUpClientX: null, | ||
mouseUpClientY: null, | ||
mouseDownAnimateStartFrame: null, | ||
mouseDownAnimateCurrentFrame: null, | ||
mouseDownAnimateReleasedFrame: null, | ||
mouseDownAnimateLogicFrame: null, | ||
mousePressed: false, | ||
mouseReleased: false, | ||
}; | ||
this._storage.push(storage); | ||
return storage; | ||
const store = new RevealBoundaryStore(hashId); | ||
this._storage.push(store); | ||
return store; | ||
}; | ||
this.removeBoundary = (store) => { | ||
this._storage = this._storage.filter(x => x === store); | ||
}; | ||
} | ||
} | ||
const paintCanvas = (config, storage, force, debug) => { | ||
const animationPlaying = storage.animationQueue.has(config); | ||
if (storage.clientX === storage.paintedClientX && | ||
storage.clientY === storage.paintedClientY && | ||
!animationPlaying && | ||
!force) | ||
return; | ||
if (!config.ctx) | ||
return; | ||
config.ctx.clearRect(0, 0, config.width, config.height); | ||
storage.dirty = false; | ||
if (!storage.mouseInBoundary && !animationPlaying) | ||
return; | ||
if (config.cachedRevealBitmap.bitmaps.length < 2) | ||
return; | ||
config.cacheCanvasPaintingStyle(); | ||
config.cacheRevealBitmaps(); | ||
const { top, left, width, height, cacheCanvasSize, trueFillRadius } = config.cachedStyle; | ||
const borderStyle = config.cachedStyle.borderStyle; | ||
const borderWidth = config.cachedStyle.borderWidth; | ||
const fillMode = config.cachedStyle.fillMode; | ||
const relativeX = storage.clientX - left; | ||
const relativeY = storage.clientY - top; | ||
const mouseInCanvas = relativeX > 0 && relativeX < width && (relativeY > 0 && relativeY < height); | ||
if (!mouseInCanvas && !config.cachedStyle.diffuse && !animationPlaying) | ||
return; | ||
let fillX = 0, fillY = 0, fillW = 0, fillH = 0; | ||
switch (borderStyle) { | ||
case 'full': | ||
fillX = borderWidth; | ||
fillY = borderWidth; | ||
fillW = width - 2 * borderWidth; | ||
fillH = height - 2 * borderWidth; | ||
break; | ||
case 'half': | ||
fillX = 0; | ||
fillY = borderWidth; | ||
fillW = width; | ||
fillH = height - 2 * borderWidth; | ||
break; | ||
case 'none': | ||
fillX = 0; | ||
fillY = 0; | ||
fillW = width; | ||
fillH = height; | ||
break; | ||
default: | ||
throw new SyntaxError('The value of `--reveal-border-style` must be `full`, `half` or `none`!'); | ||
} | ||
const putX = relativeX - cacheCanvasSize / 2; | ||
const putY = relativeY - cacheCanvasSize / 2; | ||
if (isNaN(relativeX) || isNaN(relativeY)) | ||
return; | ||
if (storage.mouseInBoundary) { | ||
if (borderStyle !== 'none') { | ||
config.ctx.putImageData(config.cachedRevealBitmap.bitmaps[0], putX, putY, -putX, -putY, width, height); | ||
config.ctx.clearRect(fillX, fillY, fillW, fillH); | ||
} | ||
if (fillMode != 'none' && mouseInCanvas) | ||
config.ctx.putImageData(config.cachedRevealBitmap.bitmaps[1], putX, putY, fillX - putX, fillY - putY, fillW, fillH); | ||
} | ||
if (!config.mousePressed || !config.mouseDownAnimateLogicFrame) | ||
return; | ||
let animateGrd; | ||
if (config.mouseReleased && config.mouseUpClientX && config.mouseUpClientY) { | ||
animateGrd = config.ctx.createRadialGradient(config.mouseUpClientX - left, config.mouseUpClientY - top, 0, config.mouseUpClientX - left, config.mouseUpClientY - top, trueFillRadius[1]); | ||
} | ||
else { | ||
animateGrd = config.ctx.createRadialGradient(relativeX, relativeY, 0, relativeX, relativeY, trueFillRadius[1]); | ||
} | ||
config.getAnimateGrd(config.mouseDownAnimateLogicFrame, animateGrd); | ||
config.ctx.fillStyle = animateGrd; | ||
config.ctx.fillRect(fillX, fillY, fillW * 1.5, fillH * 1.5); | ||
}; | ||
export default RevealStateManager; | ||
//# sourceMappingURL=RevealStateManager.js.map |
{ | ||
"name": "@ax-design/reveal-highlight", | ||
"version": "0.1.10-beta.0", | ||
"version": "0.1.10", | ||
"description": "Web component that implement Reveal Highlight of Axiom Design System.", | ||
@@ -5,0 +5,0 @@ "main": "build/main.js", |
@@ -1,2 +0,3 @@ | ||
import RevealStateManager, { RevealBoundaryStore } from './RevealStateManager.js'; | ||
import { RevealStateManager } from './RevealStateManager.js'; | ||
import { RevealBoundaryStore } from './RevealBoundryStore.js'; | ||
@@ -9,36 +10,55 @@ export class AxRevealProvider extends HTMLElement { | ||
export class AxRevealBoundary extends HTMLElement { | ||
static readonly storage = new RevealStateManager(); | ||
static readonly ElementName = 'ax-reveal-bound'; | ||
private _storage!: RevealBoundaryStore | undefined; | ||
static readonly removeStorageEvent = 'removeStorage'; | ||
static readonly attachStorageEvent = 'attachStorage'; | ||
static readonly replaceStorageEvent = 'replaceStorage'; | ||
static readonly stateManager = new RevealStateManager(); | ||
private _storage!: RevealBoundaryStore | undefined; | ||
private get storage() { | ||
return this._storage; | ||
} | ||
private set storage(newS) { | ||
const old = this._storage; | ||
if (old) this.dispatchEvent(new CustomEvent(AxRevealBoundary.removeStorageEvent, { detail: old })); | ||
if (newS) { | ||
this._storage = newS; | ||
this.dispatchEvent(new CustomEvent(AxRevealBoundary.attachStorageEvent, { detail: this._storage })); | ||
if (old) | ||
this.dispatchEvent( | ||
new CustomEvent(AxRevealBoundary.replaceStorageEvent, { detail: { old, new: newS } }) | ||
); | ||
private set storage(newStorage) { | ||
const oldStorage = this._storage; | ||
if (oldStorage) { | ||
this.dispatchEvent(new CustomEvent(AxRevealBoundary.removeStorageEvent, { detail: oldStorage })); | ||
AxRevealBoundary.stateManager.removeBoundary(oldStorage); | ||
} | ||
if (newStorage) { | ||
this._storage = newStorage; | ||
this.dispatchEvent(new CustomEvent(AxRevealBoundary.attachStorageEvent, { detail: newStorage })); | ||
} | ||
if (oldStorage && newStorage) { | ||
this.dispatchEvent(new CustomEvent(AxRevealBoundary.replaceStorageEvent, { | ||
detail: { old: oldStorage, new: newStorage }, | ||
})); | ||
} | ||
} | ||
public waitForStorage(f: (storage: RevealBoundaryStore) => void) { | ||
if (this.storage === undefined) | ||
this.addEventListener(AxRevealBoundary.attachStorageEvent, () => f(this.storage!), { | ||
once: true | ||
}); | ||
else f(this.storage); | ||
if (this.storage === undefined) { | ||
this.addEventListener(AxRevealBoundary.attachStorageEvent, () => f(this.storage!), { once: true }); | ||
} else { | ||
f(this.storage); | ||
} | ||
} | ||
private appendStorage(force = false) { | ||
if (!force) if (this.storage) return; | ||
if (!force && this.storage) { | ||
return; | ||
} | ||
const parent = this.closest(AxRevealProvider.ElementName) as AxRevealProvider; | ||
const stateManager = parent ? parent.stateManager : AxRevealBoundary.storage; | ||
const stateManager = parent ? parent.stateManager : AxRevealBoundary.stateManager; | ||
this.storage = stateManager.newBoundary(); | ||
} | ||
updatePointerPosition = (ev: MouseEvent) => { | ||
@@ -49,6 +69,8 @@ this.waitForStorage(storage => { | ||
}); | ||
} | ||
}; | ||
handlePointerEnter = () => this.waitForStorage(storage => storage.onPointerEnterBoundary()); | ||
handlePointerLeave = () => this.waitForStorage(storage => storage.onPointerLeaveBoundary()); | ||
handlePointerMove = (ev: MouseEvent) => this.updatePointerPosition(ev); | ||
handlePointerUp = () => this.waitForStorage(storage => storage.switchAnimation()); | ||
handlePointerDown = (ev: MouseEvent) => this.waitForStorage(storage => { | ||
@@ -58,3 +80,3 @@ this.updatePointerPosition(ev); | ||
}); | ||
handlePointerUp = () => this.waitForStorage(storage => storage.switchAnimation()); | ||
connectedCallback() { | ||
@@ -68,2 +90,3 @@ this.appendStorage(true); | ||
} | ||
disconnectedCallback() { | ||
@@ -79,2 +102,3 @@ this.storage = undefined; | ||
private boundary!: AxRevealBoundary; | ||
adoptedCallback() { | ||
@@ -84,5 +108,7 @@ this.disconnectedCallback(); | ||
} | ||
disconnectedCallback() { | ||
this.boundary && this.boundary.waitForStorage(storage => storage.removeReveal(this.canvas)); | ||
} | ||
connectedCallback() { | ||
@@ -95,2 +121,3 @@ this.boundary = this.closest(AxRevealBoundary.ElementName) as AxRevealBoundary; | ||
} | ||
constructor() { | ||
@@ -97,0 +124,0 @@ super(); |
@@ -1,560 +0,20 @@ | ||
// interface RevealStyle { | ||
// color: string; | ||
// borderStyle: 'full' | 'half' | 'none'; | ||
// borderWidth: number; | ||
// fillMode: 'relative' | 'absolute' | 'none'; | ||
// fillRadius: number; | ||
// diffuse: boolean; | ||
// revealAnimateSpeed: number; | ||
// revealReleasedAccelerateRate: number; | ||
// } | ||
import { RevealBoundaryStore } from './RevealBoundryStore.js'; | ||
import { createStorage } from './ComputedStyleStorage.js'; | ||
export class RevealStateManager { | ||
private _currentHashId = 0; | ||
interface CachedStyle { | ||
top: number; | ||
left: number; | ||
width: number; | ||
height: number; | ||
color: string; | ||
opacity: number; | ||
trueFillRadius: number[]; | ||
cacheCanvasSize: number; | ||
borderStyle: string; | ||
borderWidth: number; | ||
fillMode: string; | ||
fillRadius: number; | ||
diffuse: boolean; | ||
revealAnimateSpeed: number; | ||
revealReleasedAccelerateRate: number; | ||
} | ||
private _storage: RevealBoundaryStore[] = []; | ||
interface CanvasConfig { | ||
canvas: HTMLCanvasElement; | ||
ctx: CanvasRenderingContext2D | null; | ||
width: number; | ||
height: number; | ||
cachedRevealBitmap: CachedRevealBitmap; | ||
mouseUpClientX: number | null; | ||
mouseUpClientY: number | null; | ||
mouseDownAnimateStartFrame: number | null; | ||
mouseDownAnimateCurrentFrame: number | null; | ||
mouseDownAnimateReleasedFrame: number | null; | ||
mouseDownAnimateLogicFrame: number | null; | ||
mousePressed: boolean; | ||
mouseReleased: boolean; | ||
cachedStyle: CachedStyle; | ||
currentFrameId: any; | ||
cachedFrameId: any; | ||
cacheCanvasPaintingStyle(): void; | ||
cacheRevealBitmaps(): void; | ||
mouseInCanvas(): boolean; | ||
getAnimateGrd(frame: number, grd: CanvasGradient): void; | ||
} | ||
export interface RevealBoundaryStore extends Partial<CanvasConfig> { | ||
_currentHashId: number; | ||
id: number; | ||
// The current cursor position relative to window. | ||
clientX: number; | ||
clientY: number; | ||
// The cursor position of painted reveal effect. | ||
paintedClientX: number; | ||
paintedClientY: number; | ||
destroy(): void; | ||
// Add a new reveal effect. | ||
addReveal($el: HTMLCanvasElement): void; | ||
removeReveal($el: HTMLCanvasElement): void; | ||
mouseInBoundary: boolean; | ||
canvasList: CanvasConfig[]; | ||
onPointerEnterBoundary(): void; | ||
onPointerLeaveBoundary(): void; | ||
paintAll(frame?: number, force?: boolean): void; | ||
resetAll(): void; | ||
initializeAnimation(): void; | ||
switchAnimation(): void; | ||
cleanUpAnimation(config: CanvasConfig): void; | ||
animationQueue: Set<CanvasConfig>; | ||
dirty: boolean; | ||
raf: number | null; | ||
getRevealAnimationConfig(): CanvasConfig | null; | ||
revealAnimationConfig: CanvasConfig | null; | ||
} | ||
interface CachedRevealBitmap { | ||
width: number; | ||
height: number; | ||
color: string; | ||
opacity: number; | ||
bitmaps: ImageData[]; | ||
} | ||
class RevealStateManager { | ||
private _currentHashId = 0; | ||
private _storage: RevealBoundaryStore[] = []; | ||
newBoundary = () => { | ||
const hashId = this._currentHashId++; | ||
const storage: RevealBoundaryStore = { | ||
_currentHashId: 0, | ||
id: hashId, | ||
clientX: -1000, | ||
clientY: -1000, | ||
paintedClientX: -1000, | ||
paintedClientY: -1000, | ||
mouseInBoundary: false, | ||
canvasList: [], | ||
dirty: false, | ||
raf: null, | ||
destroy: () => { | ||
this._storage.find((sto, idx) => { | ||
const answer = sto === storage; | ||
this._storage.splice(idx, 1); | ||
return answer; | ||
}); | ||
}, | ||
addReveal: ($el: HTMLCanvasElement) => { | ||
const canvasConfig: CanvasConfig = { | ||
canvas: $el, | ||
ctx: $el.getContext('2d'), | ||
cachedRevealBitmap: { | ||
width: 0, | ||
height: 0, | ||
color: '', | ||
opacity: 0, | ||
bitmaps: [], | ||
}, | ||
width: 0, | ||
height: 0, | ||
cachedStyle: { | ||
top: -1, | ||
left: -1, | ||
width: -1, | ||
height: -1, | ||
trueFillRadius: [0, 0], | ||
cacheCanvasSize: -1, | ||
color: '', | ||
opacity: 1, | ||
borderStyle: '', | ||
borderWidth: 1, | ||
fillMode: '', | ||
fillRadius: 0, | ||
diffuse: true, | ||
revealAnimateSpeed: 0, | ||
revealReleasedAccelerateRate: 0, | ||
}, | ||
currentFrameId: -1, | ||
cachedFrameId: -2, | ||
const store = new RevealBoundaryStore(hashId); | ||
this._storage.push(store); | ||
cacheCanvasPaintingStyle: () => { | ||
if (canvasConfig.currentFrameId === canvasConfig.cachedFrameId) { | ||
return canvasConfig.cachedStyle; | ||
} | ||
return store; | ||
}; | ||
const compat = !!document.documentElement.dataset.revealCompat; | ||
const computedStyle = createStorage($el, compat); | ||
if (computedStyle.size() === 0) { | ||
return canvasConfig.cachedStyle; | ||
} | ||
const boundingRect = $el.getBoundingClientRect(); | ||
const colorString = computedStyle.getColor('--reveal-color'); | ||
const colorStringMatch = colorString.match(/\((\d+,\s*\d+,\s*\d+)[\s\S]*?\)/); | ||
const cachedStyle = { | ||
top: Math.round(boundingRect.top), | ||
left: Math.round(boundingRect.left), | ||
width: Math.round(boundingRect.width), | ||
height: Math.round(boundingRect.height), | ||
color: colorStringMatch && colorStringMatch.length > 1 ? colorStringMatch[1] : '0, 0, 0', | ||
opacity: computedStyle.getNumber('--reveal-opacity'), | ||
borderStyle: computedStyle.get('--reveal-border-style'), | ||
borderWidth: computedStyle.getNumber('--reveal-border-width'), | ||
fillMode: computedStyle.get('--reveal-fill-mode'), | ||
fillRadius: computedStyle.getNumber('--reveal-fill-radius'), | ||
diffuse: computedStyle.get('--reveal-diffuse') === 'true', | ||
revealAnimateSpeed: computedStyle.getNumber('--reveal-animate-speed'), | ||
revealReleasedAccelerateRate: computedStyle.getNumber('--reveal-released-accelerate-rate'), | ||
}; | ||
const { width, height, fillMode, fillRadius } = cachedStyle; | ||
let trueFillRadius = [0, 0]; | ||
switch (fillMode) { | ||
case 'none': | ||
break; | ||
case 'relative': | ||
trueFillRadius = [width, height].sort((a, b) => a - b).map(x => x * fillRadius); | ||
break; | ||
case 'absolute': | ||
trueFillRadius = [fillRadius, fillRadius]; | ||
break; | ||
default: | ||
throw new SyntaxError('The value of `--reveal-border-style` must be `relative`, `absolute` or `none`!'); | ||
} | ||
const cacheCanvasSize = trueFillRadius[1] * 2; | ||
canvasConfig.cachedStyle = { | ||
...cachedStyle, | ||
trueFillRadius, | ||
cacheCanvasSize, | ||
}; | ||
canvasConfig.cachedFrameId = canvasConfig.currentFrameId; | ||
}, | ||
cacheRevealBitmaps: () => { | ||
if (!canvasConfig.ctx) return; | ||
const { | ||
width, | ||
height, | ||
color, | ||
opacity, | ||
trueFillRadius, | ||
cacheCanvasSize, | ||
} = canvasConfig.cachedStyle; | ||
const last = canvasConfig.cachedRevealBitmap; | ||
if (width === last.width && height == last.height && color == last.color && opacity == last.opacity) { | ||
return; | ||
} | ||
canvasConfig.width = width; | ||
canvasConfig.height = height; | ||
canvasConfig.canvas.width = width; | ||
canvasConfig.canvas.height = height; | ||
canvasConfig.cachedRevealBitmap = { | ||
width, height, color, opacity, | ||
bitmaps: [], | ||
}; | ||
for (const i of [0, 1]) { | ||
// 0 means border, 1 means fill. | ||
const revealCanvas = document.createElement('canvas'); | ||
revealCanvas.width = cacheCanvasSize; | ||
revealCanvas.height = cacheCanvasSize; | ||
const revealCtx = revealCanvas.getContext('2d'); | ||
if (!revealCtx) return; | ||
const fillAlpha = i === 0 ? opacity : (opacity * 0.5); | ||
const grd = revealCtx.createRadialGradient( | ||
cacheCanvasSize / 2, | ||
cacheCanvasSize / 2, | ||
0, | ||
cacheCanvasSize / 2, | ||
cacheCanvasSize / 2, | ||
trueFillRadius[i], | ||
); | ||
grd.addColorStop(0, 'rgba(' + color + ', ' + fillAlpha + ')'); | ||
grd.addColorStop(1, 'rgba(' + color + ', 0.0)'); | ||
revealCtx.fillStyle = grd; | ||
revealCtx.fillRect(0, 0, cacheCanvasSize, cacheCanvasSize); | ||
const bitmap = revealCtx.getImageData(0, 0, cacheCanvasSize, cacheCanvasSize); | ||
canvasConfig.cachedRevealBitmap.bitmaps.push(bitmap); | ||
} | ||
}, | ||
mouseInCanvas: () => { | ||
canvasConfig.cacheCanvasPaintingStyle(); | ||
const { top, left, width, height } = canvasConfig.cachedStyle; | ||
const relativeX = storage.clientX - left; | ||
const relativeY = storage.clientY - top; | ||
if (relativeX < 0 || relativeX > width) return false; | ||
if (relativeY < 0 || relativeY > height) return false; | ||
return true; | ||
}, | ||
getAnimateGrd: (frame: number, grd: CanvasGradient) => { | ||
if (!canvasConfig.ctx) return null; | ||
const { color, opacity } = canvasConfig.cachedStyle; | ||
const _innerAlpha = opacity * (0.2 - frame); | ||
const _outerAlpha = opacity * (0.1 - frame * 0.07); | ||
const _outerBorder = 0.1 + frame * 0.8; | ||
const innerAlpha = _innerAlpha < 0 ? 0 : _innerAlpha; | ||
const outerAlpha = _outerAlpha < 0 ? 0 : _outerAlpha; | ||
const outerBorder = _outerBorder > 1 ? 1 : _outerBorder; | ||
grd.addColorStop(0, `rgba(${color},${innerAlpha})`); | ||
grd.addColorStop(outerBorder * 0.55, `rgba(${color},${outerAlpha})`); | ||
grd.addColorStop(outerBorder, `rgba(${color}, 0)`); | ||
return grd; | ||
}, | ||
mouseUpClientX: null, | ||
mouseUpClientY: null, | ||
mouseDownAnimateStartFrame: null, | ||
mouseDownAnimateCurrentFrame: null, | ||
mouseDownAnimateReleasedFrame: null, | ||
mouseDownAnimateLogicFrame: null, | ||
mousePressed: false, | ||
mouseReleased: false, | ||
}; | ||
canvasConfig.cacheCanvasPaintingStyle(); | ||
canvasConfig.cacheRevealBitmaps(); | ||
storage.canvasList.push(canvasConfig); | ||
}, | ||
removeReveal: ($el: HTMLCanvasElement) => { | ||
storage.canvasList.find((config, idx) => { | ||
if (config && $el === config.canvas) { | ||
storage.canvasList.splice(idx, 1); | ||
return true; | ||
} | ||
return false; | ||
}); | ||
}, | ||
onPointerEnterBoundary: () => { | ||
storage.mouseInBoundary = true; | ||
if (!storage.raf) storage.raf = window.requestAnimationFrame((frame) => storage.paintAll(frame)); | ||
}, | ||
onPointerLeaveBoundary: () => { | ||
storage.mouseInBoundary = false; | ||
storage.paintAll(0, true); | ||
}, | ||
paintAll: (frame?: number, force?: boolean) => { | ||
if (!force) { | ||
if (!storage.mouseInBoundary && storage.animationQueue.size === 0) { | ||
return; | ||
} | ||
} | ||
storage.animationQueue.forEach(config => { | ||
if (!frame) return; | ||
if (config.currentFrameId === frame) return; | ||
config.currentFrameId = frame; | ||
if (config.mouseDownAnimateStartFrame === null) config.mouseDownAnimateStartFrame = frame; | ||
const relativeFrame = frame - config.mouseDownAnimateStartFrame; | ||
config.mouseDownAnimateCurrentFrame = relativeFrame; | ||
const speed = config.cachedStyle.revealAnimateSpeed; | ||
const accelerateRate = config.cachedStyle.revealReleasedAccelerateRate; | ||
config.mouseDownAnimateLogicFrame = | ||
!config.mouseReleased || !config.mouseDownAnimateReleasedFrame | ||
? relativeFrame / speed | ||
: relativeFrame / speed + | ||
((relativeFrame - config.mouseDownAnimateReleasedFrame) / speed) * accelerateRate; | ||
if (config.mouseDownAnimateLogicFrame > 1) storage.cleanUpAnimation(config); | ||
}); | ||
storage.canvasList.forEach((config, index) => { | ||
config.currentFrameId = frame; | ||
paintCanvas(config, storage, force); | ||
}); | ||
storage.dirty = true; | ||
storage.paintedClientX = storage.clientX; | ||
storage.paintedClientY = storage.clientY; | ||
if (storage.mouseInBoundary || storage.animationQueue.size !== 0) { | ||
window.requestAnimationFrame(frame => { | ||
storage.paintAll(frame); | ||
}); | ||
} else { | ||
storage.raf = null; | ||
} | ||
}, | ||
resetAll: () => { | ||
storage.canvasList.forEach(config => { | ||
paintCanvas(config, storage); | ||
}); | ||
}, | ||
initializeAnimation: () => { | ||
const config = storage.canvasList.find(x => x.mouseInCanvas()); | ||
if (!config) return; | ||
storage.animationQueue.add(config); | ||
config.mouseDownAnimateStartFrame = null; | ||
config.mousePressed = true; | ||
config.mouseReleased = false; | ||
}, | ||
switchAnimation: () => { | ||
storage.animationQueue.forEach(config => { | ||
if (!config.mouseReleased) { | ||
config.mouseReleased = true; | ||
config.mouseDownAnimateReleasedFrame = config.mouseDownAnimateCurrentFrame; | ||
config.mouseUpClientX = storage.clientX; | ||
config.mouseUpClientY = storage.clientY; | ||
} | ||
}); | ||
}, | ||
cleanUpAnimation: (config: CanvasConfig) => { | ||
config.mouseUpClientX = null; | ||
config.mouseUpClientY = null; | ||
config.mouseDownAnimateStartFrame = null; | ||
config.mouseDownAnimateCurrentFrame = null; | ||
config.mouseDownAnimateReleasedFrame = null; | ||
config.mouseDownAnimateLogicFrame = null; | ||
config.mousePressed = false; | ||
config.mouseReleased = false; | ||
storage.animationQueue.delete(config); | ||
paintCanvas(config, storage, true); | ||
}, | ||
animationQueue: new Set(), | ||
getRevealAnimationConfig: () => { | ||
return storage.canvasList.find(x => x.mouseInCanvas()) || null; | ||
}, | ||
revealAnimationConfig: null, | ||
mouseUpClientX: null, | ||
mouseUpClientY: null, | ||
mouseDownAnimateStartFrame: null, | ||
mouseDownAnimateCurrentFrame: null, | ||
mouseDownAnimateReleasedFrame: null, | ||
mouseDownAnimateLogicFrame: null, | ||
mousePressed: false, | ||
mouseReleased: false, | ||
}; | ||
this._storage.push(storage); | ||
return storage; | ||
removeBoundary = (store: RevealBoundaryStore) => { | ||
this._storage = this._storage.filter(x => x === store); | ||
}; | ||
} | ||
const paintCanvas = (config: CanvasConfig, storage: RevealBoundaryStore, force?: boolean, debug?: boolean) => { | ||
const animationPlaying = storage.animationQueue.has(config); | ||
if ( | ||
storage.clientX === storage.paintedClientX && | ||
storage.clientY === storage.paintedClientY && | ||
!animationPlaying && | ||
!force | ||
) | ||
return; | ||
if (!config.ctx) return; | ||
config.ctx.clearRect(0, 0, config.width, config.height); | ||
storage.dirty = false; | ||
if (!storage.mouseInBoundary && !animationPlaying) return; | ||
if (config.cachedRevealBitmap.bitmaps.length < 2) return; | ||
config.cacheCanvasPaintingStyle(); | ||
config.cacheRevealBitmaps(); | ||
const { top, left, width, height, cacheCanvasSize, trueFillRadius } = config.cachedStyle; | ||
const borderStyle = config.cachedStyle.borderStyle; | ||
const borderWidth = config.cachedStyle.borderWidth; | ||
const fillMode = config.cachedStyle.fillMode; | ||
const relativeX = storage.clientX - left; | ||
const relativeY = storage.clientY - top; | ||
const mouseInCanvas = relativeX > 0 && relativeX < width && (relativeY > 0 && relativeY < height); | ||
if (!mouseInCanvas && !config.cachedStyle.diffuse && !animationPlaying) return; | ||
let fillX = 0, | ||
fillY = 0, | ||
fillW = 0, | ||
fillH = 0; | ||
switch (borderStyle) { | ||
case 'full': | ||
fillX = borderWidth; | ||
fillY = borderWidth; | ||
fillW = width - 2 * borderWidth; | ||
fillH = height - 2 * borderWidth; | ||
break; | ||
case 'half': | ||
fillX = 0; | ||
fillY = borderWidth; | ||
fillW = width; | ||
fillH = height - 2 * borderWidth; | ||
break; | ||
case 'none': | ||
fillX = 0; | ||
fillY = 0; | ||
fillW = width; | ||
fillH = height; | ||
break; | ||
default: | ||
throw new SyntaxError('The value of `--reveal-border-style` must be `full`, `half` or `none`!'); | ||
} | ||
const putX = relativeX - cacheCanvasSize / 2; | ||
const putY = relativeY - cacheCanvasSize / 2; | ||
if (isNaN(relativeX) || isNaN(relativeY)) return; | ||
if (storage.mouseInBoundary) { | ||
if (borderStyle !== 'none') { | ||
config.ctx.putImageData(config.cachedRevealBitmap.bitmaps[0], putX, putY, -putX, -putY, width, height); | ||
config.ctx.clearRect(fillX, fillY, fillW, fillH); | ||
} | ||
if (fillMode != 'none' && mouseInCanvas) | ||
config.ctx.putImageData( | ||
config.cachedRevealBitmap.bitmaps[1], | ||
putX, | ||
putY, | ||
fillX - putX, | ||
fillY - putY, | ||
fillW, | ||
fillH, | ||
); | ||
} | ||
if (!config.mousePressed || !config.mouseDownAnimateLogicFrame) return; | ||
let animateGrd; | ||
if (config.mouseReleased && config.mouseUpClientX && config.mouseUpClientY) { | ||
animateGrd = config.ctx.createRadialGradient( | ||
config.mouseUpClientX - left, | ||
config.mouseUpClientY - top, | ||
0, | ||
config.mouseUpClientX - left, | ||
config.mouseUpClientY - top, | ||
trueFillRadius[1], | ||
); | ||
} else { | ||
animateGrd = config.ctx.createRadialGradient(relativeX, relativeY, 0, relativeX, relativeY, trueFillRadius[1]); | ||
} | ||
config.getAnimateGrd(config.mouseDownAnimateLogicFrame, animateGrd); | ||
config.ctx.fillStyle = animateGrd; | ||
config.ctx.fillRect(fillX, fillY, fillW * 1.5, fillH * 1.5); | ||
}; | ||
export default RevealStateManager; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
65
1410870
1724
1