particle-canvas-pro 1.0.0 → 1.1.0
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/LICENSE +1 -1
- package/README.md +110 -0
- package/dist/bundle.cjs.js +1 -1
- package/dist/bundle.esm.js +1 -1
- package/dist/bundle.js +1 -1
- package/dist/types/ParticleCanvas.d.ts +103 -0
- package/dist/types/index.d.ts +3 -106
- package/package.json +9 -7
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026 lebron_shi
|
|
3
|
+
Copyright (c) 2026 lebron_shi
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# particle-canvas-pro
|
|
2
|
+
|
|
3
|
+
`particle-canvas-pro` 是一款轻量高效的粒子动画库,基于 Canvas 的现代化高性能粒子动画系统,可无缝集成到各类web应用中作为背景或独立可视化元素,为你的网页带来沉浸式动态背景与视觉特效;
|
|
4
|
+
|
|
5
|
+
# 核心优势
|
|
6
|
+
|
|
7
|
+
✨ 高性能渲染:基于 `requestAnimationFrame` 实现帧同步动画,自动适配屏幕刷新率,兼顾流畅度与性能开销;
|
|
8
|
+
|
|
9
|
+
💡 智能资源调度:监听页面可见性变化,切换标签 / 最小化窗口时自动暂停动画、停止帧请求,避免后台无意义的 CPU/GPU 消耗;页面恢复可见时自动恢复动画状态,兼顾体验与资源节省,适配多标签页场景;
|
|
10
|
+
|
|
11
|
+
🎨 双模式适配:支持全屏背景模式(覆盖视口)和内嵌元素模式(自定义尺寸),适配不同业务场景,带来沉浸式体验;
|
|
12
|
+
|
|
13
|
+
🔧 多维度配置:粒子数量、速度、大小、颜色等参数均可自定义配置,并支持单色 / 多色粒子;
|
|
14
|
+
|
|
15
|
+
🔄 实时交互控制:支持即时启动、暂停和销毁等操作,且实现动态FPS监控;
|
|
16
|
+
|
|
17
|
+
🌈 炫酷视觉效果:内置轨迹效果、粒子脉动、连线交互等多种特效;
|
|
18
|
+
|
|
19
|
+
📱 响应式适配:全屏背景模式下,可以自动响应窗口大小变化,无需手动调整;
|
|
20
|
+
|
|
21
|
+
# 安装
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# 通过 npm 安装
|
|
25
|
+
npm i particle-canvas-pro -S
|
|
26
|
+
|
|
27
|
+
# 通过 yarn 安装
|
|
28
|
+
yarn add particle-canvas-pro
|
|
29
|
+
|
|
30
|
+
# 通过 CDN 引入
|
|
31
|
+
<script src="https://unpkg.com/particle-canvas-pro@latest/dist/bundle.js"></script>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
# 快速开始
|
|
35
|
+
|
|
36
|
+
只需几行代码即可接入粒子动效,轻松提升页面视觉层次感。
|
|
37
|
+
|
|
38
|
+
## 基础使用
|
|
39
|
+
|
|
40
|
+
### 全屏背景模式
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
import ParticleCanvas from 'particle-canvas-pro';
|
|
44
|
+
|
|
45
|
+
// 创建粒子系统实例
|
|
46
|
+
const particleSystem = new ParticleCanvas({
|
|
47
|
+
isBackground: true, // 全屏背景模式,默认模式
|
|
48
|
+
particleNumber: 150, // 粒子数量
|
|
49
|
+
particleColor: '#9c4aff', // 粒子颜色
|
|
50
|
+
lineColor: '#ffffff', // 连线颜色
|
|
51
|
+
trailValue: 0.2 // 轨迹效果
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// 启动动画
|
|
55
|
+
particleSystem.start();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 内嵌模式(自定义容器)
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
import ParticleCanvas from 'particle-canvas-pro';
|
|
62
|
+
|
|
63
|
+
// 在指定容器中创建粒子画布
|
|
64
|
+
const particleSystem = new ParticleCanvas({
|
|
65
|
+
canvasContainer: '#particle-container', // 容器选择器
|
|
66
|
+
isBackground: false, // 非背景模式
|
|
67
|
+
canvasSize: [800, 600], // 画布尺寸
|
|
68
|
+
particleNumber: 200,
|
|
69
|
+
showLinkLine: true,
|
|
70
|
+
linkDistance: 150
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// 启动动画
|
|
74
|
+
particleSystem.start();
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
# 参数配置
|
|
78
|
+
|
|
79
|
+
| 参数名 | 说明 | 类型 | 取值范围 | 默认值 |
|
|
80
|
+
|------------------------|----------------------|--------------------------|-----------------------------|---------------------------|
|
|
81
|
+
| canvasContainer | 画布容器元素或选择器 | `string` / `HTMLElement` | - | `document.body` |
|
|
82
|
+
| isBackground | 是否作为页面背景 | `boolean` | `true` / `false` | `true` |
|
|
83
|
+
| canvasBackgroundColor | 画布背景颜色 | `string` | - | `rgba(10, 10, 25, 1)` |
|
|
84
|
+
| particleNumber | 粒子数量 | `number` | 1-800 | 150 |
|
|
85
|
+
| particleSpeed | 粒子运动速度 | `number` | 0.1-3 | 1 |
|
|
86
|
+
| particleSize | 粒子大小(像素) | `number` | 1-10 | 5 |
|
|
87
|
+
| particleColor | 粒子颜色(单色或多色) | `string` / `string[]` | - | `rgba(156, 74, 255, 0.6)` |
|
|
88
|
+
| lineWidth | 连线宽度(像素) | `number` | 0.1-5 | 1 |
|
|
89
|
+
| lineColor | 连线颜色 | `string` | - | `#ffffff` |
|
|
90
|
+
| lineOpacity | 连线透明度 | `number` | 0-1 | 0.3 |
|
|
91
|
+
| linkDistance | 连线距离(像素) | `number` | 10-300 | 120 |
|
|
92
|
+
| showLinkLine | 是否显示连线 | `boolean` | `true` / `false` | `true` |
|
|
93
|
+
| canvasSize | 画布尺寸(非背景模式) | `[number, number]` | - | [800, 600] |
|
|
94
|
+
| trailValue | 轨迹效果强度 | `number` | 0-1 | 0.2 |
|
|
95
|
+
|
|
96
|
+
# 实例方法
|
|
97
|
+
|
|
98
|
+
| 方法名 | 说明 |
|
|
99
|
+
|-----------|--------------------------------------|
|
|
100
|
+
| start() | 启动粒子动画 |
|
|
101
|
+
| pause() | 暂停粒子动画 |
|
|
102
|
+
| reset() | 重置粒子位置和状态,重新启动动画 |
|
|
103
|
+
| destroy() | 销毁粒子动画,移除画布元素 |
|
|
104
|
+
|
|
105
|
+
# 构建配置
|
|
106
|
+
### 该粒子动画库支持多种模块格式:
|
|
107
|
+
|
|
108
|
+
- ES Module (dist/bundle.esm.js)
|
|
109
|
+
- CommonJS (dist/bundle.cjs.js)
|
|
110
|
+
- UMD (dist/bundle.js)
|
package/dist/bundle.cjs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";module.exports=class{constructor(i={}){this.MAX_PARTICLE_NUMBER=800,this.MAX_PARTICLE_SPEED=3,this.MAX_PARTICLE_SIZE=10,this.MAX_LINK_DISTANCE=300,this.MAX_LINE_WIDTH=5,this.MAX_LINE_OPACITY=1,this.MAX_TRAIL_VALUE=1,this.validateOptionsType(i);const t=this.validateAndLimitOptions(i);this.config={canvasContainer:t.canvasContainer||document.body,isBackground:void 0===t.isBackground||t.isBackground,canvasBackgroundColor:t.canvasBackgroundColor||"rgba(10, 10, 25, 1)",particleNumber:t.particleNumber||150,particleSpeed:t.particleSpeed||1,particleSize:t.particleSize||5,particleColor:t.particleColor||"rgba(156, 74, 255, 0.6)",lineWidth:t.lineWidth||1,lineColor:t.lineColor||"#ffffff",lineOpacity:t.lineOpacity||.3,linkDistance:t.linkDistance||120,showLinkLine:void 0===t.showLinkLine||t.showLinkLine,canvasSize:t.canvasSize||[800,600],trailValue:t.trailValue||.2},this.particles=[],this.animationId=null,this.isRunning=!1,this.lastTime=0,this.fps=60,this.fpsInterval=1e3/60,this.then=Date.now(),this.frameCount=0,this.lastFpsUpdate=Date.now(),this.init()}validateOptionsType(i){null!=i&&("object"!=typeof i||Array.isArray(i)?console.warn("Warning: options parameter must be an object. Using default configuration."):(void 0===i.canvasContainer||"string"==typeof i.canvasContainer||i.canvasContainer instanceof HTMLElement||console.warn("Warning: canvasContainer must be a string selector or HTMLElement. Using default value."),void 0!==i.isBackground&&"boolean"!=typeof i.isBackground&&console.warn("Warning: isBackground must be a boolean. Using default value."),void 0!==i.canvasBackgroundColor&&"string"!=typeof i.canvasBackgroundColor&&console.warn("Warning: canvasBackgroundColor must be a string. Using default value."),void 0!==i.particleNumber&&"number"!=typeof i.particleNumber&&console.warn("Warning: particleNumber must be a number. Using default value."),void 0!==i.particleSpeed&&"number"!=typeof i.particleSpeed&&console.warn("Warning: particleSpeed must be a number. Using default value."),void 0!==i.particleSize&&"number"!=typeof i.particleSize&&console.warn("Warning: particleSize must be a number. Using default value."),void 0===i.particleColor||"string"==typeof i.particleColor||Array.isArray(i.particleColor)||console.warn("Warning: particleColor must be a string or array of strings. Using default value."),void 0!==i.lineWidth&&"number"!=typeof i.lineWidth&&console.warn("Warning: lineWidth must be a number. Using default value."),void 0!==i.lineColor&&"string"!=typeof i.lineColor&&console.warn("Warning: lineColor must be a string. Using default value."),void 0!==i.lineOpacity&&"number"!=typeof i.lineOpacity&&console.warn("Warning: lineOpacity must be a number. Using default value."),void 0!==i.linkDistance&&"number"!=typeof i.linkDistance&&console.warn("Warning: linkDistance must be a number. Using default value."),void 0!==i.showLinkLine&&"boolean"!=typeof i.showLinkLine&&console.warn("Warning: showLinkLine must be a boolean. Using default value."),void 0===i.canvasSize||Array.isArray(i.canvasSize)&&2===i.canvasSize.length&&"number"==typeof i.canvasSize[0]&&"number"==typeof i.canvasSize[1]||console.warn("Warning: canvasSize must be an array of two numbers [width, height]. Using default value."),void 0!==i.trailValue&&"number"!=typeof i.trailValue&&console.warn("Warning: trailValue must be a number. Using default value.")))}validateAndLimitOptions(i){const t=Object.assign({},i);return void 0!==i.particleNumber&&(i.particleNumber>this.MAX_PARTICLE_NUMBER?(console.warn(`Warning: particleNumber (${i.particleNumber}) exceeds maximum allowed value (${this.MAX_PARTICLE_NUMBER}). Using maximum value instead.`),t.particleNumber=this.MAX_PARTICLE_NUMBER):i.particleNumber<1&&(console.warn(`Warning: particleNumber (${i.particleNumber}) is below minimum value (1). Using minimum value instead.`),t.particleNumber=1)),void 0!==i.particleSpeed&&(i.particleSpeed>this.MAX_PARTICLE_SPEED?(console.warn(`Warning: particleSpeed (${i.particleSpeed}) exceeds maximum allowed value (${this.MAX_PARTICLE_SPEED}). Using maximum value instead.`),t.particleSpeed=this.MAX_PARTICLE_SPEED):i.particleSpeed<.1&&(console.warn(`Warning: particleSpeed (${i.particleSpeed}) is below minimum value (0.1). Using minimum value instead.`),t.particleSpeed=.1)),void 0!==i.particleSize&&(i.particleSize>this.MAX_PARTICLE_SIZE?(console.warn(`Warning: particleSize (${i.particleSize}) exceeds maximum allowed value (${this.MAX_PARTICLE_SIZE}). Using maximum value instead.`),t.particleSize=this.MAX_PARTICLE_SIZE):i.particleSize<1&&(console.warn(`Warning: particleSize (${i.particleSize}) is below minimum value (1). Using minimum value instead.`),t.particleSize=1)),void 0!==i.linkDistance&&(i.linkDistance>this.MAX_LINK_DISTANCE?(console.warn(`Warning: linkDistance (${i.linkDistance}) exceeds maximum allowed value (${this.MAX_LINK_DISTANCE}). Using maximum value instead.`),t.linkDistance=this.MAX_LINK_DISTANCE):i.linkDistance<10&&(console.warn(`Warning: linkDistance (${i.linkDistance}) is below minimum value (10). Using minimum value instead.`),t.linkDistance=10)),void 0!==i.lineWidth&&(i.lineWidth>this.MAX_LINE_WIDTH?(console.warn(`Warning: lineWidth (${i.lineWidth}) exceeds maximum allowed value (${this.MAX_LINE_WIDTH}). Using maximum value instead.`),t.lineWidth=this.MAX_LINE_WIDTH):i.lineWidth<.1&&(console.warn(`Warning: lineWidth (${i.lineWidth}) is below minimum value (0.1). Using minimum value instead.`),t.lineWidth=.1)),void 0!==i.lineOpacity&&(i.lineOpacity>this.MAX_LINE_OPACITY?(console.warn(`Warning: lineOpacity (${i.lineOpacity}) exceeds maximum allowed value (${this.MAX_LINE_OPACITY}). Using maximum value instead.`),t.lineOpacity=this.MAX_LINE_OPACITY):i.lineOpacity<0&&(console.warn(`Warning: lineOpacity (${i.lineOpacity}) is below minimum value (0). Using minimum value instead.`),t.lineOpacity=0)),void 0!==i.trailValue&&(i.trailValue>this.MAX_TRAIL_VALUE?(console.warn(`Warning: trailValue (${i.trailValue}) exceeds maximum allowed value (${this.MAX_TRAIL_VALUE}). Using maximum value instead.`),t.trailValue=this.MAX_TRAIL_VALUE):i.trailValue<0&&(console.warn(`Warning: trailValue (${i.trailValue}) is below minimum value (0). Using minimum value instead.`),t.trailValue=0)),t}init(){this.canvas=document.createElement("canvas");const i=this.canvas.getContext("2d");if(!i)throw new Error("Could not get 2D context from canvas");if(this.ctx=i,"string"==typeof this.config.canvasContainer){const i=document.querySelector(this.config.canvasContainer);if(!i)throw new Error(`Canvas container not found: ${this.config.canvasContainer}`);this.container=i}else this.container=this.config.canvasContainer;this.container?(this.container.appendChild(this.canvas),this.setupCanvas(),this.createParticles(),this.bindEvents()):console.error("Canvas container not found")}setupCanvas(){if(this.config.isBackground)this.canvas.style.position="fixed",this.canvas.style.top="0",this.canvas.style.left="0",this.canvas.style.width="100%",this.canvas.style.height="100%",this.canvas.style.zIndex="-1",this.canvas.width=window.innerWidth,this.canvas.height=window.innerHeight;else{this.canvas.style.display="block";const[i,t]=this.config.canvasSize;this.canvas.width=i,this.canvas.height=t,this.canvas.style.width=i+"px",this.canvas.style.height=t+"px"}this.canvas.style.pointerEvents="none"}createParticles(){this.particles=[];const i=Array.isArray(this.config.particleColor);for(let t=0;t<this.config.particleNumber;t++){let t;if(i){const i=this.config.particleColor;t=i[Math.floor(Math.random()*i.length)]}else t=this.config.particleColor;this.particles.push({x:Math.random()*this.canvas.width,y:Math.random()*this.canvas.height,vx:2*(Math.random()-.5)*this.config.particleSpeed,vy:2*(Math.random()-.5)*this.config.particleSpeed,size:this.config.particleSize,color:t,originalColor:t,pulse:0,pulseSpeed:.05*Math.random()+.02})}}updateParticles(){for(const i of this.particles)i.pulse+=i.pulseSpeed,i.pulse>2*Math.PI&&(i.pulse=0),i.x+=i.vx,i.y+=i.vy,(i.x<0||i.x>this.canvas.width)&&(i.vx*=-1),(i.y<0||i.y>this.canvas.height)&&(i.vy*=-1),i.x=Math.max(0,Math.min(this.canvas.width,i.x)),i.y=Math.max(0,Math.min(this.canvas.height,i.y))}drawParticles(){if(this.config.trailValue>0){let i=this.config.canvasBackgroundColor;if(i.startsWith("#")){const t=parseInt(i.slice(1,3),16),e=parseInt(i.slice(3,5),16),a=parseInt(i.slice(5,7),16);i=`rgba(${t}, ${e}, ${a}, ${this.config.trailValue})`}else if(i.startsWith("rgb("))i=i.replace("rgb(","rgba(").replace(")",`, ${this.config.trailValue})`);else if(i.startsWith("rgba(")){const t=i.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/);if(t){const e=parseFloat(t[4])*this.config.trailValue;i=`rgba(${t[1]}, ${t[2]}, ${t[3]}, ${e})`}}this.ctx.fillStyle=i,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height)}else this.ctx.fillStyle=this.config.canvasBackgroundColor,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height);if(this.config.showLinkLine)for(let i=0;i<this.particles.length;i++)for(let t=i+1;t<this.particles.length;t++){const e=this.particles[i].x-this.particles[t].x,a=this.particles[i].y-this.particles[t].y,n=Math.sqrt(e*e+a*a);if(n<this.config.linkDistance){const e=this.config.lineOpacity*(1-n/this.config.linkDistance);this.ctx.strokeStyle=this.config.lineColor,this.ctx.globalAlpha=e,this.ctx.lineWidth=this.config.lineWidth,this.ctx.beginPath(),this.ctx.moveTo(this.particles[i].x,this.particles[i].y),this.ctx.lineTo(this.particles[t].x,this.particles[t].y),this.ctx.stroke()}}this.ctx.globalAlpha=1;for(const i of this.particles){const t=i.size*(1+.2*Math.sin(i.pulse));this.ctx.shadowColor=i.color,this.ctx.shadowBlur=10,this.ctx.beginPath(),this.ctx.arc(i.x,i.y,t,0,2*Math.PI),this.ctx.fillStyle=i.color,this.ctx.fill(),this.ctx.shadowBlur=0}}updateFPS(){this.frameCount++;const i=Date.now(),t=i-this.lastFpsUpdate;t>=1e3&&(this.fps=Math.round(1e3*this.frameCount/t),this.frameCount=0,this.lastFpsUpdate=i)}animate(){if(!this.isRunning)return;const i=Date.now(),t=i-this.then;t>this.fpsInterval&&(this.then=i-t%this.fpsInterval,this.updateParticles(),this.drawParticles(),this.updateFPS()),this.animationId=requestAnimationFrame(()=>this.animate())}start(){this.isRunning||(this.isRunning=!0,this.then=Date.now(),this.animate())}pause(){this.isRunning=!1,this.animationId&&(cancelAnimationFrame(this.animationId),this.animationId=null)}reset(){this.pause(),this.createParticles(),this.start()}resize(){this.config.isBackground&&(this.canvas.width=window.innerWidth,this.canvas.height=window.innerHeight),this.reset()}destroy(){this.pause(),this.canvas&&this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas)}bindEvents(){window.addEventListener("resize",()=>{this.config.isBackground&&this.resize()}),this.canvas.addEventListener("mousemove",i=>{const t=this.canvas.getBoundingClientRect(),e=i.clientX-t.left,a=i.clientY-t.top;for(const i of this.particles){const t=i.x-e,n=i.y-a,s=Math.sqrt(t*t+n*n);if(s<100){const e=(100-s)/100;i.vx+=.01*t*e,i.vy+=.01*n*e}}})}};
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});class i{constructor(i={}){this.MAX_PARTICLE_NUMBER=800,this.MAX_PARTICLE_SPEED=3,this.MAX_PARTICLE_SIZE=10,this.MAX_LINK_DISTANCE=300,this.MAX_LINE_WIDTH=5,this.MAX_LINE_OPACITY=1,this.MAX_TRAIL_VALUE=1;const t=this.validateAndLimitOptions(i);this.config={canvasContainer:t.canvasContainer||document.body,isBackground:void 0===t.isBackground||t.isBackground,canvasBackgroundColor:t.canvasBackgroundColor||"rgba(10, 10, 25, 1)",particleNumber:t.particleNumber||150,particleSpeed:t.particleSpeed||1,particleSize:t.particleSize||5,particleColor:t.particleColor||"rgba(156, 74, 255, 0.6)",lineWidth:t.lineWidth||1,lineColor:t.lineColor||"#ffffff",lineOpacity:t.lineOpacity||.3,linkDistance:t.linkDistance||120,showLinkLine:void 0===t.showLinkLine||t.showLinkLine,canvasSize:t.canvasSize||[800,600],trailValue:t.trailValue||.2},this.particles=[],this.animationId=null,this.isRunning=!1,this.lastTime=0,this.fps=60,this.fpsInterval=1e3/60,this.then=Date.now(),this.frameCount=0,this.lastFpsUpdate=Date.now(),this.resizeTimer=null,this.wasRunningBeforeHide=!1,this.init()}validateAndLimitOptions(i){const t=Object.assign({},i);return void 0!==i.particleNumber&&(i.particleNumber>this.MAX_PARTICLE_NUMBER?(console.warn(`Warning: particleNumber (${i.particleNumber}) exceeds maximum allowed value (${this.MAX_PARTICLE_NUMBER}). Using maximum value instead.`),t.particleNumber=this.MAX_PARTICLE_NUMBER):i.particleNumber<1&&(console.warn(`Warning: particleNumber (${i.particleNumber}) is below minimum value (1). Using minimum value instead.`),t.particleNumber=1)),void 0!==i.particleSpeed&&(i.particleSpeed>this.MAX_PARTICLE_SPEED?(console.warn(`Warning: particleSpeed (${i.particleSpeed}) exceeds maximum allowed value (${this.MAX_PARTICLE_SPEED}). Using maximum value instead.`),t.particleSpeed=this.MAX_PARTICLE_SPEED):i.particleSpeed<.1&&(console.warn(`Warning: particleSpeed (${i.particleSpeed}) is below minimum value (0.1). Using minimum value instead.`),t.particleSpeed=.1)),void 0!==i.particleSize&&(i.particleSize>this.MAX_PARTICLE_SIZE?(console.warn(`Warning: particleSize (${i.particleSize}) exceeds maximum allowed value (${this.MAX_PARTICLE_SIZE}). Using maximum value instead.`),t.particleSize=this.MAX_PARTICLE_SIZE):i.particleSize<1&&(console.warn(`Warning: particleSize (${i.particleSize}) is below minimum value (1). Using minimum value instead.`),t.particleSize=1)),void 0!==i.linkDistance&&(i.linkDistance>this.MAX_LINK_DISTANCE?(console.warn(`Warning: linkDistance (${i.linkDistance}) exceeds maximum allowed value (${this.MAX_LINK_DISTANCE}). Using maximum value instead.`),t.linkDistance=this.MAX_LINK_DISTANCE):i.linkDistance<10&&(console.warn(`Warning: linkDistance (${i.linkDistance}) is below minimum value (10). Using minimum value instead.`),t.linkDistance=10)),void 0!==i.lineWidth&&(i.lineWidth>this.MAX_LINE_WIDTH?(console.warn(`Warning: lineWidth (${i.lineWidth}) exceeds maximum allowed value (${this.MAX_LINE_WIDTH}). Using maximum value instead.`),t.lineWidth=this.MAX_LINE_WIDTH):i.lineWidth<.1&&(console.warn(`Warning: lineWidth (${i.lineWidth}) is below minimum value (0.1). Using minimum value instead.`),t.lineWidth=.1)),void 0!==i.lineOpacity&&(i.lineOpacity>this.MAX_LINE_OPACITY?(console.warn(`Warning: lineOpacity (${i.lineOpacity}) exceeds maximum allowed value (${this.MAX_LINE_OPACITY}). Using maximum value instead.`),t.lineOpacity=this.MAX_LINE_OPACITY):i.lineOpacity<0&&(console.warn(`Warning: lineOpacity (${i.lineOpacity}) is below minimum value (0). Using minimum value instead.`),t.lineOpacity=0)),void 0!==i.trailValue&&(i.trailValue>this.MAX_TRAIL_VALUE?(console.warn(`Warning: trailValue (${i.trailValue}) exceeds maximum allowed value (${this.MAX_TRAIL_VALUE}). Using maximum value instead.`),t.trailValue=this.MAX_TRAIL_VALUE):i.trailValue<0&&(console.warn(`Warning: trailValue (${i.trailValue}) is below minimum value (0). Using minimum value instead.`),t.trailValue=0)),t}init(){this.canvas=document.createElement("canvas");const i=this.canvas.getContext("2d");if(!i)throw new Error("Could not get 2D context from canvas");if(this.ctx=i,"string"==typeof this.config.canvasContainer){const i=document.querySelector(this.config.canvasContainer);if(!i)throw new Error(`Canvas container not found: ${this.config.canvasContainer}`);this.container=i}else this.container=this.config.canvasContainer;this.container?(this.container.appendChild(this.canvas),this.setupCanvas(),this.createParticles(),this.bindEvents()):console.error("Canvas container not found")}setupCanvas(){if(this.config.isBackground)this.canvas.style.position="fixed",this.canvas.style.top="0",this.canvas.style.left="0",this.canvas.style.width="100%",this.canvas.style.height="100%",this.canvas.style.zIndex="-1",this.canvas.width=window.innerWidth,this.canvas.height=window.innerHeight;else{this.canvas.style.display="block";const[i,t]=this.config.canvasSize;this.canvas.width=i,this.canvas.height=t,this.canvas.style.width=i+"px",this.canvas.style.height=t+"px"}this.canvas.style.pointerEvents="none"}createParticles(){this.particles=[];const i=Array.isArray(this.config.particleColor);for(let t=0;t<this.config.particleNumber;t++){let t;if(i){const i=this.config.particleColor;t=i[Math.floor(Math.random()*i.length)]}else t=this.config.particleColor;this.particles.push({x:Math.random()*this.canvas.width,y:Math.random()*this.canvas.height,vx:2*(Math.random()-.5)*this.config.particleSpeed,vy:2*(Math.random()-.5)*this.config.particleSpeed,size:this.config.particleSize,color:t,originalColor:t,pulse:0,pulseSpeed:.05*Math.random()+.02})}}updateParticles(){for(const i of this.particles)i.pulse+=i.pulseSpeed,i.pulse>2*Math.PI&&(i.pulse=0),i.x+=i.vx,i.y+=i.vy,(i.x<0||i.x>this.canvas.width)&&(i.vx*=-1),(i.y<0||i.y>this.canvas.height)&&(i.vy*=-1),i.x=Math.max(0,Math.min(this.canvas.width,i.x)),i.y=Math.max(0,Math.min(this.canvas.height,i.y))}drawParticles(){if(this.config.trailValue>0){let i=this.config.canvasBackgroundColor;if(i.startsWith("#")){const t=parseInt(i.slice(1,3),16),e=parseInt(i.slice(3,5),16),s=parseInt(i.slice(5,7),16);i=`rgba(${t}, ${e}, ${s}, ${this.config.trailValue})`}else if(i.startsWith("rgb("))i=i.replace("rgb(","rgba(").replace(")",`, ${this.config.trailValue})`);else if(i.startsWith("rgba(")){const t=i.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/);if(t){const e=parseFloat(t[4])*this.config.trailValue;i=`rgba(${t[1]}, ${t[2]}, ${t[3]}, ${e})`}}this.ctx.fillStyle=i,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height)}else this.ctx.fillStyle=this.config.canvasBackgroundColor,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height);if(this.config.showLinkLine)for(let i=0;i<this.particles.length;i++)for(let t=i+1;t<this.particles.length;t++){const e=this.particles[i].x-this.particles[t].x,s=this.particles[i].y-this.particles[t].y,a=Math.sqrt(e*e+s*s);if(a<this.config.linkDistance){const e=this.config.lineOpacity*(1-a/this.config.linkDistance);this.ctx.strokeStyle=this.config.lineColor,this.ctx.globalAlpha=e,this.ctx.lineWidth=this.config.lineWidth,this.ctx.beginPath(),this.ctx.moveTo(this.particles[i].x,this.particles[i].y),this.ctx.lineTo(this.particles[t].x,this.particles[t].y),this.ctx.stroke()}}this.ctx.globalAlpha=1;for(const i of this.particles){const t=i.size*(1+.2*Math.sin(i.pulse));this.ctx.shadowColor=i.color,this.ctx.shadowBlur=10,this.ctx.beginPath(),this.ctx.arc(i.x,i.y,t,0,2*Math.PI),this.ctx.fillStyle=i.color,this.ctx.fill(),this.ctx.shadowBlur=0}}updateFPS(){this.frameCount++;const i=Date.now(),t=i-this.lastFpsUpdate;t>=1e3&&(this.fps=Math.round(1e3*this.frameCount/t),this.frameCount=0,this.lastFpsUpdate=i)}animate(){if(!this.isRunning)return;const i=Date.now(),t=i-this.then;t>this.fpsInterval&&(this.then=i-t%this.fpsInterval,this.updateParticles(),this.drawParticles(),this.updateFPS()),this.animationId=requestAnimationFrame(()=>this.animate())}start(){this.isRunning||(this.isRunning=!0,this.then=Date.now(),this.animate())}pause(){this.isRunning=!1,this.animationId&&(cancelAnimationFrame(this.animationId),this.animationId=null)}reset(){const i=this.isRunning;i&&this.pause(),this.createParticles(),i?this.start():this.drawParticles()}resize(){if(this.config.isBackground){const i=this.canvas.width,t=this.canvas.height,e=window.innerWidth,s=window.innerHeight;if(this.canvas.width=e,this.canvas.height=s,this.particles.length>0){const a=e/i,n=s/t;for(const i of this.particles)i.x*=a,i.y*=n,i.x=Math.max(0,Math.min(e,i.x)),i.y=Math.max(0,Math.min(s,i.y)),(i.x<=0||i.x>=e)&&(i.vx*=-1),(i.y<=0||i.y>=s)&&(i.vy*=-1);this.drawParticles()}else this.createParticles(),this.drawParticles()}}destroy(){this.pause(),this.canvas&&this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas)}bindEvents(){window.addEventListener("resize",()=>{this.config.isBackground&&(this.resizeTimer&&clearTimeout(this.resizeTimer),this.resizeTimer=window.setTimeout(()=>{this.resize()},150))}),document.addEventListener("visibilitychange",()=>{document.hidden?(this.wasRunningBeforeHide=this.isRunning,this.isRunning&&this.pause()):(this.wasRunningBeforeHide&&this.start(),this.wasRunningBeforeHide=!1)})}}exports.ParticleCanvas=i,exports.default=i;
|
package/dist/bundle.esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
class i{constructor(i={}){this.MAX_PARTICLE_NUMBER=800,this.MAX_PARTICLE_SPEED=3,this.MAX_PARTICLE_SIZE=10,this.MAX_LINK_DISTANCE=300,this.MAX_LINE_WIDTH=5,this.MAX_LINE_OPACITY=1,this.MAX_TRAIL_VALUE=1,this.validateOptionsType(i);const t=this.validateAndLimitOptions(i);this.config={canvasContainer:t.canvasContainer||document.body,isBackground:void 0===t.isBackground||t.isBackground,canvasBackgroundColor:t.canvasBackgroundColor||"rgba(10, 10, 25, 1)",particleNumber:t.particleNumber||150,particleSpeed:t.particleSpeed||1,particleSize:t.particleSize||5,particleColor:t.particleColor||"rgba(156, 74, 255, 0.6)",lineWidth:t.lineWidth||1,lineColor:t.lineColor||"#ffffff",lineOpacity:t.lineOpacity||.3,linkDistance:t.linkDistance||120,showLinkLine:void 0===t.showLinkLine||t.showLinkLine,canvasSize:t.canvasSize||[800,600],trailValue:t.trailValue||.2},this.particles=[],this.animationId=null,this.isRunning=!1,this.lastTime=0,this.fps=60,this.fpsInterval=1e3/60,this.then=Date.now(),this.frameCount=0,this.lastFpsUpdate=Date.now(),this.init()}validateOptionsType(i){null!=i&&("object"!=typeof i||Array.isArray(i)?console.warn("Warning: options parameter must be an object. Using default configuration."):(void 0===i.canvasContainer||"string"==typeof i.canvasContainer||i.canvasContainer instanceof HTMLElement||console.warn("Warning: canvasContainer must be a string selector or HTMLElement. Using default value."),void 0!==i.isBackground&&"boolean"!=typeof i.isBackground&&console.warn("Warning: isBackground must be a boolean. Using default value."),void 0!==i.canvasBackgroundColor&&"string"!=typeof i.canvasBackgroundColor&&console.warn("Warning: canvasBackgroundColor must be a string. Using default value."),void 0!==i.particleNumber&&"number"!=typeof i.particleNumber&&console.warn("Warning: particleNumber must be a number. Using default value."),void 0!==i.particleSpeed&&"number"!=typeof i.particleSpeed&&console.warn("Warning: particleSpeed must be a number. Using default value."),void 0!==i.particleSize&&"number"!=typeof i.particleSize&&console.warn("Warning: particleSize must be a number. Using default value."),void 0===i.particleColor||"string"==typeof i.particleColor||Array.isArray(i.particleColor)||console.warn("Warning: particleColor must be a string or array of strings. Using default value."),void 0!==i.lineWidth&&"number"!=typeof i.lineWidth&&console.warn("Warning: lineWidth must be a number. Using default value."),void 0!==i.lineColor&&"string"!=typeof i.lineColor&&console.warn("Warning: lineColor must be a string. Using default value."),void 0!==i.lineOpacity&&"number"!=typeof i.lineOpacity&&console.warn("Warning: lineOpacity must be a number. Using default value."),void 0!==i.linkDistance&&"number"!=typeof i.linkDistance&&console.warn("Warning: linkDistance must be a number. Using default value."),void 0!==i.showLinkLine&&"boolean"!=typeof i.showLinkLine&&console.warn("Warning: showLinkLine must be a boolean. Using default value."),void 0===i.canvasSize||Array.isArray(i.canvasSize)&&2===i.canvasSize.length&&"number"==typeof i.canvasSize[0]&&"number"==typeof i.canvasSize[1]||console.warn("Warning: canvasSize must be an array of two numbers [width, height]. Using default value."),void 0!==i.trailValue&&"number"!=typeof i.trailValue&&console.warn("Warning: trailValue must be a number. Using default value.")))}validateAndLimitOptions(i){const t=Object.assign({},i);return void 0!==i.particleNumber&&(i.particleNumber>this.MAX_PARTICLE_NUMBER?(console.warn(`Warning: particleNumber (${i.particleNumber}) exceeds maximum allowed value (${this.MAX_PARTICLE_NUMBER}). Using maximum value instead.`),t.particleNumber=this.MAX_PARTICLE_NUMBER):i.particleNumber<1&&(console.warn(`Warning: particleNumber (${i.particleNumber}) is below minimum value (1). Using minimum value instead.`),t.particleNumber=1)),void 0!==i.particleSpeed&&(i.particleSpeed>this.MAX_PARTICLE_SPEED?(console.warn(`Warning: particleSpeed (${i.particleSpeed}) exceeds maximum allowed value (${this.MAX_PARTICLE_SPEED}). Using maximum value instead.`),t.particleSpeed=this.MAX_PARTICLE_SPEED):i.particleSpeed<.1&&(console.warn(`Warning: particleSpeed (${i.particleSpeed}) is below minimum value (0.1). Using minimum value instead.`),t.particleSpeed=.1)),void 0!==i.particleSize&&(i.particleSize>this.MAX_PARTICLE_SIZE?(console.warn(`Warning: particleSize (${i.particleSize}) exceeds maximum allowed value (${this.MAX_PARTICLE_SIZE}). Using maximum value instead.`),t.particleSize=this.MAX_PARTICLE_SIZE):i.particleSize<1&&(console.warn(`Warning: particleSize (${i.particleSize}) is below minimum value (1). Using minimum value instead.`),t.particleSize=1)),void 0!==i.linkDistance&&(i.linkDistance>this.MAX_LINK_DISTANCE?(console.warn(`Warning: linkDistance (${i.linkDistance}) exceeds maximum allowed value (${this.MAX_LINK_DISTANCE}). Using maximum value instead.`),t.linkDistance=this.MAX_LINK_DISTANCE):i.linkDistance<10&&(console.warn(`Warning: linkDistance (${i.linkDistance}) is below minimum value (10). Using minimum value instead.`),t.linkDistance=10)),void 0!==i.lineWidth&&(i.lineWidth>this.MAX_LINE_WIDTH?(console.warn(`Warning: lineWidth (${i.lineWidth}) exceeds maximum allowed value (${this.MAX_LINE_WIDTH}). Using maximum value instead.`),t.lineWidth=this.MAX_LINE_WIDTH):i.lineWidth<.1&&(console.warn(`Warning: lineWidth (${i.lineWidth}) is below minimum value (0.1). Using minimum value instead.`),t.lineWidth=.1)),void 0!==i.lineOpacity&&(i.lineOpacity>this.MAX_LINE_OPACITY?(console.warn(`Warning: lineOpacity (${i.lineOpacity}) exceeds maximum allowed value (${this.MAX_LINE_OPACITY}). Using maximum value instead.`),t.lineOpacity=this.MAX_LINE_OPACITY):i.lineOpacity<0&&(console.warn(`Warning: lineOpacity (${i.lineOpacity}) is below minimum value (0). Using minimum value instead.`),t.lineOpacity=0)),void 0!==i.trailValue&&(i.trailValue>this.MAX_TRAIL_VALUE?(console.warn(`Warning: trailValue (${i.trailValue}) exceeds maximum allowed value (${this.MAX_TRAIL_VALUE}). Using maximum value instead.`),t.trailValue=this.MAX_TRAIL_VALUE):i.trailValue<0&&(console.warn(`Warning: trailValue (${i.trailValue}) is below minimum value (0). Using minimum value instead.`),t.trailValue=0)),t}init(){this.canvas=document.createElement("canvas");const i=this.canvas.getContext("2d");if(!i)throw new Error("Could not get 2D context from canvas");if(this.ctx=i,"string"==typeof this.config.canvasContainer){const i=document.querySelector(this.config.canvasContainer);if(!i)throw new Error(`Canvas container not found: ${this.config.canvasContainer}`);this.container=i}else this.container=this.config.canvasContainer;this.container?(this.container.appendChild(this.canvas),this.setupCanvas(),this.createParticles(),this.bindEvents()):console.error("Canvas container not found")}setupCanvas(){if(this.config.isBackground)this.canvas.style.position="fixed",this.canvas.style.top="0",this.canvas.style.left="0",this.canvas.style.width="100%",this.canvas.style.height="100%",this.canvas.style.zIndex="-1",this.canvas.width=window.innerWidth,this.canvas.height=window.innerHeight;else{this.canvas.style.display="block";const[i,t]=this.config.canvasSize;this.canvas.width=i,this.canvas.height=t,this.canvas.style.width=i+"px",this.canvas.style.height=t+"px"}this.canvas.style.pointerEvents="none"}createParticles(){this.particles=[];const i=Array.isArray(this.config.particleColor);for(let t=0;t<this.config.particleNumber;t++){let t;if(i){const i=this.config.particleColor;t=i[Math.floor(Math.random()*i.length)]}else t=this.config.particleColor;this.particles.push({x:Math.random()*this.canvas.width,y:Math.random()*this.canvas.height,vx:2*(Math.random()-.5)*this.config.particleSpeed,vy:2*(Math.random()-.5)*this.config.particleSpeed,size:this.config.particleSize,color:t,originalColor:t,pulse:0,pulseSpeed:.05*Math.random()+.02})}}updateParticles(){for(const i of this.particles)i.pulse+=i.pulseSpeed,i.pulse>2*Math.PI&&(i.pulse=0),i.x+=i.vx,i.y+=i.vy,(i.x<0||i.x>this.canvas.width)&&(i.vx*=-1),(i.y<0||i.y>this.canvas.height)&&(i.vy*=-1),i.x=Math.max(0,Math.min(this.canvas.width,i.x)),i.y=Math.max(0,Math.min(this.canvas.height,i.y))}drawParticles(){if(this.config.trailValue>0){let i=this.config.canvasBackgroundColor;if(i.startsWith("#")){const t=parseInt(i.slice(1,3),16),e=parseInt(i.slice(3,5),16),a=parseInt(i.slice(5,7),16);i=`rgba(${t}, ${e}, ${a}, ${this.config.trailValue})`}else if(i.startsWith("rgb("))i=i.replace("rgb(","rgba(").replace(")",`, ${this.config.trailValue})`);else if(i.startsWith("rgba(")){const t=i.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/);if(t){const e=parseFloat(t[4])*this.config.trailValue;i=`rgba(${t[1]}, ${t[2]}, ${t[3]}, ${e})`}}this.ctx.fillStyle=i,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height)}else this.ctx.fillStyle=this.config.canvasBackgroundColor,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height);if(this.config.showLinkLine)for(let i=0;i<this.particles.length;i++)for(let t=i+1;t<this.particles.length;t++){const e=this.particles[i].x-this.particles[t].x,a=this.particles[i].y-this.particles[t].y,n=Math.sqrt(e*e+a*a);if(n<this.config.linkDistance){const e=this.config.lineOpacity*(1-n/this.config.linkDistance);this.ctx.strokeStyle=this.config.lineColor,this.ctx.globalAlpha=e,this.ctx.lineWidth=this.config.lineWidth,this.ctx.beginPath(),this.ctx.moveTo(this.particles[i].x,this.particles[i].y),this.ctx.lineTo(this.particles[t].x,this.particles[t].y),this.ctx.stroke()}}this.ctx.globalAlpha=1;for(const i of this.particles){const t=i.size*(1+.2*Math.sin(i.pulse));this.ctx.shadowColor=i.color,this.ctx.shadowBlur=10,this.ctx.beginPath(),this.ctx.arc(i.x,i.y,t,0,2*Math.PI),this.ctx.fillStyle=i.color,this.ctx.fill(),this.ctx.shadowBlur=0}}updateFPS(){this.frameCount++;const i=Date.now(),t=i-this.lastFpsUpdate;t>=1e3&&(this.fps=Math.round(1e3*this.frameCount/t),this.frameCount=0,this.lastFpsUpdate=i)}animate(){if(!this.isRunning)return;const i=Date.now(),t=i-this.then;t>this.fpsInterval&&(this.then=i-t%this.fpsInterval,this.updateParticles(),this.drawParticles(),this.updateFPS()),this.animationId=requestAnimationFrame(()=>this.animate())}start(){this.isRunning||(this.isRunning=!0,this.then=Date.now(),this.animate())}pause(){this.isRunning=!1,this.animationId&&(cancelAnimationFrame(this.animationId),this.animationId=null)}reset(){this.pause(),this.createParticles(),this.start()}resize(){this.config.isBackground&&(this.canvas.width=window.innerWidth,this.canvas.height=window.innerHeight),this.reset()}destroy(){this.pause(),this.canvas&&this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas)}bindEvents(){window.addEventListener("resize",()=>{this.config.isBackground&&this.resize()}),this.canvas.addEventListener("mousemove",i=>{const t=this.canvas.getBoundingClientRect(),e=i.clientX-t.left,a=i.clientY-t.top;for(const i of this.particles){const t=i.x-e,n=i.y-a,s=Math.sqrt(t*t+n*n);if(s<100){const e=(100-s)/100;i.vx+=.01*t*e,i.vy+=.01*n*e}}})}}export{i as default};
|
|
1
|
+
class i{constructor(i={}){this.MAX_PARTICLE_NUMBER=800,this.MAX_PARTICLE_SPEED=3,this.MAX_PARTICLE_SIZE=10,this.MAX_LINK_DISTANCE=300,this.MAX_LINE_WIDTH=5,this.MAX_LINE_OPACITY=1,this.MAX_TRAIL_VALUE=1;const t=this.validateAndLimitOptions(i);this.config={canvasContainer:t.canvasContainer||document.body,isBackground:void 0===t.isBackground||t.isBackground,canvasBackgroundColor:t.canvasBackgroundColor||"rgba(10, 10, 25, 1)",particleNumber:t.particleNumber||150,particleSpeed:t.particleSpeed||1,particleSize:t.particleSize||5,particleColor:t.particleColor||"rgba(156, 74, 255, 0.6)",lineWidth:t.lineWidth||1,lineColor:t.lineColor||"#ffffff",lineOpacity:t.lineOpacity||.3,linkDistance:t.linkDistance||120,showLinkLine:void 0===t.showLinkLine||t.showLinkLine,canvasSize:t.canvasSize||[800,600],trailValue:t.trailValue||.2},this.particles=[],this.animationId=null,this.isRunning=!1,this.lastTime=0,this.fps=60,this.fpsInterval=1e3/60,this.then=Date.now(),this.frameCount=0,this.lastFpsUpdate=Date.now(),this.resizeTimer=null,this.wasRunningBeforeHide=!1,this.init()}validateAndLimitOptions(i){const t=Object.assign({},i);return void 0!==i.particleNumber&&(i.particleNumber>this.MAX_PARTICLE_NUMBER?(console.warn(`Warning: particleNumber (${i.particleNumber}) exceeds maximum allowed value (${this.MAX_PARTICLE_NUMBER}). Using maximum value instead.`),t.particleNumber=this.MAX_PARTICLE_NUMBER):i.particleNumber<1&&(console.warn(`Warning: particleNumber (${i.particleNumber}) is below minimum value (1). Using minimum value instead.`),t.particleNumber=1)),void 0!==i.particleSpeed&&(i.particleSpeed>this.MAX_PARTICLE_SPEED?(console.warn(`Warning: particleSpeed (${i.particleSpeed}) exceeds maximum allowed value (${this.MAX_PARTICLE_SPEED}). Using maximum value instead.`),t.particleSpeed=this.MAX_PARTICLE_SPEED):i.particleSpeed<.1&&(console.warn(`Warning: particleSpeed (${i.particleSpeed}) is below minimum value (0.1). Using minimum value instead.`),t.particleSpeed=.1)),void 0!==i.particleSize&&(i.particleSize>this.MAX_PARTICLE_SIZE?(console.warn(`Warning: particleSize (${i.particleSize}) exceeds maximum allowed value (${this.MAX_PARTICLE_SIZE}). Using maximum value instead.`),t.particleSize=this.MAX_PARTICLE_SIZE):i.particleSize<1&&(console.warn(`Warning: particleSize (${i.particleSize}) is below minimum value (1). Using minimum value instead.`),t.particleSize=1)),void 0!==i.linkDistance&&(i.linkDistance>this.MAX_LINK_DISTANCE?(console.warn(`Warning: linkDistance (${i.linkDistance}) exceeds maximum allowed value (${this.MAX_LINK_DISTANCE}). Using maximum value instead.`),t.linkDistance=this.MAX_LINK_DISTANCE):i.linkDistance<10&&(console.warn(`Warning: linkDistance (${i.linkDistance}) is below minimum value (10). Using minimum value instead.`),t.linkDistance=10)),void 0!==i.lineWidth&&(i.lineWidth>this.MAX_LINE_WIDTH?(console.warn(`Warning: lineWidth (${i.lineWidth}) exceeds maximum allowed value (${this.MAX_LINE_WIDTH}). Using maximum value instead.`),t.lineWidth=this.MAX_LINE_WIDTH):i.lineWidth<.1&&(console.warn(`Warning: lineWidth (${i.lineWidth}) is below minimum value (0.1). Using minimum value instead.`),t.lineWidth=.1)),void 0!==i.lineOpacity&&(i.lineOpacity>this.MAX_LINE_OPACITY?(console.warn(`Warning: lineOpacity (${i.lineOpacity}) exceeds maximum allowed value (${this.MAX_LINE_OPACITY}). Using maximum value instead.`),t.lineOpacity=this.MAX_LINE_OPACITY):i.lineOpacity<0&&(console.warn(`Warning: lineOpacity (${i.lineOpacity}) is below minimum value (0). Using minimum value instead.`),t.lineOpacity=0)),void 0!==i.trailValue&&(i.trailValue>this.MAX_TRAIL_VALUE?(console.warn(`Warning: trailValue (${i.trailValue}) exceeds maximum allowed value (${this.MAX_TRAIL_VALUE}). Using maximum value instead.`),t.trailValue=this.MAX_TRAIL_VALUE):i.trailValue<0&&(console.warn(`Warning: trailValue (${i.trailValue}) is below minimum value (0). Using minimum value instead.`),t.trailValue=0)),t}init(){this.canvas=document.createElement("canvas");const i=this.canvas.getContext("2d");if(!i)throw new Error("Could not get 2D context from canvas");if(this.ctx=i,"string"==typeof this.config.canvasContainer){const i=document.querySelector(this.config.canvasContainer);if(!i)throw new Error(`Canvas container not found: ${this.config.canvasContainer}`);this.container=i}else this.container=this.config.canvasContainer;this.container?(this.container.appendChild(this.canvas),this.setupCanvas(),this.createParticles(),this.bindEvents()):console.error("Canvas container not found")}setupCanvas(){if(this.config.isBackground)this.canvas.style.position="fixed",this.canvas.style.top="0",this.canvas.style.left="0",this.canvas.style.width="100%",this.canvas.style.height="100%",this.canvas.style.zIndex="-1",this.canvas.width=window.innerWidth,this.canvas.height=window.innerHeight;else{this.canvas.style.display="block";const[i,t]=this.config.canvasSize;this.canvas.width=i,this.canvas.height=t,this.canvas.style.width=i+"px",this.canvas.style.height=t+"px"}this.canvas.style.pointerEvents="none"}createParticles(){this.particles=[];const i=Array.isArray(this.config.particleColor);for(let t=0;t<this.config.particleNumber;t++){let t;if(i){const i=this.config.particleColor;t=i[Math.floor(Math.random()*i.length)]}else t=this.config.particleColor;this.particles.push({x:Math.random()*this.canvas.width,y:Math.random()*this.canvas.height,vx:2*(Math.random()-.5)*this.config.particleSpeed,vy:2*(Math.random()-.5)*this.config.particleSpeed,size:this.config.particleSize,color:t,originalColor:t,pulse:0,pulseSpeed:.05*Math.random()+.02})}}updateParticles(){for(const i of this.particles)i.pulse+=i.pulseSpeed,i.pulse>2*Math.PI&&(i.pulse=0),i.x+=i.vx,i.y+=i.vy,(i.x<0||i.x>this.canvas.width)&&(i.vx*=-1),(i.y<0||i.y>this.canvas.height)&&(i.vy*=-1),i.x=Math.max(0,Math.min(this.canvas.width,i.x)),i.y=Math.max(0,Math.min(this.canvas.height,i.y))}drawParticles(){if(this.config.trailValue>0){let i=this.config.canvasBackgroundColor;if(i.startsWith("#")){const t=parseInt(i.slice(1,3),16),e=parseInt(i.slice(3,5),16),a=parseInt(i.slice(5,7),16);i=`rgba(${t}, ${e}, ${a}, ${this.config.trailValue})`}else if(i.startsWith("rgb("))i=i.replace("rgb(","rgba(").replace(")",`, ${this.config.trailValue})`);else if(i.startsWith("rgba(")){const t=i.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/);if(t){const e=parseFloat(t[4])*this.config.trailValue;i=`rgba(${t[1]}, ${t[2]}, ${t[3]}, ${e})`}}this.ctx.fillStyle=i,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height)}else this.ctx.fillStyle=this.config.canvasBackgroundColor,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height);if(this.config.showLinkLine)for(let i=0;i<this.particles.length;i++)for(let t=i+1;t<this.particles.length;t++){const e=this.particles[i].x-this.particles[t].x,a=this.particles[i].y-this.particles[t].y,s=Math.sqrt(e*e+a*a);if(s<this.config.linkDistance){const e=this.config.lineOpacity*(1-s/this.config.linkDistance);this.ctx.strokeStyle=this.config.lineColor,this.ctx.globalAlpha=e,this.ctx.lineWidth=this.config.lineWidth,this.ctx.beginPath(),this.ctx.moveTo(this.particles[i].x,this.particles[i].y),this.ctx.lineTo(this.particles[t].x,this.particles[t].y),this.ctx.stroke()}}this.ctx.globalAlpha=1;for(const i of this.particles){const t=i.size*(1+.2*Math.sin(i.pulse));this.ctx.shadowColor=i.color,this.ctx.shadowBlur=10,this.ctx.beginPath(),this.ctx.arc(i.x,i.y,t,0,2*Math.PI),this.ctx.fillStyle=i.color,this.ctx.fill(),this.ctx.shadowBlur=0}}updateFPS(){this.frameCount++;const i=Date.now(),t=i-this.lastFpsUpdate;t>=1e3&&(this.fps=Math.round(1e3*this.frameCount/t),this.frameCount=0,this.lastFpsUpdate=i)}animate(){if(!this.isRunning)return;const i=Date.now(),t=i-this.then;t>this.fpsInterval&&(this.then=i-t%this.fpsInterval,this.updateParticles(),this.drawParticles(),this.updateFPS()),this.animationId=requestAnimationFrame(()=>this.animate())}start(){this.isRunning||(this.isRunning=!0,this.then=Date.now(),this.animate())}pause(){this.isRunning=!1,this.animationId&&(cancelAnimationFrame(this.animationId),this.animationId=null)}reset(){const i=this.isRunning;i&&this.pause(),this.createParticles(),i?this.start():this.drawParticles()}resize(){if(this.config.isBackground){const i=this.canvas.width,t=this.canvas.height,e=window.innerWidth,a=window.innerHeight;if(this.canvas.width=e,this.canvas.height=a,this.particles.length>0){const s=e/i,n=a/t;for(const i of this.particles)i.x*=s,i.y*=n,i.x=Math.max(0,Math.min(e,i.x)),i.y=Math.max(0,Math.min(a,i.y)),(i.x<=0||i.x>=e)&&(i.vx*=-1),(i.y<=0||i.y>=a)&&(i.vy*=-1);this.drawParticles()}else this.createParticles(),this.drawParticles()}}destroy(){this.pause(),this.canvas&&this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas)}bindEvents(){window.addEventListener("resize",()=>{this.config.isBackground&&(this.resizeTimer&&clearTimeout(this.resizeTimer),this.resizeTimer=window.setTimeout(()=>{this.resize()},150))}),document.addEventListener("visibilitychange",()=>{document.hidden?(this.wasRunningBeforeHide=this.isRunning,this.isRunning&&this.pause()):(this.wasRunningBeforeHide&&this.start(),this.wasRunningBeforeHide=!1)})}}export{i as ParticleCanvas,i as default};
|
package/dist/bundle.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var MyBundle=function(){"use strict";return class{constructor(i={}){this.MAX_PARTICLE_NUMBER=800,this.MAX_PARTICLE_SPEED=3,this.MAX_PARTICLE_SIZE=10,this.MAX_LINK_DISTANCE=300,this.MAX_LINE_WIDTH=5,this.MAX_LINE_OPACITY=1,this.MAX_TRAIL_VALUE=1,this.validateOptionsType(i);const t=this.validateAndLimitOptions(i);this.config={canvasContainer:t.canvasContainer||document.body,isBackground:void 0===t.isBackground||t.isBackground,canvasBackgroundColor:t.canvasBackgroundColor||"rgba(10, 10, 25, 1)",particleNumber:t.particleNumber||150,particleSpeed:t.particleSpeed||1,particleSize:t.particleSize||5,particleColor:t.particleColor||"rgba(156, 74, 255, 0.6)",lineWidth:t.lineWidth||1,lineColor:t.lineColor||"#ffffff",lineOpacity:t.lineOpacity||.3,linkDistance:t.linkDistance||120,showLinkLine:void 0===t.showLinkLine||t.showLinkLine,canvasSize:t.canvasSize||[800,600],trailValue:t.trailValue||.2},this.particles=[],this.animationId=null,this.isRunning=!1,this.lastTime=0,this.fps=60,this.fpsInterval=1e3/60,this.then=Date.now(),this.frameCount=0,this.lastFpsUpdate=Date.now(),this.init()}validateOptionsType(i){null!=i&&("object"!=typeof i||Array.isArray(i)?console.warn("Warning: options parameter must be an object. Using default configuration."):(void 0===i.canvasContainer||"string"==typeof i.canvasContainer||i.canvasContainer instanceof HTMLElement||console.warn("Warning: canvasContainer must be a string selector or HTMLElement. Using default value."),void 0!==i.isBackground&&"boolean"!=typeof i.isBackground&&console.warn("Warning: isBackground must be a boolean. Using default value."),void 0!==i.canvasBackgroundColor&&"string"!=typeof i.canvasBackgroundColor&&console.warn("Warning: canvasBackgroundColor must be a string. Using default value."),void 0!==i.particleNumber&&"number"!=typeof i.particleNumber&&console.warn("Warning: particleNumber must be a number. Using default value."),void 0!==i.particleSpeed&&"number"!=typeof i.particleSpeed&&console.warn("Warning: particleSpeed must be a number. Using default value."),void 0!==i.particleSize&&"number"!=typeof i.particleSize&&console.warn("Warning: particleSize must be a number. Using default value."),void 0===i.particleColor||"string"==typeof i.particleColor||Array.isArray(i.particleColor)||console.warn("Warning: particleColor must be a string or array of strings. Using default value."),void 0!==i.lineWidth&&"number"!=typeof i.lineWidth&&console.warn("Warning: lineWidth must be a number. Using default value."),void 0!==i.lineColor&&"string"!=typeof i.lineColor&&console.warn("Warning: lineColor must be a string. Using default value."),void 0!==i.lineOpacity&&"number"!=typeof i.lineOpacity&&console.warn("Warning: lineOpacity must be a number. Using default value."),void 0!==i.linkDistance&&"number"!=typeof i.linkDistance&&console.warn("Warning: linkDistance must be a number. Using default value."),void 0!==i.showLinkLine&&"boolean"!=typeof i.showLinkLine&&console.warn("Warning: showLinkLine must be a boolean. Using default value."),void 0===i.canvasSize||Array.isArray(i.canvasSize)&&2===i.canvasSize.length&&"number"==typeof i.canvasSize[0]&&"number"==typeof i.canvasSize[1]||console.warn("Warning: canvasSize must be an array of two numbers [width, height]. Using default value."),void 0!==i.trailValue&&"number"!=typeof i.trailValue&&console.warn("Warning: trailValue must be a number. Using default value.")))}validateAndLimitOptions(i){const t=Object.assign({},i);return void 0!==i.particleNumber&&(i.particleNumber>this.MAX_PARTICLE_NUMBER?(console.warn(`Warning: particleNumber (${i.particleNumber}) exceeds maximum allowed value (${this.MAX_PARTICLE_NUMBER}). Using maximum value instead.`),t.particleNumber=this.MAX_PARTICLE_NUMBER):i.particleNumber<1&&(console.warn(`Warning: particleNumber (${i.particleNumber}) is below minimum value (1). Using minimum value instead.`),t.particleNumber=1)),void 0!==i.particleSpeed&&(i.particleSpeed>this.MAX_PARTICLE_SPEED?(console.warn(`Warning: particleSpeed (${i.particleSpeed}) exceeds maximum allowed value (${this.MAX_PARTICLE_SPEED}). Using maximum value instead.`),t.particleSpeed=this.MAX_PARTICLE_SPEED):i.particleSpeed<.1&&(console.warn(`Warning: particleSpeed (${i.particleSpeed}) is below minimum value (0.1). Using minimum value instead.`),t.particleSpeed=.1)),void 0!==i.particleSize&&(i.particleSize>this.MAX_PARTICLE_SIZE?(console.warn(`Warning: particleSize (${i.particleSize}) exceeds maximum allowed value (${this.MAX_PARTICLE_SIZE}). Using maximum value instead.`),t.particleSize=this.MAX_PARTICLE_SIZE):i.particleSize<1&&(console.warn(`Warning: particleSize (${i.particleSize}) is below minimum value (1). Using minimum value instead.`),t.particleSize=1)),void 0!==i.linkDistance&&(i.linkDistance>this.MAX_LINK_DISTANCE?(console.warn(`Warning: linkDistance (${i.linkDistance}) exceeds maximum allowed value (${this.MAX_LINK_DISTANCE}). Using maximum value instead.`),t.linkDistance=this.MAX_LINK_DISTANCE):i.linkDistance<10&&(console.warn(`Warning: linkDistance (${i.linkDistance}) is below minimum value (10). Using minimum value instead.`),t.linkDistance=10)),void 0!==i.lineWidth&&(i.lineWidth>this.MAX_LINE_WIDTH?(console.warn(`Warning: lineWidth (${i.lineWidth}) exceeds maximum allowed value (${this.MAX_LINE_WIDTH}). Using maximum value instead.`),t.lineWidth=this.MAX_LINE_WIDTH):i.lineWidth<.1&&(console.warn(`Warning: lineWidth (${i.lineWidth}) is below minimum value (0.1). Using minimum value instead.`),t.lineWidth=.1)),void 0!==i.lineOpacity&&(i.lineOpacity>this.MAX_LINE_OPACITY?(console.warn(`Warning: lineOpacity (${i.lineOpacity}) exceeds maximum allowed value (${this.MAX_LINE_OPACITY}). Using maximum value instead.`),t.lineOpacity=this.MAX_LINE_OPACITY):i.lineOpacity<0&&(console.warn(`Warning: lineOpacity (${i.lineOpacity}) is below minimum value (0). Using minimum value instead.`),t.lineOpacity=0)),void 0!==i.trailValue&&(i.trailValue>this.MAX_TRAIL_VALUE?(console.warn(`Warning: trailValue (${i.trailValue}) exceeds maximum allowed value (${this.MAX_TRAIL_VALUE}). Using maximum value instead.`),t.trailValue=this.MAX_TRAIL_VALUE):i.trailValue<0&&(console.warn(`Warning: trailValue (${i.trailValue}) is below minimum value (0). Using minimum value instead.`),t.trailValue=0)),t}init(){this.canvas=document.createElement("canvas");const i=this.canvas.getContext("2d");if(!i)throw new Error("Could not get 2D context from canvas");if(this.ctx=i,"string"==typeof this.config.canvasContainer){const i=document.querySelector(this.config.canvasContainer);if(!i)throw new Error(`Canvas container not found: ${this.config.canvasContainer}`);this.container=i}else this.container=this.config.canvasContainer;this.container?(this.container.appendChild(this.canvas),this.setupCanvas(),this.createParticles(),this.bindEvents()):console.error("Canvas container not found")}setupCanvas(){if(this.config.isBackground)this.canvas.style.position="fixed",this.canvas.style.top="0",this.canvas.style.left="0",this.canvas.style.width="100%",this.canvas.style.height="100%",this.canvas.style.zIndex="-1",this.canvas.width=window.innerWidth,this.canvas.height=window.innerHeight;else{this.canvas.style.display="block";const[i,t]=this.config.canvasSize;this.canvas.width=i,this.canvas.height=t,this.canvas.style.width=i+"px",this.canvas.style.height=t+"px"}this.canvas.style.pointerEvents="none"}createParticles(){this.particles=[];const i=Array.isArray(this.config.particleColor);for(let t=0;t<this.config.particleNumber;t++){let t;if(i){const i=this.config.particleColor;t=i[Math.floor(Math.random()*i.length)]}else t=this.config.particleColor;this.particles.push({x:Math.random()*this.canvas.width,y:Math.random()*this.canvas.height,vx:2*(Math.random()-.5)*this.config.particleSpeed,vy:2*(Math.random()-.5)*this.config.particleSpeed,size:this.config.particleSize,color:t,originalColor:t,pulse:0,pulseSpeed:.05*Math.random()+.02})}}updateParticles(){for(const i of this.particles)i.pulse+=i.pulseSpeed,i.pulse>2*Math.PI&&(i.pulse=0),i.x+=i.vx,i.y+=i.vy,(i.x<0||i.x>this.canvas.width)&&(i.vx*=-1),(i.y<0||i.y>this.canvas.height)&&(i.vy*=-1),i.x=Math.max(0,Math.min(this.canvas.width,i.x)),i.y=Math.max(0,Math.min(this.canvas.height,i.y))}drawParticles(){if(this.config.trailValue>0){let i=this.config.canvasBackgroundColor;if(i.startsWith("#")){const t=parseInt(i.slice(1,3),16),e=parseInt(i.slice(3,5),16),a=parseInt(i.slice(5,7),16);i=`rgba(${t}, ${e}, ${a}, ${this.config.trailValue})`}else if(i.startsWith("rgb("))i=i.replace("rgb(","rgba(").replace(")",`, ${this.config.trailValue})`);else if(i.startsWith("rgba(")){const t=i.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/);if(t){const e=parseFloat(t[4])*this.config.trailValue;i=`rgba(${t[1]}, ${t[2]}, ${t[3]}, ${e})`}}this.ctx.fillStyle=i,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height)}else this.ctx.fillStyle=this.config.canvasBackgroundColor,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height);if(this.config.showLinkLine)for(let i=0;i<this.particles.length;i++)for(let t=i+1;t<this.particles.length;t++){const e=this.particles[i].x-this.particles[t].x,a=this.particles[i].y-this.particles[t].y,n=Math.sqrt(e*e+a*a);if(n<this.config.linkDistance){const e=this.config.lineOpacity*(1-n/this.config.linkDistance);this.ctx.strokeStyle=this.config.lineColor,this.ctx.globalAlpha=e,this.ctx.lineWidth=this.config.lineWidth,this.ctx.beginPath(),this.ctx.moveTo(this.particles[i].x,this.particles[i].y),this.ctx.lineTo(this.particles[t].x,this.particles[t].y),this.ctx.stroke()}}this.ctx.globalAlpha=1;for(const i of this.particles){const t=i.size*(1+.2*Math.sin(i.pulse));this.ctx.shadowColor=i.color,this.ctx.shadowBlur=10,this.ctx.beginPath(),this.ctx.arc(i.x,i.y,t,0,2*Math.PI),this.ctx.fillStyle=i.color,this.ctx.fill(),this.ctx.shadowBlur=0}}updateFPS(){this.frameCount++;const i=Date.now(),t=i-this.lastFpsUpdate;t>=1e3&&(this.fps=Math.round(1e3*this.frameCount/t),this.frameCount=0,this.lastFpsUpdate=i)}animate(){if(!this.isRunning)return;const i=Date.now(),t=i-this.then;t>this.fpsInterval&&(this.then=i-t%this.fpsInterval,this.updateParticles(),this.drawParticles(),this.updateFPS()),this.animationId=requestAnimationFrame(()=>this.animate())}start(){this.isRunning||(this.isRunning=!0,this.then=Date.now(),this.animate())}pause(){this.isRunning=!1,this.animationId&&(cancelAnimationFrame(this.animationId),this.animationId=null)}reset(){this.pause(),this.createParticles(),this.start()}resize(){this.config.isBackground&&(this.canvas.width=window.innerWidth,this.canvas.height=window.innerHeight),this.reset()}destroy(){this.pause(),this.canvas&&this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas)}bindEvents(){window.addEventListener("resize",()=>{this.config.isBackground&&this.resize()}),this.canvas.addEventListener("mousemove",i=>{const t=this.canvas.getBoundingClientRect(),e=i.clientX-t.left,a=i.clientY-t.top;for(const i of this.particles){const t=i.x-e,n=i.y-a,s=Math.sqrt(t*t+n*n);if(s<100){const e=(100-s)/100;i.vx+=.01*t*e,i.vy+=.01*n*e}}})}}}();
|
|
1
|
+
var ParticleCanvasPro=function(i){"use strict";class t{constructor(i={}){this.MAX_PARTICLE_NUMBER=800,this.MAX_PARTICLE_SPEED=3,this.MAX_PARTICLE_SIZE=10,this.MAX_LINK_DISTANCE=300,this.MAX_LINE_WIDTH=5,this.MAX_LINE_OPACITY=1,this.MAX_TRAIL_VALUE=1;const t=this.validateAndLimitOptions(i);this.config={canvasContainer:t.canvasContainer||document.body,isBackground:void 0===t.isBackground||t.isBackground,canvasBackgroundColor:t.canvasBackgroundColor||"rgba(10, 10, 25, 1)",particleNumber:t.particleNumber||150,particleSpeed:t.particleSpeed||1,particleSize:t.particleSize||5,particleColor:t.particleColor||"rgba(156, 74, 255, 0.6)",lineWidth:t.lineWidth||1,lineColor:t.lineColor||"#ffffff",lineOpacity:t.lineOpacity||.3,linkDistance:t.linkDistance||120,showLinkLine:void 0===t.showLinkLine||t.showLinkLine,canvasSize:t.canvasSize||[800,600],trailValue:t.trailValue||.2},this.particles=[],this.animationId=null,this.isRunning=!1,this.lastTime=0,this.fps=60,this.fpsInterval=1e3/60,this.then=Date.now(),this.frameCount=0,this.lastFpsUpdate=Date.now(),this.resizeTimer=null,this.wasRunningBeforeHide=!1,this.init()}validateAndLimitOptions(i){const t=Object.assign({},i);return void 0!==i.particleNumber&&(i.particleNumber>this.MAX_PARTICLE_NUMBER?(console.warn(`Warning: particleNumber (${i.particleNumber}) exceeds maximum allowed value (${this.MAX_PARTICLE_NUMBER}). Using maximum value instead.`),t.particleNumber=this.MAX_PARTICLE_NUMBER):i.particleNumber<1&&(console.warn(`Warning: particleNumber (${i.particleNumber}) is below minimum value (1). Using minimum value instead.`),t.particleNumber=1)),void 0!==i.particleSpeed&&(i.particleSpeed>this.MAX_PARTICLE_SPEED?(console.warn(`Warning: particleSpeed (${i.particleSpeed}) exceeds maximum allowed value (${this.MAX_PARTICLE_SPEED}). Using maximum value instead.`),t.particleSpeed=this.MAX_PARTICLE_SPEED):i.particleSpeed<.1&&(console.warn(`Warning: particleSpeed (${i.particleSpeed}) is below minimum value (0.1). Using minimum value instead.`),t.particleSpeed=.1)),void 0!==i.particleSize&&(i.particleSize>this.MAX_PARTICLE_SIZE?(console.warn(`Warning: particleSize (${i.particleSize}) exceeds maximum allowed value (${this.MAX_PARTICLE_SIZE}). Using maximum value instead.`),t.particleSize=this.MAX_PARTICLE_SIZE):i.particleSize<1&&(console.warn(`Warning: particleSize (${i.particleSize}) is below minimum value (1). Using minimum value instead.`),t.particleSize=1)),void 0!==i.linkDistance&&(i.linkDistance>this.MAX_LINK_DISTANCE?(console.warn(`Warning: linkDistance (${i.linkDistance}) exceeds maximum allowed value (${this.MAX_LINK_DISTANCE}). Using maximum value instead.`),t.linkDistance=this.MAX_LINK_DISTANCE):i.linkDistance<10&&(console.warn(`Warning: linkDistance (${i.linkDistance}) is below minimum value (10). Using minimum value instead.`),t.linkDistance=10)),void 0!==i.lineWidth&&(i.lineWidth>this.MAX_LINE_WIDTH?(console.warn(`Warning: lineWidth (${i.lineWidth}) exceeds maximum allowed value (${this.MAX_LINE_WIDTH}). Using maximum value instead.`),t.lineWidth=this.MAX_LINE_WIDTH):i.lineWidth<.1&&(console.warn(`Warning: lineWidth (${i.lineWidth}) is below minimum value (0.1). Using minimum value instead.`),t.lineWidth=.1)),void 0!==i.lineOpacity&&(i.lineOpacity>this.MAX_LINE_OPACITY?(console.warn(`Warning: lineOpacity (${i.lineOpacity}) exceeds maximum allowed value (${this.MAX_LINE_OPACITY}). Using maximum value instead.`),t.lineOpacity=this.MAX_LINE_OPACITY):i.lineOpacity<0&&(console.warn(`Warning: lineOpacity (${i.lineOpacity}) is below minimum value (0). Using minimum value instead.`),t.lineOpacity=0)),void 0!==i.trailValue&&(i.trailValue>this.MAX_TRAIL_VALUE?(console.warn(`Warning: trailValue (${i.trailValue}) exceeds maximum allowed value (${this.MAX_TRAIL_VALUE}). Using maximum value instead.`),t.trailValue=this.MAX_TRAIL_VALUE):i.trailValue<0&&(console.warn(`Warning: trailValue (${i.trailValue}) is below minimum value (0). Using minimum value instead.`),t.trailValue=0)),t}init(){this.canvas=document.createElement("canvas");const i=this.canvas.getContext("2d");if(!i)throw new Error("Could not get 2D context from canvas");if(this.ctx=i,"string"==typeof this.config.canvasContainer){const i=document.querySelector(this.config.canvasContainer);if(!i)throw new Error(`Canvas container not found: ${this.config.canvasContainer}`);this.container=i}else this.container=this.config.canvasContainer;this.container?(this.container.appendChild(this.canvas),this.setupCanvas(),this.createParticles(),this.bindEvents()):console.error("Canvas container not found")}setupCanvas(){if(this.config.isBackground)this.canvas.style.position="fixed",this.canvas.style.top="0",this.canvas.style.left="0",this.canvas.style.width="100%",this.canvas.style.height="100%",this.canvas.style.zIndex="-1",this.canvas.width=window.innerWidth,this.canvas.height=window.innerHeight;else{this.canvas.style.display="block";const[i,t]=this.config.canvasSize;this.canvas.width=i,this.canvas.height=t,this.canvas.style.width=i+"px",this.canvas.style.height=t+"px"}this.canvas.style.pointerEvents="none"}createParticles(){this.particles=[];const i=Array.isArray(this.config.particleColor);for(let t=0;t<this.config.particleNumber;t++){let t;if(i){const i=this.config.particleColor;t=i[Math.floor(Math.random()*i.length)]}else t=this.config.particleColor;this.particles.push({x:Math.random()*this.canvas.width,y:Math.random()*this.canvas.height,vx:2*(Math.random()-.5)*this.config.particleSpeed,vy:2*(Math.random()-.5)*this.config.particleSpeed,size:this.config.particleSize,color:t,originalColor:t,pulse:0,pulseSpeed:.05*Math.random()+.02})}}updateParticles(){for(const i of this.particles)i.pulse+=i.pulseSpeed,i.pulse>2*Math.PI&&(i.pulse=0),i.x+=i.vx,i.y+=i.vy,(i.x<0||i.x>this.canvas.width)&&(i.vx*=-1),(i.y<0||i.y>this.canvas.height)&&(i.vy*=-1),i.x=Math.max(0,Math.min(this.canvas.width,i.x)),i.y=Math.max(0,Math.min(this.canvas.height,i.y))}drawParticles(){if(this.config.trailValue>0){let i=this.config.canvasBackgroundColor;if(i.startsWith("#")){const t=parseInt(i.slice(1,3),16),e=parseInt(i.slice(3,5),16),a=parseInt(i.slice(5,7),16);i=`rgba(${t}, ${e}, ${a}, ${this.config.trailValue})`}else if(i.startsWith("rgb("))i=i.replace("rgb(","rgba(").replace(")",`, ${this.config.trailValue})`);else if(i.startsWith("rgba(")){const t=i.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/);if(t){const e=parseFloat(t[4])*this.config.trailValue;i=`rgba(${t[1]}, ${t[2]}, ${t[3]}, ${e})`}}this.ctx.fillStyle=i,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height)}else this.ctx.fillStyle=this.config.canvasBackgroundColor,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height);if(this.config.showLinkLine)for(let i=0;i<this.particles.length;i++)for(let t=i+1;t<this.particles.length;t++){const e=this.particles[i].x-this.particles[t].x,a=this.particles[i].y-this.particles[t].y,s=Math.sqrt(e*e+a*a);if(s<this.config.linkDistance){const e=this.config.lineOpacity*(1-s/this.config.linkDistance);this.ctx.strokeStyle=this.config.lineColor,this.ctx.globalAlpha=e,this.ctx.lineWidth=this.config.lineWidth,this.ctx.beginPath(),this.ctx.moveTo(this.particles[i].x,this.particles[i].y),this.ctx.lineTo(this.particles[t].x,this.particles[t].y),this.ctx.stroke()}}this.ctx.globalAlpha=1;for(const i of this.particles){const t=i.size*(1+.2*Math.sin(i.pulse));this.ctx.shadowColor=i.color,this.ctx.shadowBlur=10,this.ctx.beginPath(),this.ctx.arc(i.x,i.y,t,0,2*Math.PI),this.ctx.fillStyle=i.color,this.ctx.fill(),this.ctx.shadowBlur=0}}updateFPS(){this.frameCount++;const i=Date.now(),t=i-this.lastFpsUpdate;t>=1e3&&(this.fps=Math.round(1e3*this.frameCount/t),this.frameCount=0,this.lastFpsUpdate=i)}animate(){if(!this.isRunning)return;const i=Date.now(),t=i-this.then;t>this.fpsInterval&&(this.then=i-t%this.fpsInterval,this.updateParticles(),this.drawParticles(),this.updateFPS()),this.animationId=requestAnimationFrame(()=>this.animate())}start(){this.isRunning||(this.isRunning=!0,this.then=Date.now(),this.animate())}pause(){this.isRunning=!1,this.animationId&&(cancelAnimationFrame(this.animationId),this.animationId=null)}reset(){const i=this.isRunning;i&&this.pause(),this.createParticles(),i?this.start():this.drawParticles()}resize(){if(this.config.isBackground){const i=this.canvas.width,t=this.canvas.height,e=window.innerWidth,a=window.innerHeight;if(this.canvas.width=e,this.canvas.height=a,this.particles.length>0){const s=e/i,n=a/t;for(const i of this.particles)i.x*=s,i.y*=n,i.x=Math.max(0,Math.min(e,i.x)),i.y=Math.max(0,Math.min(a,i.y)),(i.x<=0||i.x>=e)&&(i.vx*=-1),(i.y<=0||i.y>=a)&&(i.vy*=-1);this.drawParticles()}else this.createParticles(),this.drawParticles()}}destroy(){this.pause(),this.canvas&&this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas)}bindEvents(){window.addEventListener("resize",()=>{this.config.isBackground&&(this.resizeTimer&&clearTimeout(this.resizeTimer),this.resizeTimer=window.setTimeout(()=>{this.resize()},150))}),document.addEventListener("visibilitychange",()=>{document.hidden?(this.wasRunningBeforeHide=this.isRunning,this.isRunning&&this.pause()):(this.wasRunningBeforeHide&&this.start(),this.wasRunningBeforeHide=!1)})}}return i.ParticleCanvas=t,i.default=t,Object.defineProperty(i,"__esModule",{value:!0}),i}({});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 高性能粒子系统
|
|
3
|
+
* 使用Canvas实现可配置的粒子动画效果,支持背景模式和内嵌模式
|
|
4
|
+
*/
|
|
5
|
+
import { ParticleCanvasOptions } from './types';
|
|
6
|
+
declare class ParticleCanvas {
|
|
7
|
+
private config;
|
|
8
|
+
private particles;
|
|
9
|
+
private animationId;
|
|
10
|
+
private isRunning;
|
|
11
|
+
private lastTime;
|
|
12
|
+
private fps;
|
|
13
|
+
private fpsInterval;
|
|
14
|
+
private then;
|
|
15
|
+
private frameCount;
|
|
16
|
+
private lastFpsUpdate;
|
|
17
|
+
private canvas;
|
|
18
|
+
private ctx;
|
|
19
|
+
private container;
|
|
20
|
+
private resizeTimer;
|
|
21
|
+
private wasRunningBeforeHide;
|
|
22
|
+
private readonly MAX_PARTICLE_NUMBER;
|
|
23
|
+
private readonly MAX_PARTICLE_SPEED;
|
|
24
|
+
private readonly MAX_PARTICLE_SIZE;
|
|
25
|
+
private readonly MAX_LINK_DISTANCE;
|
|
26
|
+
private readonly MAX_LINE_WIDTH;
|
|
27
|
+
private readonly MAX_LINE_OPACITY;
|
|
28
|
+
private readonly MAX_TRAIL_VALUE;
|
|
29
|
+
/**
|
|
30
|
+
* 初始化配置
|
|
31
|
+
* @param {ParticleCanvasOptions} options - 配置对象
|
|
32
|
+
*/
|
|
33
|
+
constructor(options?: ParticleCanvasOptions);
|
|
34
|
+
/**
|
|
35
|
+
* 验证并限制配置选项,确保参数在合理范围内
|
|
36
|
+
* @param options 用户提供的配置选项
|
|
37
|
+
* @returns 验证后的配置选项
|
|
38
|
+
*/
|
|
39
|
+
private validateAndLimitOptions;
|
|
40
|
+
/**
|
|
41
|
+
* 初始化粒子系统
|
|
42
|
+
* 创建画布、设置容器、创建粒子、绑定事件
|
|
43
|
+
*/
|
|
44
|
+
private init;
|
|
45
|
+
/**
|
|
46
|
+
* 设置画布样式和尺寸
|
|
47
|
+
* 根据配置决定是背景模式还是内嵌模式
|
|
48
|
+
*/
|
|
49
|
+
private setupCanvas;
|
|
50
|
+
/**
|
|
51
|
+
* 创建粒子数组
|
|
52
|
+
* 根据配置生成指定数量的粒子
|
|
53
|
+
*/
|
|
54
|
+
private createParticles;
|
|
55
|
+
/**
|
|
56
|
+
* 更新粒子状态
|
|
57
|
+
* 计算每个粒子的新位置和状态
|
|
58
|
+
*/
|
|
59
|
+
private updateParticles;
|
|
60
|
+
/**
|
|
61
|
+
* 绘制粒子和连线
|
|
62
|
+
* 在画布上渲染粒子系统
|
|
63
|
+
*/
|
|
64
|
+
private drawParticles;
|
|
65
|
+
/**
|
|
66
|
+
* 更新FPS计数器
|
|
67
|
+
* 计算并更新当前帧率
|
|
68
|
+
*/
|
|
69
|
+
private updateFPS;
|
|
70
|
+
/**
|
|
71
|
+
* 动画循环
|
|
72
|
+
* 使用requestAnimationFrame实现平滑动画
|
|
73
|
+
*/
|
|
74
|
+
private animate;
|
|
75
|
+
/**
|
|
76
|
+
* 开始粒子动画
|
|
77
|
+
*/
|
|
78
|
+
start(): void;
|
|
79
|
+
/**
|
|
80
|
+
* 暂停粒子动画
|
|
81
|
+
*/
|
|
82
|
+
pause(): void;
|
|
83
|
+
/**
|
|
84
|
+
* 重置粒子系统
|
|
85
|
+
* 重新创建粒子并开始动画
|
|
86
|
+
*/
|
|
87
|
+
reset(): void;
|
|
88
|
+
/**
|
|
89
|
+
* 调整画布尺寸
|
|
90
|
+
* 主要用于响应窗口大小变化,优化性能
|
|
91
|
+
*/
|
|
92
|
+
resize(): void;
|
|
93
|
+
/**
|
|
94
|
+
* 销毁粒子系统
|
|
95
|
+
* 停止动画并移除画布
|
|
96
|
+
*/
|
|
97
|
+
destroy(): void;
|
|
98
|
+
/**
|
|
99
|
+
* 绑定事件监听器
|
|
100
|
+
*/
|
|
101
|
+
private bindEvents;
|
|
102
|
+
}
|
|
103
|
+
export default ParticleCanvas;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,106 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*/
|
|
5
|
-
import { ParticleCanvasOptions } from './types';
|
|
6
|
-
declare class ParticleCanvas {
|
|
7
|
-
private config;
|
|
8
|
-
private particles;
|
|
9
|
-
private animationId;
|
|
10
|
-
private isRunning;
|
|
11
|
-
private lastTime;
|
|
12
|
-
private fps;
|
|
13
|
-
private fpsInterval;
|
|
14
|
-
private then;
|
|
15
|
-
private frameCount;
|
|
16
|
-
private lastFpsUpdate;
|
|
17
|
-
private canvas;
|
|
18
|
-
private ctx;
|
|
19
|
-
private container;
|
|
20
|
-
private readonly MAX_PARTICLE_NUMBER;
|
|
21
|
-
private readonly MAX_PARTICLE_SPEED;
|
|
22
|
-
private readonly MAX_PARTICLE_SIZE;
|
|
23
|
-
private readonly MAX_LINK_DISTANCE;
|
|
24
|
-
private readonly MAX_LINE_WIDTH;
|
|
25
|
-
private readonly MAX_LINE_OPACITY;
|
|
26
|
-
private readonly MAX_TRAIL_VALUE;
|
|
27
|
-
/**
|
|
28
|
-
* 初始化配置
|
|
29
|
-
* @param {ParticleCanvasOptions} options - 配置对象
|
|
30
|
-
*/
|
|
31
|
-
constructor(options?: ParticleCanvasOptions);
|
|
32
|
-
/**
|
|
33
|
-
* 验证配置选项类型
|
|
34
|
-
* @param options 用户提供的配置选项
|
|
35
|
-
*/
|
|
36
|
-
private validateOptionsType;
|
|
37
|
-
/**
|
|
38
|
-
* 验证并限制配置选项,确保参数在合理范围内
|
|
39
|
-
* @param options 用户提供的配置选项
|
|
40
|
-
* @returns 验证后的配置选项
|
|
41
|
-
*/
|
|
42
|
-
private validateAndLimitOptions;
|
|
43
|
-
/**
|
|
44
|
-
* 初始化粒子系统
|
|
45
|
-
* 创建画布、设置容器、创建粒子、绑定事件
|
|
46
|
-
*/
|
|
47
|
-
private init;
|
|
48
|
-
/**
|
|
49
|
-
* 设置画布样式和尺寸
|
|
50
|
-
* 根据配置决定是背景模式还是内嵌模式
|
|
51
|
-
*/
|
|
52
|
-
private setupCanvas;
|
|
53
|
-
/**
|
|
54
|
-
* 创建粒子数组
|
|
55
|
-
* 根据配置生成指定数量的粒子
|
|
56
|
-
*/
|
|
57
|
-
private createParticles;
|
|
58
|
-
/**
|
|
59
|
-
* 更新粒子状态
|
|
60
|
-
* 计算每个粒子的新位置和状态
|
|
61
|
-
*/
|
|
62
|
-
private updateParticles;
|
|
63
|
-
/**
|
|
64
|
-
* 绘制粒子和连线
|
|
65
|
-
* 在画布上渲染粒子系统
|
|
66
|
-
*/
|
|
67
|
-
private drawParticles;
|
|
68
|
-
/**
|
|
69
|
-
* 更新FPS计数器
|
|
70
|
-
* 计算并更新当前帧率
|
|
71
|
-
*/
|
|
72
|
-
private updateFPS;
|
|
73
|
-
/**
|
|
74
|
-
* 动画循环
|
|
75
|
-
* 使用requestAnimationFrame实现平滑动画
|
|
76
|
-
*/
|
|
77
|
-
private animate;
|
|
78
|
-
/**
|
|
79
|
-
* 开始粒子动画
|
|
80
|
-
*/
|
|
81
|
-
start(): void;
|
|
82
|
-
/**
|
|
83
|
-
* 暂停粒子动画
|
|
84
|
-
*/
|
|
85
|
-
pause(): void;
|
|
86
|
-
/**
|
|
87
|
-
* 重置粒子系统
|
|
88
|
-
* 重新创建粒子并开始动画
|
|
89
|
-
*/
|
|
90
|
-
reset(): void;
|
|
91
|
-
/**
|
|
92
|
-
* 调整画布尺寸
|
|
93
|
-
* 主要用于响应窗口大小变化
|
|
94
|
-
*/
|
|
95
|
-
resize(): void;
|
|
96
|
-
/**
|
|
97
|
-
* 销毁粒子系统
|
|
98
|
-
* 停止动画并移除画布
|
|
99
|
-
*/
|
|
100
|
-
destroy(): void;
|
|
101
|
-
/**
|
|
102
|
-
* 绑定事件监听器
|
|
103
|
-
*/
|
|
104
|
-
private bindEvents;
|
|
105
|
-
}
|
|
106
|
-
export default ParticleCanvas;
|
|
1
|
+
export * from './types';
|
|
2
|
+
export { default as ParticleCanvas } from './ParticleCanvas';
|
|
3
|
+
export { default } from './ParticleCanvas';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "particle-canvas-pro",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "🚀
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "🚀 🚀 一款基于requestAnimationFrame的高性能Canvas粒子系统,专为 Web 场景提供流畅、可配置的视觉动效方案,支持全屏背景与独立画布,提供实时控制、丰富视觉效果与流畅交互体验。",
|
|
5
5
|
"main": "dist/bundle.cjs.js",
|
|
6
6
|
"module": "dist/bundle.esm.js",
|
|
7
7
|
"exports": {
|
|
@@ -29,14 +29,16 @@
|
|
|
29
29
|
"dev": "serve dist"
|
|
30
30
|
},
|
|
31
31
|
"keywords": [
|
|
32
|
+
"particle-system",
|
|
32
33
|
"particle",
|
|
34
|
+
"requestanimationframe",
|
|
33
35
|
"canvas",
|
|
34
36
|
"animation",
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
37
|
+
"fps",
|
|
38
|
+
"粒子系统",
|
|
39
|
+
"高性能粒子",
|
|
40
|
+
"可配置粒子",
|
|
41
|
+
"全屏背景动画"
|
|
40
42
|
],
|
|
41
43
|
"author": "lebron_shi",
|
|
42
44
|
"license": "MIT",
|