canvasparticles-js 4.2.1 → 4.2.3
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.
- package/dist/index.cjs +14 -12
- package/dist/index.mjs +14 -12
- package/dist/index.umd.js +1 -1
- package/dist/types/index.d.ts +3 -2
- package/package.json +1 -1
- package/src/index.ts +31 -26
- package/src/types/index.ts +4 -2
package/dist/index.cjs
CHANGED
|
@@ -21,7 +21,7 @@ function Mulberry32(seed) {
|
|
|
21
21
|
// Spectral test: /demo/mulberry32.html
|
|
22
22
|
const prng = Mulberry32(Math.random() * 2 ** 32).next;
|
|
23
23
|
class CanvasParticles {
|
|
24
|
-
static version = "4.2.
|
|
24
|
+
static version = "4.2.3";
|
|
25
25
|
static MAX_DT = 1000 / 50; // milliseconds between updates @ 50 FPS
|
|
26
26
|
static BASE_DT = 1000 / 60; // milliseconds between updates @ 60 FPS
|
|
27
27
|
/** Defines mouse interaction types with the particles */
|
|
@@ -43,6 +43,8 @@ class CanvasParticles {
|
|
|
43
43
|
else
|
|
44
44
|
instance.options.animation?.stopOnLeave && instance.stop({ auto: true, clear: false });
|
|
45
45
|
}
|
|
46
|
+
}, {
|
|
47
|
+
rootMargin: '-1px',
|
|
46
48
|
});
|
|
47
49
|
static canvasResizeObserver = new ResizeObserver((entries) => {
|
|
48
50
|
// Seperate for loops is very important to prevent huge forced reflow overhead
|
|
@@ -241,8 +243,8 @@ class CanvasParticles {
|
|
|
241
243
|
const gravRepulsiveMult = connectDist * this.option.gravity.repulsive * step;
|
|
242
244
|
const gravPullingMult = connectDist * this.option.gravity.pulling * step;
|
|
243
245
|
const maxRepulsiveDist = connectDist / 2;
|
|
244
|
-
const maxRepulsiveDistSq = maxRepulsiveDist
|
|
245
|
-
const eps =
|
|
246
|
+
const maxRepulsiveDistSq = maxRepulsiveDist ** 2;
|
|
247
|
+
const eps = connectDist ** 2 / 256;
|
|
246
248
|
for (let i = 0; i < len; i++) {
|
|
247
249
|
const particleA = particles[i];
|
|
248
250
|
for (let j = i + 1; j < len; j++) {
|
|
@@ -405,9 +407,9 @@ class CanvasParticles {
|
|
|
405
407
|
const particles = this.particles;
|
|
406
408
|
const ctx = this.ctx;
|
|
407
409
|
const maxDist = this.option.particles.connectDist;
|
|
408
|
-
const maxDistSq = maxDist
|
|
410
|
+
const maxDistSq = maxDist ** 2;
|
|
409
411
|
const halfMaxDist = maxDist / 2;
|
|
410
|
-
const halfMaxDistSq = halfMaxDist
|
|
412
|
+
const halfMaxDistSq = halfMaxDist ** 2;
|
|
411
413
|
const drawAll = maxDist >= Math.min(this.canvas.width, this.canvas.height);
|
|
412
414
|
const maxWorkPerParticle = maxDistSq * this.option.particles.maxWork;
|
|
413
415
|
const alpha = this.color.alpha;
|
|
@@ -531,16 +533,16 @@ class CanvasParticles {
|
|
|
531
533
|
},
|
|
532
534
|
mouse: {
|
|
533
535
|
interactionType: ~~pno('mouse.interactionType', options.mouse?.interactionType, CanvasParticles.interactionType.MOVE, { min: 0, max: 2 }),
|
|
534
|
-
connectDistMult: pno('mouse.connectDistMult', options.mouse?.connectDistMult, 2 / 3),
|
|
536
|
+
connectDistMult: pno('mouse.connectDistMult', options.mouse?.connectDistMult, 2 / 3, { min: 0 }),
|
|
535
537
|
connectDist: 1 /* post processed */,
|
|
536
|
-
distRatio: pno('mouse.distRatio', options.mouse?.distRatio, 2 / 3),
|
|
538
|
+
distRatio: pno('mouse.distRatio', options.mouse?.distRatio, 2 / 3, { min: 0 }),
|
|
537
539
|
},
|
|
538
540
|
particles: {
|
|
539
541
|
regenerateOnResize: !!options.particles?.regenerateOnResize,
|
|
540
542
|
drawLines: !!(options.particles?.drawLines ?? true),
|
|
541
543
|
color: options.particles?.color ?? 'black',
|
|
542
544
|
ppm: ~~pno('particles.ppm', options.particles?.ppm, 100),
|
|
543
|
-
max: Math.round(pno('particles.max', options.particles?.max, Infinity)),
|
|
545
|
+
max: Math.round(pno('particles.max', options.particles?.max, Infinity, { min: 0 })),
|
|
544
546
|
maxWork: Math.round(pno('particles.maxWork', options.particles?.maxWork, Infinity, { min: 0 })),
|
|
545
547
|
connectDist: ~~pno('particles.connectDistance', options.particles?.connectDistance, 150, { min: 1 }),
|
|
546
548
|
relSpeed: pno('particles.relSpeed', options.particles?.relSpeed, 1, { min: 0 }),
|
|
@@ -548,8 +550,8 @@ class CanvasParticles {
|
|
|
548
550
|
rotationSpeed: pno('particles.rotationSpeed', options.particles?.rotationSpeed, 2, { min: 0 }) / 100,
|
|
549
551
|
},
|
|
550
552
|
gravity: {
|
|
551
|
-
repulsive: pno('gravity.repulsive', options.gravity?.repulsive, 0),
|
|
552
|
-
pulling: pno('gravity.pulling', options.gravity?.pulling, 0),
|
|
553
|
+
repulsive: pno('gravity.repulsive', options.gravity?.repulsive, 0, { min: 0 }),
|
|
554
|
+
pulling: pno('gravity.pulling', options.gravity?.pulling, 0, { min: 0 }),
|
|
553
555
|
friction: pno('gravity.friction', options.gravity?.friction, 0.8, { min: 0, max: 1 }),
|
|
554
556
|
},
|
|
555
557
|
};
|
|
@@ -570,8 +572,8 @@ class CanvasParticles {
|
|
|
570
572
|
}
|
|
571
573
|
/** @public Transform the distance multiplier (float) to absolute distance (px) */
|
|
572
574
|
setMouseConnectDistMult(connectDistMult) {
|
|
573
|
-
|
|
574
|
-
|
|
575
|
+
const mult = CanvasParticles.parseNumericOption('mouse.connectDistMult', connectDistMult, 2 / 3, { min: 0 });
|
|
576
|
+
this.option.mouse.connectDist = this.option.particles.connectDist * mult;
|
|
575
577
|
}
|
|
576
578
|
/** @public Format particle color and opacity */
|
|
577
579
|
setParticleColor(color) {
|
package/dist/index.mjs
CHANGED
|
@@ -19,7 +19,7 @@ function Mulberry32(seed) {
|
|
|
19
19
|
// Spectral test: /demo/mulberry32.html
|
|
20
20
|
const prng = Mulberry32(Math.random() * 2 ** 32).next;
|
|
21
21
|
class CanvasParticles {
|
|
22
|
-
static version = "4.2.
|
|
22
|
+
static version = "4.2.3";
|
|
23
23
|
static MAX_DT = 1000 / 50; // milliseconds between updates @ 50 FPS
|
|
24
24
|
static BASE_DT = 1000 / 60; // milliseconds between updates @ 60 FPS
|
|
25
25
|
/** Defines mouse interaction types with the particles */
|
|
@@ -41,6 +41,8 @@ class CanvasParticles {
|
|
|
41
41
|
else
|
|
42
42
|
instance.options.animation?.stopOnLeave && instance.stop({ auto: true, clear: false });
|
|
43
43
|
}
|
|
44
|
+
}, {
|
|
45
|
+
rootMargin: '-1px',
|
|
44
46
|
});
|
|
45
47
|
static canvasResizeObserver = new ResizeObserver((entries) => {
|
|
46
48
|
// Seperate for loops is very important to prevent huge forced reflow overhead
|
|
@@ -239,8 +241,8 @@ class CanvasParticles {
|
|
|
239
241
|
const gravRepulsiveMult = connectDist * this.option.gravity.repulsive * step;
|
|
240
242
|
const gravPullingMult = connectDist * this.option.gravity.pulling * step;
|
|
241
243
|
const maxRepulsiveDist = connectDist / 2;
|
|
242
|
-
const maxRepulsiveDistSq = maxRepulsiveDist
|
|
243
|
-
const eps =
|
|
244
|
+
const maxRepulsiveDistSq = maxRepulsiveDist ** 2;
|
|
245
|
+
const eps = connectDist ** 2 / 256;
|
|
244
246
|
for (let i = 0; i < len; i++) {
|
|
245
247
|
const particleA = particles[i];
|
|
246
248
|
for (let j = i + 1; j < len; j++) {
|
|
@@ -403,9 +405,9 @@ class CanvasParticles {
|
|
|
403
405
|
const particles = this.particles;
|
|
404
406
|
const ctx = this.ctx;
|
|
405
407
|
const maxDist = this.option.particles.connectDist;
|
|
406
|
-
const maxDistSq = maxDist
|
|
408
|
+
const maxDistSq = maxDist ** 2;
|
|
407
409
|
const halfMaxDist = maxDist / 2;
|
|
408
|
-
const halfMaxDistSq = halfMaxDist
|
|
410
|
+
const halfMaxDistSq = halfMaxDist ** 2;
|
|
409
411
|
const drawAll = maxDist >= Math.min(this.canvas.width, this.canvas.height);
|
|
410
412
|
const maxWorkPerParticle = maxDistSq * this.option.particles.maxWork;
|
|
411
413
|
const alpha = this.color.alpha;
|
|
@@ -529,16 +531,16 @@ class CanvasParticles {
|
|
|
529
531
|
},
|
|
530
532
|
mouse: {
|
|
531
533
|
interactionType: ~~pno('mouse.interactionType', options.mouse?.interactionType, CanvasParticles.interactionType.MOVE, { min: 0, max: 2 }),
|
|
532
|
-
connectDistMult: pno('mouse.connectDistMult', options.mouse?.connectDistMult, 2 / 3),
|
|
534
|
+
connectDistMult: pno('mouse.connectDistMult', options.mouse?.connectDistMult, 2 / 3, { min: 0 }),
|
|
533
535
|
connectDist: 1 /* post processed */,
|
|
534
|
-
distRatio: pno('mouse.distRatio', options.mouse?.distRatio, 2 / 3),
|
|
536
|
+
distRatio: pno('mouse.distRatio', options.mouse?.distRatio, 2 / 3, { min: 0 }),
|
|
535
537
|
},
|
|
536
538
|
particles: {
|
|
537
539
|
regenerateOnResize: !!options.particles?.regenerateOnResize,
|
|
538
540
|
drawLines: !!(options.particles?.drawLines ?? true),
|
|
539
541
|
color: options.particles?.color ?? 'black',
|
|
540
542
|
ppm: ~~pno('particles.ppm', options.particles?.ppm, 100),
|
|
541
|
-
max: Math.round(pno('particles.max', options.particles?.max, Infinity)),
|
|
543
|
+
max: Math.round(pno('particles.max', options.particles?.max, Infinity, { min: 0 })),
|
|
542
544
|
maxWork: Math.round(pno('particles.maxWork', options.particles?.maxWork, Infinity, { min: 0 })),
|
|
543
545
|
connectDist: ~~pno('particles.connectDistance', options.particles?.connectDistance, 150, { min: 1 }),
|
|
544
546
|
relSpeed: pno('particles.relSpeed', options.particles?.relSpeed, 1, { min: 0 }),
|
|
@@ -546,8 +548,8 @@ class CanvasParticles {
|
|
|
546
548
|
rotationSpeed: pno('particles.rotationSpeed', options.particles?.rotationSpeed, 2, { min: 0 }) / 100,
|
|
547
549
|
},
|
|
548
550
|
gravity: {
|
|
549
|
-
repulsive: pno('gravity.repulsive', options.gravity?.repulsive, 0),
|
|
550
|
-
pulling: pno('gravity.pulling', options.gravity?.pulling, 0),
|
|
551
|
+
repulsive: pno('gravity.repulsive', options.gravity?.repulsive, 0, { min: 0 }),
|
|
552
|
+
pulling: pno('gravity.pulling', options.gravity?.pulling, 0, { min: 0 }),
|
|
551
553
|
friction: pno('gravity.friction', options.gravity?.friction, 0.8, { min: 0, max: 1 }),
|
|
552
554
|
},
|
|
553
555
|
};
|
|
@@ -568,8 +570,8 @@ class CanvasParticles {
|
|
|
568
570
|
}
|
|
569
571
|
/** @public Transform the distance multiplier (float) to absolute distance (px) */
|
|
570
572
|
setMouseConnectDistMult(connectDistMult) {
|
|
571
|
-
|
|
572
|
-
|
|
573
|
+
const mult = CanvasParticles.parseNumericOption('mouse.connectDistMult', connectDistMult, 2 / 3, { min: 0 });
|
|
574
|
+
this.option.mouse.connectDist = this.option.particles.connectDist * mult;
|
|
573
575
|
}
|
|
574
576
|
/** @public Format particle color and opacity */
|
|
575
577
|
setParticleColor(color) {
|
package/dist/index.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(t,i){"object"==typeof exports&&"undefined"!=typeof module?module.exports=i():"function"==typeof define&&define.amd?define(i):(t="undefined"!=typeof globalThis?globalThis:t||self).CanvasParticles=i()}(this,function(){"use strict";const t=2*Math.PI;const i=function(t){let i=t>>>0;return{next(){let t=i+1831565813|0;return i=t,t=Math.imul(t^t>>>15,1|t),t^=t+Math.imul(t^t>>>7,61|t),((t^t>>>14)>>>0)/4294967296}}}(Math.random()*2**32).next;class e{static version="4.2.1";static MAX_DT=20;static BASE_DT=1e3/60;static interactionType=Object.freeze({NONE:0,SHIFT:1,MOVE:2});static canvasIntersectionObserver=new IntersectionObserver(t=>{for(let i=0;i<t.length;i++){const e=t[i],s=e.target,n=s.instance;if(!n.options?.animation)return;(s.inViewbox=e.isIntersecting)?n.options.animation?.startOnEnter&&n.start({auto:!0}):n.options.animation?.stopOnLeave&&n.stop({auto:!0,clear:!1})}});static canvasResizeObserver=new ResizeObserver(t=>{for(let i=0;i<t.length;i++){t[i].target.instance.updateCanvasRect()}for(let i=0;i<t.length;i++){t[i].target.instance.resizeCanvas()}});static defaultIfNaN=(t,i)=>isNaN(+t)?i:+t;static parseNumericOption=(t,i,s,n)=>{if(null==i)return s;const{min:o=-1/0,max:a=1/0}=n??{};return isFinite(o)&&i<o?console.warn(new RangeError(`option.${t} was clamped to ${o} as ${i} is too low`)):isFinite(a)&&i>a&&console.warn(new RangeError(`option.${t} was clamped to ${a} as ${i} is too high`)),e.defaultIfNaN(Math.min(Math.max(i??s,o),a),s)};canvas;ctx;enableAnimating=!1;isAnimating=!1;lastAnimationFrame=0;particles=[];particleCount=0;clientX=1/0;clientY=1/0;mouseX=1/0;mouseY=1/0;width;height;offX;offY;option;color;constructor(t,i={}){let s;if(t instanceof HTMLCanvasElement)s=t;else{if("string"!=typeof t)throw new TypeError("selector is not a string and neither a HTMLCanvasElement itself");if(s=document.querySelector(t),!(s instanceof HTMLCanvasElement))throw new Error("selector does not point to a canvas")}this.canvas=s,this.canvas.instance=this,this.canvas.inViewbox=!0;const n=this.canvas.getContext("2d");if(!n)throw new Error("failed to get 2D context from canvas");this.ctx=n,this.options=i,e.canvasIntersectionObserver.observe(this.canvas),e.canvasResizeObserver.observe(this.canvas),this.resizeCanvas=this.resizeCanvas.bind(this),this.handleMouseMove=this.handleMouseMove.bind(this),this.handleScroll=this.handleScroll.bind(this),this.updateCanvasRect(),this.resizeCanvas(),window.addEventListener("mousemove",this.handleMouseMove,{passive:!0}),window.addEventListener("scroll",this.handleScroll,{passive:!0})}updateCanvasRect(){const{top:t,left:i,width:e,height:s}=this.canvas.getBoundingClientRect();this.canvas.rect={top:t,left:i,width:e,height:s}}handleMouseMove(t){this.enableAnimating&&(this.clientX=t.clientX,this.clientY=t.clientY,this.isAnimating&&this.updateMousePos())}handleScroll(){this.enableAnimating&&(this.updateCanvasRect(),this.isAnimating&&this.updateMousePos())}updateMousePos(){const{top:t,left:i}=this.canvas.rect;this.mouseX=this.clientX-i,this.mouseY=this.clientY-t}resizeCanvas(){const t=this.canvas.width=this.canvas.rect.width,i=this.canvas.height=this.canvas.rect.height;this.mouseX=1/0,this.mouseY=1/0,this.width=Math.max(t+2*this.option.particles.connectDist,1),this.height=Math.max(i+2*this.option.particles.connectDist,1),this.offX=(t-this.width)/2,this.offY=(i-this.height)/2,this.option.particles.regenerateOnResize||0===this.particles.length?this.newParticles():this.matchParticleCount({updateBounds:!0}),this.isAnimating&&this.#t()}#i(){const t=this.option.particles.ppm*this.width*this.height/1e6|0;if(this.particleCount=Math.min(this.option.particles.max,t),!isFinite(this.particleCount))throw new RangeError("particleCount must be finite")}newParticles(){this.#i(),this.particles=[];for(let t=0;t<this.particleCount;t++)this.createParticle()}matchParticleCount({updateBounds:t=!1}={}){for(this.#i(),this.particles=this.particles.slice(0,this.particleCount),t&&this.particles.forEach(t=>this.#e(t));this.particleCount>this.particles.length;)this.createParticle()}createParticle(e,s,n,o,a){const r={posX:e="number"==typeof e?e-this.offX:i()*this.width,posY:s="number"==typeof s?s-this.offY:i()*this.height,x:e,y:s,velX:0,velY:0,offX:0,offY:0,dir:n||i()*t,speed:o||(.5+.5*i())*this.option.particles.relSpeed,size:a||(.5+i()**5*2)*this.option.particles.relSize,gridPos:{x:1,y:1},isVisible:!1};this.#e(r),this.particles.push(r)}#e(t){t.bounds={top:-t.size,right:this.canvas.width+t.size,bottom:this.canvas.height+t.size,left:-t.size}}#s(t){const i=this.option.gravity.repulsive>0,e=this.option.gravity.pulling>0;if(!i&&!e)return;const s=this.particleCount,n=this.particles,o=this.option.particles.connectDist,a=o*this.option.gravity.repulsive*t,r=o*this.option.gravity.pulling*t,c=o/2,h=c*c,l=o*o/256;for(let t=0;t<s;t++){const i=n[t];for(let o=t+1;o<s;o++){const t=n[o],s=i.posX-t.posX,c=i.posY-t.posY,p=s*s+c*c;if(p>=h&&!e)continue;let u,d,f;u=Math.atan2(-c,-s),d=1/(p+l);const m=Math.cos(u),v=Math.sin(u);if(p<h){f=d*a;const e=m*f,s=v*f;i.velX-=e,i.velY-=s,t.velX+=e,t.velY+=s}if(!e)continue;f=d*r;const g=m*f,y=v*f;i.velX+=g,i.velY+=y,t.velX-=g,t.velY-=y}}}#n(i){const s=this.particleCount,n=this.particles,o=this.width,a=this.height,r=this.offX,c=this.offY,h=this.mouseX,l=this.mouseY,p=this.option.particles.rotationSpeed*i,u=this.option.gravity.friction,d=this.option.mouse.connectDist,f=this.option.mouse.distRatio,m=this.option.mouse.interactionType===e.interactionType.NONE,v=this.option.mouse.interactionType===e.interactionType.MOVE,g=1-Math.pow(.75,i);for(let e=0;e<s;e++){const s=n[e];s.dir+=2*(Math.random()-.5)*p*i,s.dir%=t;const y=Math.sin(s.dir)*s.speed,x=Math.cos(s.dir)*s.speed;s.posX+=(y+s.velX)*i,s.posY+=(x+s.velY)*i,s.posX%=o,s.posX<0&&(s.posX+=o),s.posY%=a,s.posY<0&&(s.posY+=a),s.velX*=Math.pow(u,i),s.velY*=Math.pow(u,i);const b=s.posX+r-h,w=s.posY+c-l;if(!m){const t=d/Math.hypot(b,w);f<t?(s.offX+=(t*b-b-s.offX)*g,s.offY+=(t*w-w-s.offY)*g):(s.offX-=s.offX*g,s.offY-=s.offY*g)}s.x=s.posX+s.offX,s.y=s.posY+s.offY,v&&(s.posX=s.x,s.posY=s.y),s.x+=r,s.y+=c,this.#o(s),s.isVisible=1===s.gridPos.x&&1===s.gridPos.y}}#o(t){t.gridPos.x=+(t.x>=t.bounds.left)+ +(t.x>t.bounds.right),t.gridPos.y=+(t.y>=t.bounds.top)+ +(t.y>t.bounds.bottom)}#a(t,i){return!(!t.isVisible&&!i.isVisible)||!(t.gridPos.x===i.gridPos.x&&1!==t.gridPos.x||t.gridPos.y===i.gridPos.y&&1!==t.gridPos.y)}#r(){const i=this.particleCount,e=this.particles,s=this.ctx;for(let n=0;n<i;n++){const i=e[n];i.isVisible&&(i.size>1?(s.beginPath(),s.arc(i.x,i.y,i.size,0,t),s.fill(),s.closePath()):s.fillRect(i.x-i.size,i.y-i.size,2*i.size,2*i.size))}}#c(){const t=this.particleCount,i=this.particles,e=this.ctx,s=this.option.particles.connectDist,n=s*s,o=s/2,a=o*o,r=s>=Math.min(this.canvas.width,this.canvas.height),c=n*this.option.particles.maxWork,h=this.color.alpha,l=this.color.alpha*s,p=[];for(let s=0;s<t;s++){const o=i[s];let u=0;for(let d=s+1;d<t;d++){const t=i[d];if(!r&&!this.#a(o,t))continue;const s=o.x-t.x,f=o.y-t.y,m=s*s+f*f;if(!(m>n)&&(m>a?(e.globalAlpha=l/Math.sqrt(m)-h,e.beginPath(),e.moveTo(o.x,o.y),e.lineTo(t.x,t.y),e.stroke()):p.push([o.x,o.y,t.x,t.y]),(u+=m)>=c))break}}if(p.length){e.globalAlpha=h,e.beginPath();for(let t=0;t<p.length;t++){const i=p[t];e.moveTo(i[0],i[1]),e.lineTo(i[2],i[3])}e.stroke()}}#t(){this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.globalAlpha=this.color.alpha,this.ctx.fillStyle=this.color.hex,this.ctx.strokeStyle=this.color.hex,this.ctx.lineWidth=1,this.#r(),this.options.particles.drawLines&&this.#c()}#h(){if(!this.isAnimating)return;requestAnimationFrame(()=>this.#h());const t=performance.now(),i=Math.min(t-this.lastAnimationFrame,e.MAX_DT)/e.BASE_DT;this.#s(i),this.#n(i),this.#t(),this.lastAnimationFrame=t}start({auto:t=!1}={}){return this.isAnimating||t&&!this.enableAnimating||(this.enableAnimating=!0,this.isAnimating=!0,this.updateCanvasRect(),requestAnimationFrame(()=>this.#h())),!this.canvas.inViewbox&&this.option.animation.startOnEnter&&(this.isAnimating=!1),this}stop({auto:t=!1,clear:i=!0}={}){return t||(this.enableAnimating=!1),this.isAnimating=!1,!1!==i&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),!0}destroy(){this.stop(),e.canvasIntersectionObserver.unobserve(this.canvas),e.canvasResizeObserver.unobserve(this.canvas),window.removeEventListener("mousemove",this.handleMouseMove),window.removeEventListener("scroll",this.handleScroll),this.canvas?.remove(),Object.keys(this).forEach(t=>delete this[t])}set options(t){const i=e.parseNumericOption;this.option={background:t.background??!1,animation:{startOnEnter:!!(t.animation?.startOnEnter??1),stopOnLeave:!!(t.animation?.stopOnLeave??1)},mouse:{interactionType:~~i("mouse.interactionType",t.mouse?.interactionType,e.interactionType.MOVE,{min:0,max:2}),connectDistMult:i("mouse.connectDistMult",t.mouse?.connectDistMult,2/3),connectDist:1,distRatio:i("mouse.distRatio",t.mouse?.distRatio,2/3)},particles:{regenerateOnResize:!!t.particles?.regenerateOnResize,drawLines:!!(t.particles?.drawLines??1),color:t.particles?.color??"black",ppm:~~i("particles.ppm",t.particles?.ppm,100),max:Math.round(i("particles.max",t.particles?.max,1/0)),maxWork:Math.round(i("particles.maxWork",t.particles?.maxWork,1/0,{min:0})),connectDist:~~i("particles.connectDistance",t.particles?.connectDistance,150,{min:1}),relSpeed:i("particles.relSpeed",t.particles?.relSpeed,1,{min:0}),relSize:i("particles.relSize",t.particles?.relSize,1,{min:0}),rotationSpeed:i("particles.rotationSpeed",t.particles?.rotationSpeed,2,{min:0})/100},gravity:{repulsive:i("gravity.repulsive",t.gravity?.repulsive,0),pulling:i("gravity.pulling",t.gravity?.pulling,0),friction:i("gravity.friction",t.gravity?.friction,.8,{min:0,max:1})}},this.setBackground(this.option.background),this.setMouseConnectDistMult(this.option.mouse.connectDistMult),this.setParticleColor(this.option.particles.color)}get options(){return this.option}setBackground(t){if(t){if("string"!=typeof t)throw new TypeError("background is not a string");this.canvas.style.background=this.option.background=t}}setMouseConnectDistMult(t){this.option.mouse.connectDist=this.option.particles.connectDist*(isNaN(t)?2/3:t)}setParticleColor(t){if(this.ctx.fillStyle=t,"#"===String(this.ctx.fillStyle)[0])this.color={hex:String(this.ctx.fillStyle),alpha:1};else{let t=String(this.ctx.fillStyle).split(",").at(-1);t=t?.slice(1,-1)??"1",this.ctx.fillStyle=String(this.ctx.fillStyle).split(",").slice(0,-1).join(",")+", 1)",this.color={hex:String(this.ctx.fillStyle),alpha:isNaN(+t)?1:+t}}}}return e});
|
|
1
|
+
!function(t,i){"object"==typeof exports&&"undefined"!=typeof module?module.exports=i():"function"==typeof define&&define.amd?define(i):(t="undefined"!=typeof globalThis?globalThis:t||self).CanvasParticles=i()}(this,function(){"use strict";const t=2*Math.PI;const i=function(t){let i=t>>>0;return{next(){let t=i+1831565813|0;return i=t,t=Math.imul(t^t>>>15,1|t),t^=t+Math.imul(t^t>>>7,61|t),((t^t>>>14)>>>0)/4294967296}}}(Math.random()*2**32).next;class e{static version="4.2.3";static MAX_DT=20;static BASE_DT=1e3/60;static interactionType=Object.freeze({NONE:0,SHIFT:1,MOVE:2});static canvasIntersectionObserver=new IntersectionObserver(t=>{for(let i=0;i<t.length;i++){const e=t[i],s=e.target,n=s.instance;if(!n.options?.animation)return;(s.inViewbox=e.isIntersecting)?n.options.animation?.startOnEnter&&n.start({auto:!0}):n.options.animation?.stopOnLeave&&n.stop({auto:!0,clear:!1})}},{rootMargin:"-1px"});static canvasResizeObserver=new ResizeObserver(t=>{for(let i=0;i<t.length;i++){t[i].target.instance.updateCanvasRect()}for(let i=0;i<t.length;i++){t[i].target.instance.resizeCanvas()}});static defaultIfNaN=(t,i)=>isNaN(+t)?i:+t;static parseNumericOption=(t,i,s,n)=>{if(null==i)return s;const{min:o=-1/0,max:a=1/0}=n??{};return isFinite(o)&&i<o?console.warn(new RangeError(`option.${t} was clamped to ${o} as ${i} is too low`)):isFinite(a)&&i>a&&console.warn(new RangeError(`option.${t} was clamped to ${a} as ${i} is too high`)),e.defaultIfNaN(Math.min(Math.max(i??s,o),a),s)};canvas;ctx;enableAnimating=!1;isAnimating=!1;lastAnimationFrame=0;particles=[];particleCount=0;clientX=1/0;clientY=1/0;mouseX=1/0;mouseY=1/0;width;height;offX;offY;option;color;constructor(t,i={}){let s;if(t instanceof HTMLCanvasElement)s=t;else{if("string"!=typeof t)throw new TypeError("selector is not a string and neither a HTMLCanvasElement itself");if(s=document.querySelector(t),!(s instanceof HTMLCanvasElement))throw new Error("selector does not point to a canvas")}this.canvas=s,this.canvas.instance=this,this.canvas.inViewbox=!0;const n=this.canvas.getContext("2d");if(!n)throw new Error("failed to get 2D context from canvas");this.ctx=n,this.options=i,e.canvasIntersectionObserver.observe(this.canvas),e.canvasResizeObserver.observe(this.canvas),this.resizeCanvas=this.resizeCanvas.bind(this),this.handleMouseMove=this.handleMouseMove.bind(this),this.handleScroll=this.handleScroll.bind(this),this.updateCanvasRect(),this.resizeCanvas(),window.addEventListener("mousemove",this.handleMouseMove,{passive:!0}),window.addEventListener("scroll",this.handleScroll,{passive:!0})}updateCanvasRect(){const{top:t,left:i,width:e,height:s}=this.canvas.getBoundingClientRect();this.canvas.rect={top:t,left:i,width:e,height:s}}handleMouseMove(t){this.enableAnimating&&(this.clientX=t.clientX,this.clientY=t.clientY,this.isAnimating&&this.updateMousePos())}handleScroll(){this.enableAnimating&&(this.updateCanvasRect(),this.isAnimating&&this.updateMousePos())}updateMousePos(){const{top:t,left:i}=this.canvas.rect;this.mouseX=this.clientX-i,this.mouseY=this.clientY-t}resizeCanvas(){const t=this.canvas.width=this.canvas.rect.width,i=this.canvas.height=this.canvas.rect.height;this.mouseX=1/0,this.mouseY=1/0,this.width=Math.max(t+2*this.option.particles.connectDist,1),this.height=Math.max(i+2*this.option.particles.connectDist,1),this.offX=(t-this.width)/2,this.offY=(i-this.height)/2,this.option.particles.regenerateOnResize||0===this.particles.length?this.newParticles():this.matchParticleCount({updateBounds:!0}),this.isAnimating&&this.#t()}#i(){const t=this.option.particles.ppm*this.width*this.height/1e6|0;if(this.particleCount=Math.min(this.option.particles.max,t),!isFinite(this.particleCount))throw new RangeError("particleCount must be finite")}newParticles(){this.#i(),this.particles=[];for(let t=0;t<this.particleCount;t++)this.createParticle()}matchParticleCount({updateBounds:t=!1}={}){for(this.#i(),this.particles=this.particles.slice(0,this.particleCount),t&&this.particles.forEach(t=>this.#e(t));this.particleCount>this.particles.length;)this.createParticle()}createParticle(e,s,n,o,a){const r={posX:e="number"==typeof e?e-this.offX:i()*this.width,posY:s="number"==typeof s?s-this.offY:i()*this.height,x:e,y:s,velX:0,velY:0,offX:0,offY:0,dir:n||i()*t,speed:o||(.5+.5*i())*this.option.particles.relSpeed,size:a||(.5+i()**5*2)*this.option.particles.relSize,gridPos:{x:1,y:1},isVisible:!1};this.#e(r),this.particles.push(r)}#e(t){t.bounds={top:-t.size,right:this.canvas.width+t.size,bottom:this.canvas.height+t.size,left:-t.size}}#s(t){const i=this.option.gravity.repulsive>0,e=this.option.gravity.pulling>0;if(!i&&!e)return;const s=this.particleCount,n=this.particles,o=this.option.particles.connectDist,a=o*this.option.gravity.repulsive*t,r=o*this.option.gravity.pulling*t,c=(o/2)**2,h=o**2/256;for(let t=0;t<s;t++){const i=n[t];for(let o=t+1;o<s;o++){const t=n[o],s=i.posX-t.posX,l=i.posY-t.posY,p=s*s+l*l;if(p>=c&&!e)continue;let u,d,m;u=Math.atan2(-l,-s),d=1/(p+h);const f=Math.cos(u),v=Math.sin(u);if(p<c){m=d*a;const e=f*m,s=v*m;i.velX-=e,i.velY-=s,t.velX+=e,t.velY+=s}if(!e)continue;m=d*r;const g=f*m,y=v*m;i.velX+=g,i.velY+=y,t.velX-=g,t.velY-=y}}}#n(i){const s=this.particleCount,n=this.particles,o=this.width,a=this.height,r=this.offX,c=this.offY,h=this.mouseX,l=this.mouseY,p=this.option.particles.rotationSpeed*i,u=this.option.gravity.friction,d=this.option.mouse.connectDist,m=this.option.mouse.distRatio,f=this.option.mouse.interactionType===e.interactionType.NONE,v=this.option.mouse.interactionType===e.interactionType.MOVE,g=1-Math.pow(.75,i);for(let e=0;e<s;e++){const s=n[e];s.dir+=2*(Math.random()-.5)*p*i,s.dir%=t;const y=Math.sin(s.dir)*s.speed,x=Math.cos(s.dir)*s.speed;s.posX+=(y+s.velX)*i,s.posY+=(x+s.velY)*i,s.posX%=o,s.posX<0&&(s.posX+=o),s.posY%=a,s.posY<0&&(s.posY+=a),s.velX*=Math.pow(u,i),s.velY*=Math.pow(u,i);const b=s.posX+r-h,M=s.posY+c-l;if(!f){const t=d/Math.hypot(b,M);m<t?(s.offX+=(t*b-b-s.offX)*g,s.offY+=(t*M-M-s.offY)*g):(s.offX-=s.offX*g,s.offY-=s.offY*g)}s.x=s.posX+s.offX,s.y=s.posY+s.offY,v&&(s.posX=s.x,s.posY=s.y),s.x+=r,s.y+=c,this.#o(s),s.isVisible=1===s.gridPos.x&&1===s.gridPos.y}}#o(t){t.gridPos.x=+(t.x>=t.bounds.left)+ +(t.x>t.bounds.right),t.gridPos.y=+(t.y>=t.bounds.top)+ +(t.y>t.bounds.bottom)}#a(t,i){return!(!t.isVisible&&!i.isVisible)||!(t.gridPos.x===i.gridPos.x&&1!==t.gridPos.x||t.gridPos.y===i.gridPos.y&&1!==t.gridPos.y)}#r(){const i=this.particleCount,e=this.particles,s=this.ctx;for(let n=0;n<i;n++){const i=e[n];i.isVisible&&(i.size>1?(s.beginPath(),s.arc(i.x,i.y,i.size,0,t),s.fill(),s.closePath()):s.fillRect(i.x-i.size,i.y-i.size,2*i.size,2*i.size))}}#c(){const t=this.particleCount,i=this.particles,e=this.ctx,s=this.option.particles.connectDist,n=s**2,o=(s/2)**2,a=s>=Math.min(this.canvas.width,this.canvas.height),r=n*this.option.particles.maxWork,c=this.color.alpha,h=this.color.alpha*s,l=[];for(let s=0;s<t;s++){const p=i[s];let u=0;for(let d=s+1;d<t;d++){const t=i[d];if(!a&&!this.#a(p,t))continue;const s=p.x-t.x,m=p.y-t.y,f=s*s+m*m;if(!(f>n)&&(f>o?(e.globalAlpha=h/Math.sqrt(f)-c,e.beginPath(),e.moveTo(p.x,p.y),e.lineTo(t.x,t.y),e.stroke()):l.push([p.x,p.y,t.x,t.y]),(u+=f)>=r))break}}if(l.length){e.globalAlpha=c,e.beginPath();for(let t=0;t<l.length;t++){const i=l[t];e.moveTo(i[0],i[1]),e.lineTo(i[2],i[3])}e.stroke()}}#t(){this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.globalAlpha=this.color.alpha,this.ctx.fillStyle=this.color.hex,this.ctx.strokeStyle=this.color.hex,this.ctx.lineWidth=1,this.#r(),this.options.particles.drawLines&&this.#c()}#h(){if(!this.isAnimating)return;requestAnimationFrame(()=>this.#h());const t=performance.now(),i=Math.min(t-this.lastAnimationFrame,e.MAX_DT)/e.BASE_DT;this.#s(i),this.#n(i),this.#t(),this.lastAnimationFrame=t}start({auto:t=!1}={}){return this.isAnimating||t&&!this.enableAnimating||(this.enableAnimating=!0,this.isAnimating=!0,this.updateCanvasRect(),requestAnimationFrame(()=>this.#h())),!this.canvas.inViewbox&&this.option.animation.startOnEnter&&(this.isAnimating=!1),this}stop({auto:t=!1,clear:i=!0}={}){return t||(this.enableAnimating=!1),this.isAnimating=!1,!1!==i&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),!0}destroy(){this.stop(),e.canvasIntersectionObserver.unobserve(this.canvas),e.canvasResizeObserver.unobserve(this.canvas),window.removeEventListener("mousemove",this.handleMouseMove),window.removeEventListener("scroll",this.handleScroll),this.canvas?.remove(),Object.keys(this).forEach(t=>delete this[t])}set options(t){const i=e.parseNumericOption;this.option={background:t.background??!1,animation:{startOnEnter:!!(t.animation?.startOnEnter??1),stopOnLeave:!!(t.animation?.stopOnLeave??1)},mouse:{interactionType:~~i("mouse.interactionType",t.mouse?.interactionType,e.interactionType.MOVE,{min:0,max:2}),connectDistMult:i("mouse.connectDistMult",t.mouse?.connectDistMult,2/3,{min:0}),connectDist:1,distRatio:i("mouse.distRatio",t.mouse?.distRatio,2/3,{min:0})},particles:{regenerateOnResize:!!t.particles?.regenerateOnResize,drawLines:!!(t.particles?.drawLines??1),color:t.particles?.color??"black",ppm:~~i("particles.ppm",t.particles?.ppm,100),max:Math.round(i("particles.max",t.particles?.max,1/0,{min:0})),maxWork:Math.round(i("particles.maxWork",t.particles?.maxWork,1/0,{min:0})),connectDist:~~i("particles.connectDistance",t.particles?.connectDistance,150,{min:1}),relSpeed:i("particles.relSpeed",t.particles?.relSpeed,1,{min:0}),relSize:i("particles.relSize",t.particles?.relSize,1,{min:0}),rotationSpeed:i("particles.rotationSpeed",t.particles?.rotationSpeed,2,{min:0})/100},gravity:{repulsive:i("gravity.repulsive",t.gravity?.repulsive,0,{min:0}),pulling:i("gravity.pulling",t.gravity?.pulling,0,{min:0}),friction:i("gravity.friction",t.gravity?.friction,.8,{min:0,max:1})}},this.setBackground(this.option.background),this.setMouseConnectDistMult(this.option.mouse.connectDistMult),this.setParticleColor(this.option.particles.color)}get options(){return this.option}setBackground(t){if(t){if("string"!=typeof t)throw new TypeError("background is not a string");this.canvas.style.background=this.option.background=t}}setMouseConnectDistMult(t){const i=e.parseNumericOption("mouse.connectDistMult",t,2/3,{min:0});this.option.mouse.connectDist=this.option.particles.connectDist*i}setParticleColor(t){if(this.ctx.fillStyle=t,"#"===String(this.ctx.fillStyle)[0])this.color={hex:String(this.ctx.fillStyle),alpha:1};else{let t=String(this.ctx.fillStyle).split(",").at(-1);t=t?.slice(1,-1)??"1",this.ctx.fillStyle=String(this.ctx.fillStyle).split(",").slice(0,-1).join(",")+", 1)",this.color={hex:String(this.ctx.fillStyle),alpha:isNaN(+t)?1:+t}}}}return e});
|
package/dist/types/index.d.ts
CHANGED
|
@@ -34,9 +34,10 @@ export interface ParticleBounds {
|
|
|
34
34
|
bottom: number;
|
|
35
35
|
left: number;
|
|
36
36
|
}
|
|
37
|
+
export type GridPos = 0 | 1 | 2;
|
|
37
38
|
export interface ParticleGridPos {
|
|
38
|
-
x:
|
|
39
|
-
y:
|
|
39
|
+
x: GridPos;
|
|
40
|
+
y: GridPos;
|
|
40
41
|
}
|
|
41
42
|
export interface ContextColor {
|
|
42
43
|
hex: string;
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Copyright (c) 2022–2025 Kyle Hoeckman, MIT License
|
|
2
2
|
// https://github.com/Khoeckman/canvasparticles-js/blob/main/LICENSE
|
|
3
3
|
|
|
4
|
-
import type { CanvasParticlesCanvas, Particle, ContextColor, LineSegment } from './types'
|
|
4
|
+
import type { CanvasParticlesCanvas, Particle, GridPos, ContextColor, LineSegment } from './types'
|
|
5
5
|
import type { CanvasParticlesOptions, CanvasParticlesOptionsInput } from './types/options'
|
|
6
6
|
|
|
7
7
|
const TWO_PI = 2 * Math.PI
|
|
@@ -42,19 +42,24 @@ export default class CanvasParticles {
|
|
|
42
42
|
})
|
|
43
43
|
|
|
44
44
|
/** Observes canvas elements entering or leaving the viewport to start/stop animation */
|
|
45
|
-
static readonly canvasIntersectionObserver = new IntersectionObserver(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
45
|
+
static readonly canvasIntersectionObserver = new IntersectionObserver(
|
|
46
|
+
(entries) => {
|
|
47
|
+
for (let i = 0; i < entries.length; i++) {
|
|
48
|
+
const entry = entries[i]
|
|
49
|
+
const canvas = entry.target as CanvasParticlesCanvas
|
|
50
|
+
const instance = canvas.instance // The CanvasParticles class instance bound to this canvas
|
|
51
|
+
|
|
52
|
+
if (!instance.options?.animation) return
|
|
53
|
+
|
|
54
|
+
if ((canvas.inViewbox = entry.isIntersecting))
|
|
55
|
+
instance.options.animation?.startOnEnter && instance.start({ auto: true })
|
|
56
|
+
else instance.options.animation?.stopOnLeave && instance.stop({ auto: true, clear: false })
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
rootMargin: '-1px',
|
|
56
61
|
}
|
|
57
|
-
|
|
62
|
+
)
|
|
58
63
|
|
|
59
64
|
static readonly canvasResizeObserver = new ResizeObserver((entries) => {
|
|
60
65
|
// Seperate for loops is very important to prevent huge forced reflow overhead
|
|
@@ -285,8 +290,8 @@ export default class CanvasParticles {
|
|
|
285
290
|
const gravRepulsiveMult = connectDist * this.option.gravity.repulsive * step
|
|
286
291
|
const gravPullingMult = connectDist * this.option.gravity.pulling * step
|
|
287
292
|
const maxRepulsiveDist = connectDist / 2
|
|
288
|
-
const maxRepulsiveDistSq = maxRepulsiveDist
|
|
289
|
-
const eps =
|
|
293
|
+
const maxRepulsiveDistSq = maxRepulsiveDist ** 2
|
|
294
|
+
const eps = connectDist ** 2 / 256
|
|
290
295
|
|
|
291
296
|
for (let i = 0; i < len; i++) {
|
|
292
297
|
const particleA = particles[i]
|
|
@@ -427,8 +432,8 @@ export default class CanvasParticles {
|
|
|
427
432
|
* - { x: 2, y: 2 } = bottom-right
|
|
428
433
|
*/
|
|
429
434
|
#gridPos(particle: Particle): void {
|
|
430
|
-
particle.gridPos.x = (+(particle.x >= particle.bounds.left) + +(particle.x > particle.bounds.right)) as
|
|
431
|
-
particle.gridPos.y = (+(particle.y >= particle.bounds.top) + +(particle.y > particle.bounds.bottom)) as
|
|
435
|
+
particle.gridPos.x = (+(particle.x >= particle.bounds.left) + +(particle.x > particle.bounds.right)) as GridPos
|
|
436
|
+
particle.gridPos.y = (+(particle.y >= particle.bounds.top) + +(particle.y > particle.bounds.bottom)) as GridPos
|
|
432
437
|
}
|
|
433
438
|
|
|
434
439
|
/** @private Determines whether a line between 2 particles crosses through the visible center of the canvas */
|
|
@@ -474,9 +479,9 @@ export default class CanvasParticles {
|
|
|
474
479
|
const particles = this.particles
|
|
475
480
|
const ctx = this.ctx
|
|
476
481
|
const maxDist = this.option.particles.connectDist
|
|
477
|
-
const maxDistSq = maxDist
|
|
482
|
+
const maxDistSq = maxDist ** 2
|
|
478
483
|
const halfMaxDist = maxDist / 2
|
|
479
|
-
const halfMaxDistSq = halfMaxDist
|
|
484
|
+
const halfMaxDistSq = halfMaxDist ** 2
|
|
480
485
|
const drawAll = maxDist >= Math.min(this.canvas.width, this.canvas.height)
|
|
481
486
|
const maxWorkPerParticle = maxDistSq * this.option.particles.maxWork
|
|
482
487
|
const alpha = this.color.alpha
|
|
@@ -628,16 +633,16 @@ export default class CanvasParticles {
|
|
|
628
633
|
CanvasParticles.interactionType.MOVE,
|
|
629
634
|
{ min: 0, max: 2 }
|
|
630
635
|
),
|
|
631
|
-
connectDistMult: pno('mouse.connectDistMult', options.mouse?.connectDistMult, 2 / 3),
|
|
636
|
+
connectDistMult: pno('mouse.connectDistMult', options.mouse?.connectDistMult, 2 / 3, { min: 0 }),
|
|
632
637
|
connectDist: 1 /* post processed */,
|
|
633
|
-
distRatio: pno('mouse.distRatio', options.mouse?.distRatio, 2 / 3),
|
|
638
|
+
distRatio: pno('mouse.distRatio', options.mouse?.distRatio, 2 / 3, { min: 0 }),
|
|
634
639
|
},
|
|
635
640
|
particles: {
|
|
636
641
|
regenerateOnResize: !!options.particles?.regenerateOnResize,
|
|
637
642
|
drawLines: !!(options.particles?.drawLines ?? true),
|
|
638
643
|
color: options.particles?.color ?? 'black',
|
|
639
644
|
ppm: ~~pno('particles.ppm', options.particles?.ppm, 100),
|
|
640
|
-
max: Math.round(pno('particles.max', options.particles?.max, Infinity)),
|
|
645
|
+
max: Math.round(pno('particles.max', options.particles?.max, Infinity, { min: 0 })),
|
|
641
646
|
maxWork: Math.round(pno('particles.maxWork', options.particles?.maxWork, Infinity, { min: 0 })),
|
|
642
647
|
connectDist: ~~pno('particles.connectDistance', options.particles?.connectDistance, 150, { min: 1 }),
|
|
643
648
|
relSpeed: pno('particles.relSpeed', options.particles?.relSpeed, 1, { min: 0 }),
|
|
@@ -645,8 +650,8 @@ export default class CanvasParticles {
|
|
|
645
650
|
rotationSpeed: pno('particles.rotationSpeed', options.particles?.rotationSpeed, 2, { min: 0 }) / 100,
|
|
646
651
|
},
|
|
647
652
|
gravity: {
|
|
648
|
-
repulsive: pno('gravity.repulsive', options.gravity?.repulsive, 0),
|
|
649
|
-
pulling: pno('gravity.pulling', options.gravity?.pulling, 0),
|
|
653
|
+
repulsive: pno('gravity.repulsive', options.gravity?.repulsive, 0, { min: 0 }),
|
|
654
|
+
pulling: pno('gravity.pulling', options.gravity?.pulling, 0, { min: 0 }),
|
|
650
655
|
friction: pno('gravity.friction', options.gravity?.friction, 0.8, { min: 0, max: 1 }),
|
|
651
656
|
},
|
|
652
657
|
}
|
|
@@ -669,8 +674,8 @@ export default class CanvasParticles {
|
|
|
669
674
|
|
|
670
675
|
/** @public Transform the distance multiplier (float) to absolute distance (px) */
|
|
671
676
|
setMouseConnectDistMult(connectDistMult: number) {
|
|
672
|
-
|
|
673
|
-
|
|
677
|
+
const mult = CanvasParticles.parseNumericOption('mouse.connectDistMult', connectDistMult, 2 / 3, { min: 0 })
|
|
678
|
+
this.option.mouse.connectDist = this.option.particles.connectDist * mult
|
|
674
679
|
}
|
|
675
680
|
|
|
676
681
|
/** @public Format particle color and opacity */
|
package/src/types/index.ts
CHANGED