js-waves-particles 1.0.2 → 1.0.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.
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var l=class{constructor(e={}){let{canvas:i,config:t}=e||{};this.canvas=null,this.ctx=null,this.mouseX=0,this.mouseY=0,this.targetMouseX=-1e3,this.targetMouseY=-1e3,this.mouseActive=!1,this.width=0,this.height=0,this.config=this._mergeDefaults(t||{}),this.time=0,this.isRunning=!1,this.particles=[],this._resizeTimeout=null;let s=typeof document<"u"&&i?typeof i=="string"?document.querySelector(i):i:null;if(s){this.canvas=s;let o=s.getBoundingClientRect();this.width=Math.round(o.width||window.innerWidth),this.height=Math.round(o.height||window.innerHeight)}else this._createAutoCanvas();try{if(!this.canvas)throw new Error("No canvas element available.");let o=this.canvas.getContext("2d");if(!o)throw new Error("Could not get 2D rendering context.");this.ctx=o}catch(o){console.error("[WaveParticles] Canvas error:",o);return}this._onMouseMove=this._onMouseMove.bind(this),this._onMouseLeave=this._onMouseLeave.bind(this),this._onTouchMove=this._onTouchMove.bind(this),this._onTouchEnd=this._onTouchEnd.bind(this),this._onResize=this._onResize.bind(this),typeof document<"u"&&(window.addEventListener("resize",this._onResize),this.resize()),this.initParticles(),this.start()}_mergeDefaults(e){let i={waves:[{amplitude:60,frequency:.003,speed:.006,yOffset:.55,color:"rgba(248, 225, 231, 0.25)",lineWidth:1.5,mouseInfluence:.45},{amplitude:50,frequency:.004,speed:.008,yOffset:.6,color:"rgba(212, 165, 165, 0.2)",lineWidth:1,mouseInfluence:.5},{amplitude:70,frequency:.0025,speed:.005,yOffset:.65,color:"rgba(255, 255, 255, 0.2)",lineWidth:1.5,mouseInfluence:.4},{amplitude:40,frequency:.0035,speed:.01,yOffset:.7,color:"rgba(201, 169, 110, 0.12)",lineWidth:1,mouseInfluence:.55}],particles:{countScale:8e3,maxCount:180},colors:{backgroundGradient:["#fdfcfb","#f7ede2","#e8d5d0"],particleColorPrefixes:["rgba(248, 225, 231,","rgba(212, 165, 165,","rgba(201, 169, 110,","rgba(255, 255, 255,","rgba(232, 213, 208,"],mouseGlowStops:[{offset:0,color:"rgba(255, 255, 255, 0.08)"},{offset:.5,color:"rgba(248, 225, 231, 0.04)"},{offset:1,color:"rgba(248, 225, 231, 0)"}]}},t={};for(let s in i)i.hasOwnProperty(s)&&(e&&e[s]?typeof i[s]=="object"&&!Array.isArray(i[s])&&typeof e[s]=="object"?t[s]=Object.assign({},i[s],e[s]):s==="waves"?t.waves=Array.isArray(e.waves)?[...e.waves]:JSON.parse(JSON.stringify(i.waves)):t[s]=e[s]:t[s]=JSON.parse(JSON.stringify(i[s])));return t}_createAutoCanvas(){if(typeof document>"u")return;let e=document.createElement("canvas");e.id="wave-particles-canvas",Object.assign(e.style,{position:"fixed",top:"0",left:"0",width:"100%",height:"100%",zIndex:"-1",pointerEvents:"none"});let i=document.body;i&&(i.insertBefore(e,i.firstChild),this.canvas=e,this.width=window.innerWidth,this.height=window.innerHeight)}_onResize(){this._resizeTimeout&&clearTimeout(this._resizeTimeout),this._resizeTimeout=setTimeout(()=>{this.resize()},250)}resize(){if(!this.canvas)return;let e=this.canvas.getBoundingClientRect(),i=window.devicePixelRatio||1,t=e.width||window.innerWidth,s=e.height||window.innerHeight;this.width=t,this.height=s,this.canvas.width=Math.round(t*i),this.canvas.height=Math.round(s*i),this.ctx||(this.ctx=this.canvas.getContext("2d")),this.ctx&&this.ctx.scale(i,i),this.isRunning&&this.initParticles()}initParticles(){let{countScale:e=8e3,maxCount:i=180}=this.config.particles,t=Math.min(Math.floor(this.width*this.height/(e>0?e:8e3)),i);this.particles=Array.from({length:t},()=>({x:Math.random()*this.width,y:Math.random()*this.height,size:Math.random()*4+1.5,speedX:(Math.random()-.5)*.4,speedY:(Math.random()-.5)*.4,opacity:Math.random()*.6+.3,pulse:Math.random()*Math.PI*2,pulseSpeed:Math.random()*.025+.01,colorPrefix:this.config.colors.particleColorPrefixes[Math.floor(Math.random()*this.config.colors.particleColorPrefixes.length)]}))}_onMouseMove(e){let i=this.canvas.getBoundingClientRect();i&&(this.targetMouseX=e.clientX-i.left,this.targetMouseY=e.clientY-i.top,this.mouseActive=!0)}_onMouseLeave(){this.mouseActive=!1}_onTouchMove(e){let i=e.touches[0],t=this.canvas.getBoundingClientRect();!i||!t||(this.targetMouseX=i.clientX-t.left,this.targetMouseY=i.clientY-t.top,this.mouseActive=!0)}_onTouchEnd(){this.mouseActive=!1}drawBackground(e){let i=this.config.colors.backgroundGradient,t=e.createRadialGradient(this.width/2,this.height/2,0,this.width/2,this.height/2,Math.max(this.width,this.height)*.8);i.forEach((s,o)=>t.addColorStop(o/(i.length-1||1),s)),e.fillStyle=t,e.fillRect(0,0,this.width,this.height)}drawWaves(e){let i=this.mouseActive?this.mouseX/this.width:-1,t=this.mouseActive?this.mouseY/this.height:-1;for(let s=0;s<this.config.waves.length;s++){let o=this.config.waves[s],r=Math.max(2,o.layers||2);for(let n=0;n<r;n++){let a=(1-n/r)*.5+.5;e.beginPath();let d=this.height*o.yOffset+n*3,u=this.mouseActive?(i-.5)*(o.mouseInfluence||.45)*150:0;e.moveTo(0,this.height);let f=Math.max(2,Math.floor(this.width/480));for(let h=0;h<=this.width;h+=f){let c=d;if(c+=Math.sin(h*o.frequency+this.time*o.speed+n*.1)*o.amplitude,c+=Math.sin(h*o.frequency*1.5+this.time*o.speed*.7+s)*o.amplitude*.3,this.mouseActive){let v=h-this.mouseX,w=Math.abs(v)/this.width;c+=Math.sin(w*Math.PI)*(t>0?this.mouseY-this.height/2:0)*.1*(o.mouseInfluence||.45)}c+=u*Math.sin(h/this.width*Math.PI),e.lineTo(h,c)}e.lineTo(this.width,this.height),e.closePath();let m=o.color||"rgba(139, 114, 86, 0.25)";e.fillStyle=m.replace(/([\d.]+)\)$/,h=>`${parseFloat(h)*a})`),o.lineWidth!=null&&(e.lineWidth=o.lineWidth),e.fill()}}}drawParticles(e){for(let t of this.particles){t.pulse+=t.pulseSpeed;let s=t.opacity*(Math.sin(t.pulse)*.3+.7);if(this.mouseActive){let r=t.x-this.mouseX,n=t.y-this.mouseY,a=Math.sqrt(r*r+n*n);if(a<150&&a>.1){let d=(1-a/150)*2;t.x+=r/a*d,t.y+=n/a*d}}t.x+=t.speedX,t.y+=t.speedY,t.x<0&&(t.x=this.width),t.x>this.width&&(t.x=0),t.y<0&&(t.y=this.height),t.y>this.height&&(t.y=0);let o=e.createRadialGradient(t.x,t.y,0,t.x,t.y,t.size*2);o.addColorStop(0,t.colorPrefix+s+")"),o.addColorStop(1,t.colorPrefix+"0)"),e.beginPath(),e.fillStyle=o,e.arc(t.x,t.y,t.size*2,0,Math.PI*2),e.fill(),e.beginPath(),e.fillStyle=t.colorPrefix+s*.8+")",e.arc(t.x,t.y,t.size*.5,0,Math.PI*2),e.fill()}}drawMouseGlow(e){if(!this.mouseActive)return;let i=e.createRadialGradient(this.mouseX,this.mouseY,0,this.mouseX,this.mouseY,200);this.config.colors.mouseGlowStops.forEach(s=>i.addColorStop(s.offset,s.color)),e.beginPath(),e.fillStyle=i,e.arc(this.mouseX,this.mouseY,200,0,Math.PI*2),e.fill()}animate(){this.ctx&&(this.time+=1,this.mouseX+=(this.targetMouseX-this.mouseX)*.08,this.mouseY+=(this.targetMouseY-this.mouseY)*.08,this.ctx.clearRect(0,0,this.width,this.height),this.drawBackground(this.ctx),this.drawWaves(this.ctx),this.drawParticles(this.ctx),this.drawMouseGlow(this.ctx),this.isRunning&&requestAnimationFrame(()=>this.animate()))}start(){this.isRunning||(typeof window<"u"&&(window.addEventListener("mousemove",this._onMouseMove,{passive:!0}),window.addEventListener("touchmove",this._onTouchMove,{passive:!1}),window.addEventListener("touchend",this._onTouchEnd,{passive:!0}),window.addEventListener("mouseleave",this._onMouseLeave)),this.isRunning=!0,this.animate())}stop(){this.isRunning=!1,typeof window<"u"&&(window.removeEventListener("mousemove",this._onMouseMove),window.removeEventListener("touchmove",this._onTouchMove),window.removeEventListener("touchend",this._onTouchEnd),window.removeEventListener("mouseleave",this._onMouseLeave))}destroy(){this.stop(),typeof window<"u"&&window.removeEventListener("resize",this._onResize),this.canvas&&this.canvas.id==="wave-particles-canvas"&&this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas),this.canvas=null,this.ctx=null}},p=l;export{p as default};
|
|
2
2
|
//# sourceMappingURL=wave-particles.esm.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.js"],
|
|
4
|
-
"sourcesContent": ["/**\r\n * js-waves-particles\r\n * A canvas-based wave and particle animation library with mouse-reactive effects.\r\n * Developed by Luis 'PlatinumBlade' Moniz.\r\n */\r\n\r\nclass WaveParticles {\r\n /**\r\n * Initializes a new WaveParticles animation engine.\r\n *\r\n * @param {import('./index').WaveParticlesOptions} [options] - Configuration for the engine.\r\n * @param {HTMLCanvasElement|string} [options.canvas] - The target canvas element or a CSS selector (e.g., '#bg'). If omitted, a full-screen fixed canvas is automatically created.\r\n * @param {import('./index').WaveParticlesConfig} [options.config] - Visual parameters for waves, particles, and colors.\r\n */\r\n constructor(options = {}) {\r\n const {canvas: providedCanvasOrSelector, config} = options || {};\r\n\r\n // --- State ---\r\n this.canvas = null;\r\n this.ctx = null;\r\n this.mouseX = 0;\r\n this.mouseY = 0;\r\n this.targetMouseX = -1000;\r\n this.targetMouseY = -1000;\r\n this.mouseActive = false;\r\n this.width = 0;\r\n this.height = 0;\r\n this.config = this._mergeDefaults(config || {});\r\n this.time = 0;\r\n this.isRunning = false;\r\n this.particles = [];\r\n this._resizeTimeout = null;\r\n\r\n const resolvedCanvas = typeof document !== 'undefined' && providedCanvasOrSelector ? (() => {\r\n if (typeof providedCanvasOrSelector === 'string') {\r\n return /** @type {HTMLCanvasElement|null} */ (document.querySelector(providedCanvasOrSelector));\r\n }\r\n\r\n return /** @type {HTMLCanvasElement | null} */ (providedCanvasOrSelector);\r\n })() : null;\r\n\r\n if (resolvedCanvas) {\r\n this.canvas = resolvedCanvas;\r\n const rect = resolvedCanvas.getBoundingClientRect();\r\n this.width = Math.round(rect.width || window.innerWidth);\r\n this.height = Math.round(rect.height || window.innerHeight);\r\n } else {\r\n this._createAutoCanvas();\r\n }\r\n\r\n try {\r\n if (!this.canvas) {\r\n throw new Error('No canvas element available.');\r\n }\r\n\r\n const ctx = this.canvas.getContext('2d');\r\n\r\n if (!ctx) {\r\n throw new Error('Could not get 2D rendering context.');\r\n }\r\n\r\n this.ctx = ctx;\r\n } catch (e) {\r\n console.error('[WaveParticles] Canvas error:', e);\r\n return;\r\n }\r\n\r\n this._onMouseMove = this._onMouseMove.bind(this);\r\n this._onMouseLeave = this._onMouseLeave.bind(this);\r\n this._onTouchMove = this._onTouchMove.bind(this);\r\n this._onTouchEnd = this._onTouchEnd.bind(this);\r\n this._onResize = this._onResize.bind(this);\r\n\r\n if (typeof document !== 'undefined') {\r\n window.addEventListener('resize', this._onResize);\r\n this.resize(); // Initial size setup\r\n }\r\n\r\n this.initParticles();\r\n this.start();\r\n }\r\n\r\n /**\r\n * Internal method to merge user configuration with library defaults.\r\n * @private\r\n */\r\n _mergeDefaults(userConfig) {\r\n const defaults = {\r\n waves: [\r\n {\r\n amplitude: 60,\r\n frequency: 0.003,\r\n speed: 0.006,\r\n yOffset: 0.55,\r\n color: 'rgba(248, 225, 231, 0.25)',\r\n lineWidth: 1.5,\r\n mouseInfluence: 0.45\r\n },\r\n {\r\n amplitude: 50,\r\n frequency: 0.004,\r\n speed: 0.008,\r\n yOffset: 0.60,\r\n color: 'rgba(212, 165, 165, 0.2)',\r\n lineWidth: 1,\r\n mouseInfluence: 0.5\r\n },\r\n {\r\n amplitude: 70,\r\n frequency: 0.0025,\r\n speed: 0.005,\r\n yOffset: 0.65,\r\n color: 'rgba(255, 255, 255, 0.2)',\r\n lineWidth: 1.5,\r\n mouseInfluence: 0.4\r\n },\r\n {\r\n amplitude: 40,\r\n frequency: 0.0035,\r\n speed: 0.010,\r\n yOffset: 0.70,\r\n color: 'rgba(201, 169, 110, 0.12)',\r\n lineWidth: 1,\r\n mouseInfluence: 0.55\r\n }\r\n ],\r\n particles: {countScale: 8000, maxCount: 180},\r\n colors: {\r\n backgroundGradient: ['#fdfcfb', '#f7ede2', '#e8d5d0'],\r\n particleColorPrefixes: [\r\n 'rgba(248, 225, 231,',\r\n 'rgba(212, 165, 165,',\r\n 'rgba(201, 169, 110,',\r\n 'rgba(255, 255, 255,',\r\n 'rgba(232, 213, 208,'\r\n ],\r\n mouseGlowStops: [\r\n {offset: 0, color: 'rgba(255, 255, 255, 0.08)'},\r\n {offset: 0.5, color: 'rgba(248, 225, 231, 0.04)'},\r\n {offset: 1, color: 'rgba(248, 225, 231, 0)'}\r\n ]\r\n }\r\n };\r\n\r\n const result = {};\r\n\r\n for (const key in defaults) {\r\n if (!defaults.hasOwnProperty(key)) {\r\n continue;\r\n }\r\n\r\n if (!(userConfig && userConfig[key])) {\r\n result[key] = JSON.parse(JSON.stringify(defaults[key]));\r\n } else if (typeof defaults[key] === 'object' && !Array.isArray(defaults[key]) && typeof userConfig[key] === 'object') {\r\n result[key] = Object.assign({}, defaults[key], userConfig[key]);\r\n } else if (key === 'waves') {\r\n result.waves = Array.isArray(userConfig.waves) ? [...userConfig.waves] : JSON.parse(JSON.stringify(defaults.waves));\r\n } else {\r\n result[key] = userConfig[key];\r\n }\r\n }\r\n \r\n return result;\r\n }\r\n\r\n /**\r\n * Automatically creates and injects a canvas element into the DOM if none was provided.\r\n * @private\r\n */\r\n _createAutoCanvas() {\r\n if (typeof document === 'undefined') {\r\n return;\r\n }\r\n\r\n const el = document.createElement('canvas');\r\n\r\n el.id = 'wave-particles-canvas';\r\n\r\n Object.assign(el.style, {\r\n position: 'fixed',\r\n top: '0',\r\n left: '0',\r\n width: '100%',\r\n height: '100%',\r\n zIndex: '-1',\r\n pointerEvents: 'none'\r\n });\r\n\r\n const body = document.body;\r\n\r\n if (!body) {\r\n return;\r\n }\r\n\r\n body.insertBefore(el, body.firstChild);\r\n this.canvas = el;\r\n this.width = window.innerWidth;\r\n this.height = window.innerHeight;\r\n }\r\n\r\n /**\r\n * Event handler for window resize events, including debouncing to prevent performance issues.\r\n * @private\r\n */\r\n _onResize() {\r\n if (this._resizeTimeout) clearTimeout(this._resizeTimeout);\r\n // Debounce resize to prevent flashing and high CPU usage\r\n this._resizeTimeout = setTimeout(() => {\r\n this.resize();\r\n }, 250);\r\n }\r\n\r\n /**\r\n * Recalculates canvas dimensions based on its display size and Device Pixel Ratio.\r\n * Re-initializes particles to match the new surface area.\r\n */\r\n resize() {\r\n if (!this.canvas) {\r\n return;\r\n }\r\n\r\n const rect = this.canvas.getBoundingClientRect();\r\n\r\n let w, h;\r\n\r\n const attrW = parseInt(this.canvas.getAttribute('width'), 10);\r\n const attrH = parseInt(this.canvas.getAttribute('height'), 10);\r\n\r\n if (attrW && attrH) {\r\n w = Math.round(rect.width || attrW);\r\n h = Math.round(rect.height || attrH);\r\n } else {\r\n w = window.innerWidth;\r\n h = window.innerHeight;\r\n\r\n if (!this.canvas.parentNode && document.body) {\r\n document.body.insertBefore(this.canvas, document.body.firstChild);\r\n }\r\n }\r\n \r\n this.width = w;\r\n this.height = h;\r\n\r\n const dpr = window.devicePixelRatio || 1;\r\n\r\n this.canvas.width = Math.round(w * dpr);\r\n this.canvas.height = Math.round(h * dpr);\r\n\r\n if (this.ctx && this.isRunning) {\r\n this.initParticles();\r\n }\r\n }\r\n\r\n /**\r\n * Generates a new set of particles based on the current canvas area and configuration.\r\n */\r\n initParticles() {\r\n const {countScale = 8000, maxCount = 180} = this.config.particles;\r\n\r\n const particleCount = Math.min(Math.floor((this.width * this.height) / (countScale > 0 ? countScale : 8000)), maxCount);\r\n\r\n this.particles = Array.from({length: particleCount}, () => ({\r\n x: Math.random() * this.width,\r\n y: Math.random() * this.height,\r\n size: Math.random() * 4 + 1.5,\r\n speedX: (Math.random() - 0.5) * 0.4,\r\n speedY: (Math.random() - 0.5) * 0.4,\r\n opacity: Math.random() * 0.6 + 0.3,\r\n pulse: Math.random() * Math.PI * 2,\r\n pulseSpeed: Math.random() * 0.025 + 0.01,\r\n colorPrefix: this.config.colors.particleColorPrefixes[Math.floor(Math.random() * this.config.colors.particleColorPrefixes.length)]\r\n }));\r\n }\r\n\r\n _onMouseMove(e) {\r\n const rect = this.canvas.getBoundingClientRect();\r\n \r\n if (!rect) {\r\n return;\r\n }\r\n\r\n // Calculate position relative to the canvas in CSS pixels\r\n this.targetMouseX = e.clientX - rect.left;\r\n this.targetMouseY = e.clientY - rect.top;\r\n this.mouseActive = true;\r\n }\r\n\r\n _onMouseLeave() {\r\n this.mouseActive = false;\r\n }\r\n\r\n _onTouchMove(e) {\r\n const touch = e.touches[0];\r\n const rect = this.canvas.getBoundingClientRect();\r\n \r\n if (!touch || !rect) {\r\n return;\r\n }\r\n\r\n this.targetMouseX = touch.clientX - rect.left;\r\n this.targetMouseY = touch.clientY - rect.top;\r\n this.mouseActive = true;\r\n }\r\n\r\n _onTouchEnd() {\r\n this.mouseActive = false;\r\n }\r\n\r\n /**\r\n * Renders the radial gradient background across the entire canvas.\r\n * @param {CanvasRenderingContext2D} ctx\r\n */\r\n drawBackground(ctx) {\r\n const colors = this.config.colors.backgroundGradient;\r\n \r\n const gradient = ctx.createRadialGradient(this.width / 2, this.height / 2, 0, this.width / 2, this.height / 2, Math.max(this.width, this.height) * 0.8);\r\n \r\n colors.forEach((c, i) => gradient.addColorStop(i / (colors.length - 1 || 1), c));\r\n \r\n ctx.fillStyle = gradient;\r\n \r\n ctx.fillRect(0, 0, this.width, this.height);\r\n }\r\n\r\n /**\r\n * Renders all wave layers, accounting for mouse interaction and layer offsets.\r\n * @param {CanvasRenderingContext2D} ctx\r\n */\r\n drawWaves(ctx) {\r\n const mouseNormX = this.mouseActive ? this.mouseX / this.width : -1;\r\n const mouseNormY = this.mouseActive ? this.mouseY / this.height : -1;\r\n\r\n for (let wi = 0; wi < this.config.waves.length; wi++) {\r\n const wave = this.config.waves[wi];\r\n const numLayers = Math.max(2, wave.layers || 2);\r\n\r\n for (let layer = 0; layer < numLayers; layer++) {\r\n const layerOpacity = (1 - layer / numLayers) * 0.5 + 0.5;\r\n \r\n ctx.beginPath();\r\n \r\n const baseY = this.height * wave.yOffset + layer * 3;\r\n \r\n const mouseEffect = this.mouseActive ? (mouseNormX - 0.5) * (wave.mouseInfluence || 0.45) * 150 : 0;\r\n \r\n ctx.moveTo(0, this.height);\r\n\r\n const step = Math.max(2, Math.floor(this.width / 480));\r\n \r\n for (let x = 0; x <= this.width; x += step) {\r\n let y = baseY;\r\n \r\n y += Math.sin(x * wave.frequency + this.time * wave.speed + layer * 0.1) * wave.amplitude;\r\n y += Math.sin(x * wave.frequency * 1.5 + this.time * wave.speed * 0.7 + wi) * wave.amplitude * 0.3;\r\n \r\n if (this.mouseActive) {\r\n const dx = x - this.mouseX;\r\n const dist = Math.abs(dx) / this.width;\r\n y += Math.sin(dist * Math.PI) * (mouseNormY > 0 ? (this.mouseY - this.height / 2) : 0) * 0.1 * (wave.mouseInfluence || 0.45);\r\n }\r\n \r\n y += mouseEffect * Math.sin((x / this.width) * Math.PI);\r\n \r\n ctx.lineTo(x, y);\r\n }\r\n \r\n ctx.lineTo(this.width, this.height);\r\n \r\n ctx.closePath();\r\n \r\n const baseColor = wave.color || 'rgba(139, 114, 86, 0.25)';\r\n \r\n ctx.fillStyle = baseColor.replace(/([\\d.]+)\\)$/, (m) => `${parseFloat(m) * layerOpacity})`);\r\n \r\n if (wave.lineWidth != null) {\r\n ctx.lineWidth = wave.lineWidth;\r\n }\r\n \r\n ctx.fill();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Updates and renders the particle system, including movement and mouse repulsion logic.\r\n * @param {CanvasRenderingContext2D} ctx\r\n */\r\n drawParticles(ctx) {\r\n const maxDist = 150;\r\n \r\n for (const p of this.particles) {\r\n p.pulse += p.pulseSpeed;\r\n \r\n const currentOpacity = p.opacity * (Math.sin(p.pulse) * 0.3 + 0.7);\r\n \r\n if (this.mouseActive) {\r\n const dx = p.x - this.mouseX, dy = p.y - this.mouseY, dist = Math.sqrt(dx * dx + dy * dy);\r\n \r\n if (dist < maxDist && dist > 0.1) {\r\n const force = (1 - dist / maxDist) * 2;\r\n p.x += (dx / dist) * force;\r\n p.y += (dy / dist) * force;\r\n }\r\n }\r\n \r\n p.x += p.speedX;\r\n p.y += p.speedY;\r\n \r\n if (p.x < 0) {\r\n p.x = this.width;\r\n }\r\n \r\n if (p.x > this.width) {\r\n p.x = 0;\r\n }\r\n \r\n if (p.y < 0) {\r\n p.y = this.height;\r\n }\r\n \r\n if (p.y > this.height) {\r\n p.y = 0;\r\n }\r\n\r\n const grad = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.size * 2);\r\n \r\n grad.addColorStop(0, p.colorPrefix + currentOpacity + ')');\r\n \r\n grad.addColorStop(1, p.colorPrefix + '0)');\r\n \r\n ctx.beginPath();\r\n \r\n ctx.fillStyle = grad;\r\n \r\n ctx.arc(p.x, p.y, p.size * 2, 0, Math.PI * 2);\r\n \r\n ctx.fill();\r\n \r\n ctx.beginPath();\r\n \r\n ctx.fillStyle = p.colorPrefix + (currentOpacity * 0.8) + ')';\r\n \r\n ctx.arc(p.x, p.y, p.size * 0.5, 0, Math.PI * 2);\r\n \r\n ctx.fill();\r\n }\r\n }\r\n\r\n /**\r\n * Renders a soft glow effect centered at the current mouse position.\r\n * @param {CanvasRenderingContext2D} ctx\r\n */\r\n drawMouseGlow(ctx) {\r\n if (!this.mouseActive) {\r\n return;\r\n }\r\n \r\n const gradient = ctx.createRadialGradient(this.mouseX, this.mouseY, 0, this.mouseX, this.mouseY, 200);\r\n \r\n const stops = this.config.colors.mouseGlowStops;\r\n \r\n stops.forEach((s) => gradient.addColorStop(s.offset, s.color));\r\n \r\n ctx.beginPath();\r\n \r\n ctx.fillStyle = gradient;\r\n \r\n ctx.arc(this.mouseX, this.mouseY, 200, 0, Math.PI * 2);\r\n \r\n ctx.fill();\r\n }\r\n\r\n /**\r\n * The main animation loop. Updates state and draws the next frame.\r\n */\r\n animate() {\r\n if (!this.ctx) {\r\n return;\r\n }\r\n \r\n this.time += 1;\r\n \r\n this.mouseX += (this.targetMouseX - this.mouseX) * 0.08;\r\n this.mouseY += (this.targetMouseY - this.mouseY) * 0.08;\r\n \r\n this.ctx.clearRect(0, 0, this.width, this.height);\r\n \r\n this.drawBackground(this.ctx);\r\n this.drawWaves(this.ctx);\r\n this.drawParticles(this.ctx);\r\n this.drawMouseGlow(this.ctx);\r\n \r\n if (this.isRunning) {\r\n requestAnimationFrame(() => this.animate());\r\n }\r\n }\r\n\r\n /**\r\n * Starts or resumes the animation loop and attaches interaction listeners.\r\n */\r\n start() {\r\n if (this.isRunning) {\r\n return;\r\n }\r\n \r\n if (typeof window !== 'undefined') {\r\n window.addEventListener('mousemove', this._onMouseMove, {passive: true});\r\n window.addEventListener('touchmove', this._onTouchMove, {passive: false});\r\n window.addEventListener('touchend', this._onTouchEnd, {passive: true});\r\n window.addEventListener('mouseleave', this._onMouseLeave);\r\n }\r\n \r\n this.isRunning = true;\r\n \r\n this.animate();\r\n }\r\n\r\n /**\r\n * Pauses the animation loop and detaches interaction listeners to save resources.\r\n */\r\n stop() {\r\n this.isRunning = false;\r\n \r\n if (typeof window !== 'undefined') {\r\n window.removeEventListener('mousemove', this._onMouseMove);\r\n window.removeEventListener('touchmove', this._onTouchMove);\r\n window.removeEventListener('touchend', this._onTouchEnd);\r\n window.removeEventListener('mouseleave', this._onMouseLeave);\r\n }\r\n }\r\n\r\n /**\r\n * Stops the animation, removes event listeners, and cleans up any auto-injected DOM elements.\r\n */\r\n destroy() {\r\n this.stop();\r\n \r\n if (typeof window !== 'undefined') {\r\n window.removeEventListener('resize', this._onResize);\r\n }\r\n \r\n if (this.canvas && this.canvas.id === 'wave-particles-canvas' && this.canvas.parentNode) {\r\n this.canvas.parentNode.removeChild(this.canvas);\r\n }\r\n \r\n this.canvas = null;\r\n this.ctx = null;\r\n }\r\n}\r\n\r\nexport default WaveParticles;\r\n"],
|
|
5
|
-
"mappings": "AAMA,IAAMA,EAAN,KAAoB,CAQhB,YAAYC,EAAU,CAAC,EAAG,CACtB,GAAM,
|
|
6
|
-
"names": ["WaveParticles", "options", "providedCanvasOrSelector", "config", "resolvedCanvas", "rect", "ctx", "e", "userConfig", "defaults", "result", "key", "el", "body", "
|
|
4
|
+
"sourcesContent": ["/**\r\n * js-waves-particles\r\n * A canvas-based wave and particle animation library with mouse-reactive effects.\r\n * Developed by Luis 'PlatinumBlade' Moniz.\r\n */\r\n\r\nclass WaveParticles {\r\n /**\r\n * Initializes a new WaveParticles animation engine.\r\n *\r\n * @param {import('./index').WaveParticlesOptions} [options] - Configuration for the engine.\r\n * @param {HTMLCanvasElement|string} [options.canvas] - The target canvas element or a CSS selector (e.g., '#bg'). If omitted, a full-screen fixed canvas is automatically created.\r\n * @param {import('./index').WaveParticlesConfig} [options.config] - Visual parameters for waves, particles, and colors.\r\n */\r\n constructor(options = {}) {\r\n const { canvas: providedCanvasOrSelector, config } = options || {};\r\n\r\n // --- State ---\r\n this.canvas = null;\r\n this.ctx = null;\r\n this.mouseX = 0;\r\n this.mouseY = 0;\r\n this.targetMouseX = -1000;\r\n this.targetMouseY = -1000;\r\n this.mouseActive = false;\r\n this.width = 0;\r\n this.height = 0;\r\n this.config = this._mergeDefaults(config || {});\r\n this.time = 0;\r\n this.isRunning = false;\r\n this.particles = [];\r\n this._resizeTimeout = null;\r\n\r\n const resolvedCanvas = typeof document !== 'undefined' && providedCanvasOrSelector ? (() => {\r\n if (typeof providedCanvasOrSelector === 'string') {\r\n return /** @type {HTMLCanvasElement|null} */ (document.querySelector(providedCanvasOrSelector));\r\n }\r\n\r\n return /** @type {HTMLCanvasElement | null} */ (providedCanvasOrSelector);\r\n })() : null;\r\n\r\n if (resolvedCanvas) {\r\n this.canvas = resolvedCanvas;\r\n const rect = resolvedCanvas.getBoundingClientRect();\r\n this.width = Math.round(rect.width || window.innerWidth);\r\n this.height = Math.round(rect.height || window.innerHeight);\r\n } else {\r\n this._createAutoCanvas();\r\n }\r\n\r\n try {\r\n if (!this.canvas) {\r\n throw new Error('No canvas element available.');\r\n }\r\n\r\n const ctx = this.canvas.getContext('2d');\r\n\r\n if (!ctx) {\r\n throw new Error('Could not get 2D rendering context.');\r\n }\r\n\r\n this.ctx = ctx;\r\n } catch (e) {\r\n console.error('[WaveParticles] Canvas error:', e);\r\n return;\r\n }\r\n\r\n this._onMouseMove = this._onMouseMove.bind(this);\r\n this._onMouseLeave = this._onMouseLeave.bind(this);\r\n this._onTouchMove = this._onTouchMove.bind(this);\r\n this._onTouchEnd = this._onTouchEnd.bind(this);\r\n this._onResize = this._onResize.bind(this);\r\n\r\n if (typeof document !== 'undefined') {\r\n window.addEventListener('resize', this._onResize);\r\n this.resize(); // Initial size setup\r\n }\r\n\r\n this.initParticles();\r\n this.start();\r\n }\r\n\r\n /**\r\n * Internal method to merge user configuration with library defaults.\r\n * @private\r\n */\r\n _mergeDefaults(userConfig) {\r\n const defaults = {\r\n waves: [\r\n {\r\n amplitude: 60,\r\n frequency: 0.003,\r\n speed: 0.006,\r\n yOffset: 0.55,\r\n color: 'rgba(248, 225, 231, 0.25)',\r\n lineWidth: 1.5,\r\n mouseInfluence: 0.45\r\n },\r\n {\r\n amplitude: 50,\r\n frequency: 0.004,\r\n speed: 0.008,\r\n yOffset: 0.60,\r\n color: 'rgba(212, 165, 165, 0.2)',\r\n lineWidth: 1,\r\n mouseInfluence: 0.5\r\n },\r\n {\r\n amplitude: 70,\r\n frequency: 0.0025,\r\n speed: 0.005,\r\n yOffset: 0.65,\r\n color: 'rgba(255, 255, 255, 0.2)',\r\n lineWidth: 1.5,\r\n mouseInfluence: 0.4\r\n },\r\n {\r\n amplitude: 40,\r\n frequency: 0.0035,\r\n speed: 0.010,\r\n yOffset: 0.70,\r\n color: 'rgba(201, 169, 110, 0.12)',\r\n lineWidth: 1,\r\n mouseInfluence: 0.55\r\n }\r\n ],\r\n particles: { countScale: 8000, maxCount: 180 },\r\n colors: {\r\n backgroundGradient: ['#fdfcfb', '#f7ede2', '#e8d5d0'],\r\n particleColorPrefixes: [\r\n 'rgba(248, 225, 231,',\r\n 'rgba(212, 165, 165,',\r\n 'rgba(201, 169, 110,',\r\n 'rgba(255, 255, 255,',\r\n 'rgba(232, 213, 208,'\r\n ],\r\n mouseGlowStops: [\r\n { offset: 0, color: 'rgba(255, 255, 255, 0.08)' },\r\n { offset: 0.5, color: 'rgba(248, 225, 231, 0.04)' },\r\n { offset: 1, color: 'rgba(248, 225, 231, 0)' }\r\n ]\r\n }\r\n };\r\n\r\n const result = {};\r\n\r\n for (const key in defaults) {\r\n if (!defaults.hasOwnProperty(key)) {\r\n continue;\r\n }\r\n\r\n if (!(userConfig && userConfig[key])) {\r\n result[key] = JSON.parse(JSON.stringify(defaults[key]));\r\n } else if (typeof defaults[key] === 'object' && !Array.isArray(defaults[key]) && typeof userConfig[key] === 'object') {\r\n result[key] = Object.assign({}, defaults[key], userConfig[key]);\r\n } else if (key === 'waves') {\r\n result.waves = Array.isArray(userConfig.waves) ? [...userConfig.waves] : JSON.parse(JSON.stringify(defaults.waves));\r\n } else {\r\n result[key] = userConfig[key];\r\n }\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Automatically creates and injects a canvas element into the DOM if none was provided.\r\n * @private\r\n */\r\n _createAutoCanvas() {\r\n if (typeof document === 'undefined') {\r\n return;\r\n }\r\n\r\n const el = document.createElement('canvas');\r\n\r\n el.id = 'wave-particles-canvas';\r\n\r\n Object.assign(el.style, {\r\n position: 'fixed',\r\n top: '0',\r\n left: '0',\r\n width: '100%',\r\n height: '100%',\r\n zIndex: '-1',\r\n pointerEvents: 'none'\r\n });\r\n\r\n const body = document.body;\r\n\r\n if (!body) {\r\n return;\r\n }\r\n\r\n body.insertBefore(el, body.firstChild);\r\n this.canvas = el;\r\n this.width = window.innerWidth;\r\n this.height = window.innerHeight;\r\n }\r\n\r\n /**\r\n * Event handler for window resize events, including debouncing to prevent performance issues.\r\n * @private\r\n */\r\n _onResize() {\r\n if (this._resizeTimeout) clearTimeout(this._resizeTimeout);\r\n // Debounce resize to prevent flashing and high CPU usage\r\n this._resizeTimeout = setTimeout(() => {\r\n this.resize();\r\n }, 250);\r\n }\r\n\r\n /**\r\n * Recalculates canvas dimensions based on its display size and Device Pixel Ratio.\r\n * Re-initializes particles to match the new surface area.\r\n */\r\n resize() {\r\n if (!this.canvas) {\r\n return;\r\n }\r\n\r\n const rect = this.canvas.getBoundingClientRect();\r\n const dpr = window.devicePixelRatio || 1;\r\n\r\n // Use CSS pixels for logical dimensions.\r\n // Fallback to window size if canvas is not in layout (prevents the \"multiplication\" bug).\r\n const w = rect.width || window.innerWidth;\r\n const h = rect.height || window.innerHeight;\r\n\r\n this.width = w;\r\n this.height = h;\r\n\r\n // Set physical pixels for the canvas buffer\r\n this.canvas.width = Math.round(w * dpr);\r\n this.canvas.height = Math.round(h * dpr);\r\n\r\n // Scale the context to ensure all drawing operations use CSS pixels.\r\n // Re-get context if it was lost or not yet initialized.\r\n if (!this.ctx) {\r\n this.ctx = this.canvas.getContext('2d');\r\n }\r\n\r\n if (this.ctx) {\r\n this.ctx.scale(dpr, dpr);\r\n }\r\n\r\n if (this.isRunning) {\r\n this.initParticles();\r\n }\r\n }\r\n\r\n /**\r\n * Generates a new set of particles based on the current canvas area and configuration.\r\n */\r\n initParticles() {\r\n const { countScale = 8000, maxCount = 180 } = this.config.particles;\r\n\r\n const particleCount = Math.min(Math.floor((this.width * this.height) / (countScale > 0 ? countScale : 8000)), maxCount);\r\n\r\n this.particles = Array.from({ length: particleCount }, () => ({\r\n x: Math.random() * this.width,\r\n y: Math.random() * this.height,\r\n size: Math.random() * 4 + 1.5,\r\n speedX: (Math.random() - 0.5) * 0.4,\r\n speedY: (Math.random() - 0.5) * 0.4,\r\n opacity: Math.random() * 0.6 + 0.3,\r\n pulse: Math.random() * Math.PI * 2,\r\n pulseSpeed: Math.random() * 0.025 + 0.01,\r\n colorPrefix: this.config.colors.particleColorPrefixes[Math.floor(Math.random() * this.config.colors.particleColorPrefixes.length)]\r\n }));\r\n }\r\n\r\n _onMouseMove(e) {\r\n const rect = this.canvas.getBoundingClientRect();\r\n\r\n if (!rect) {\r\n return;\r\n }\r\n\r\n // Calculate position relative to the canvas in CSS pixels\r\n this.targetMouseX = e.clientX - rect.left;\r\n this.targetMouseY = e.clientY - rect.top;\r\n this.mouseActive = true;\r\n }\r\n\r\n _onMouseLeave() {\r\n this.mouseActive = false;\r\n }\r\n\r\n _onTouchMove(e) {\r\n const touch = e.touches[0];\r\n const rect = this.canvas.getBoundingClientRect();\r\n\r\n if (!touch || !rect) {\r\n return;\r\n }\r\n\r\n this.targetMouseX = touch.clientX - rect.left;\r\n this.targetMouseY = touch.clientY - rect.top;\r\n this.mouseActive = true;\r\n }\r\n\r\n _onTouchEnd() {\r\n this.mouseActive = false;\r\n }\r\n\r\n /**\r\n * Renders the radial gradient background across the entire canvas.\r\n * @param {CanvasRenderingContext2D} ctx\r\n */\r\n drawBackground(ctx) {\r\n const colors = this.config.colors.backgroundGradient;\r\n\r\n const gradient = ctx.createRadialGradient(this.width / 2, this.height / 2, 0, this.width / 2, this.height / 2, Math.max(this.width, this.height) * 0.8);\r\n\r\n colors.forEach((c, i) => gradient.addColorStop(i / (colors.length - 1 || 1), c));\r\n\r\n ctx.fillStyle = gradient;\r\n\r\n ctx.fillRect(0, 0, this.width, this.height);\r\n }\r\n\r\n /**\r\n * Renders all wave layers, accounting for mouse interaction and layer offsets.\r\n * @param {CanvasRenderingContext2D} ctx\r\n */\r\n drawWaves(ctx) {\r\n const mouseNormX = this.mouseActive ? this.mouseX / this.width : -1;\r\n const mouseNormY = this.mouseActive ? this.mouseY / this.height : -1;\r\n\r\n for (let wi = 0; wi < this.config.waves.length; wi++) {\r\n const wave = this.config.waves[wi];\r\n const numLayers = Math.max(2, wave.layers || 2);\r\n\r\n for (let layer = 0; layer < numLayers; layer++) {\r\n const layerOpacity = (1 - layer / numLayers) * 0.5 + 0.5;\r\n\r\n ctx.beginPath();\r\n\r\n const baseY = this.height * wave.yOffset + layer * 3;\r\n\r\n const mouseEffect = this.mouseActive ? (mouseNormX - 0.5) * (wave.mouseInfluence || 0.45) * 150 : 0;\r\n\r\n ctx.moveTo(0, this.height);\r\n\r\n const step = Math.max(2, Math.floor(this.width / 480));\r\n\r\n for (let x = 0; x <= this.width; x += step) {\r\n let y = baseY;\r\n\r\n y += Math.sin(x * wave.frequency + this.time * wave.speed + layer * 0.1) * wave.amplitude;\r\n y += Math.sin(x * wave.frequency * 1.5 + this.time * wave.speed * 0.7 + wi) * wave.amplitude * 0.3;\r\n\r\n if (this.mouseActive) {\r\n const dx = x - this.mouseX;\r\n const dist = Math.abs(dx) / this.width;\r\n y += Math.sin(dist * Math.PI) * (mouseNormY > 0 ? (this.mouseY - this.height / 2) : 0) * 0.1 * (wave.mouseInfluence || 0.45);\r\n }\r\n\r\n y += mouseEffect * Math.sin((x / this.width) * Math.PI);\r\n\r\n ctx.lineTo(x, y);\r\n }\r\n\r\n ctx.lineTo(this.width, this.height);\r\n\r\n ctx.closePath();\r\n\r\n const baseColor = wave.color || 'rgba(139, 114, 86, 0.25)';\r\n\r\n ctx.fillStyle = baseColor.replace(/([\\d.]+)\\)$/, (m) => `${parseFloat(m) * layerOpacity})`);\r\n\r\n if (wave.lineWidth != null) {\r\n ctx.lineWidth = wave.lineWidth;\r\n }\r\n\r\n ctx.fill();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Updates and renders the particle system, including movement and mouse repulsion logic.\r\n * @param {CanvasRenderingContext2D} ctx\r\n */\r\n drawParticles(ctx) {\r\n const maxDist = 150;\r\n\r\n for (const p of this.particles) {\r\n p.pulse += p.pulseSpeed;\r\n\r\n const currentOpacity = p.opacity * (Math.sin(p.pulse) * 0.3 + 0.7);\r\n\r\n if (this.mouseActive) {\r\n const dx = p.x - this.mouseX, dy = p.y - this.mouseY, dist = Math.sqrt(dx * dx + dy * dy);\r\n\r\n if (dist < maxDist && dist > 0.1) {\r\n const force = (1 - dist / maxDist) * 2;\r\n p.x += (dx / dist) * force;\r\n p.y += (dy / dist) * force;\r\n }\r\n }\r\n\r\n p.x += p.speedX;\r\n p.y += p.speedY;\r\n\r\n if (p.x < 0) {\r\n p.x = this.width;\r\n }\r\n\r\n if (p.x > this.width) {\r\n p.x = 0;\r\n }\r\n\r\n if (p.y < 0) {\r\n p.y = this.height;\r\n }\r\n\r\n if (p.y > this.height) {\r\n p.y = 0;\r\n }\r\n\r\n const grad = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.size * 2);\r\n\r\n grad.addColorStop(0, p.colorPrefix + currentOpacity + ')');\r\n\r\n grad.addColorStop(1, p.colorPrefix + '0)');\r\n\r\n ctx.beginPath();\r\n\r\n ctx.fillStyle = grad;\r\n\r\n ctx.arc(p.x, p.y, p.size * 2, 0, Math.PI * 2);\r\n\r\n ctx.fill();\r\n\r\n ctx.beginPath();\r\n\r\n ctx.fillStyle = p.colorPrefix + (currentOpacity * 0.8) + ')';\r\n\r\n ctx.arc(p.x, p.y, p.size * 0.5, 0, Math.PI * 2);\r\n\r\n ctx.fill();\r\n }\r\n }\r\n\r\n /**\r\n * Renders a soft glow effect centered at the current mouse position.\r\n * @param {CanvasRenderingContext2D} ctx\r\n */\r\n drawMouseGlow(ctx) {\r\n if (!this.mouseActive) {\r\n return;\r\n }\r\n\r\n const gradient = ctx.createRadialGradient(this.mouseX, this.mouseY, 0, this.mouseX, this.mouseY, 200);\r\n\r\n const stops = this.config.colors.mouseGlowStops;\r\n\r\n stops.forEach((s) => gradient.addColorStop(s.offset, s.color));\r\n\r\n ctx.beginPath();\r\n\r\n ctx.fillStyle = gradient;\r\n\r\n ctx.arc(this.mouseX, this.mouseY, 200, 0, Math.PI * 2);\r\n\r\n ctx.fill();\r\n }\r\n\r\n /**\r\n * The main animation loop. Updates state and draws the next frame.\r\n */\r\n animate() {\r\n if (!this.ctx) {\r\n return;\r\n }\r\n\r\n this.time += 1;\r\n\r\n this.mouseX += (this.targetMouseX - this.mouseX) * 0.08;\r\n this.mouseY += (this.targetMouseY - this.mouseY) * 0.08;\r\n\r\n this.ctx.clearRect(0, 0, this.width, this.height);\r\n\r\n this.drawBackground(this.ctx);\r\n this.drawWaves(this.ctx);\r\n this.drawParticles(this.ctx);\r\n this.drawMouseGlow(this.ctx);\r\n\r\n if (this.isRunning) {\r\n requestAnimationFrame(() => this.animate());\r\n }\r\n }\r\n\r\n /**\r\n * Starts or resumes the animation loop and attaches interaction listeners.\r\n */\r\n start() {\r\n if (this.isRunning) {\r\n return;\r\n }\r\n\r\n if (typeof window !== 'undefined') {\r\n window.addEventListener('mousemove', this._onMouseMove, { passive: true });\r\n window.addEventListener('touchmove', this._onTouchMove, { passive: false });\r\n window.addEventListener('touchend', this._onTouchEnd, { passive: true });\r\n window.addEventListener('mouseleave', this._onMouseLeave);\r\n }\r\n\r\n this.isRunning = true;\r\n\r\n this.animate();\r\n }\r\n\r\n /**\r\n * Pauses the animation loop and detaches interaction listeners to save resources.\r\n */\r\n stop() {\r\n this.isRunning = false;\r\n\r\n if (typeof window !== 'undefined') {\r\n window.removeEventListener('mousemove', this._onMouseMove);\r\n window.removeEventListener('touchmove', this._onTouchMove);\r\n window.removeEventListener('touchend', this._onTouchEnd);\r\n window.removeEventListener('mouseleave', this._onMouseLeave);\r\n }\r\n }\r\n\r\n /**\r\n * Stops the animation, removes event listeners, and cleans up any auto-injected DOM elements.\r\n */\r\n destroy() {\r\n this.stop();\r\n\r\n if (typeof window !== 'undefined') {\r\n window.removeEventListener('resize', this._onResize);\r\n }\r\n\r\n if (this.canvas && this.canvas.id === 'wave-particles-canvas' && this.canvas.parentNode) {\r\n this.canvas.parentNode.removeChild(this.canvas);\r\n }\r\n\r\n this.canvas = null;\r\n this.ctx = null;\r\n }\r\n}\r\n\r\nexport default WaveParticles;\r\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["WaveParticles", "options", "providedCanvasOrSelector", "config", "resolvedCanvas", "rect", "ctx", "e", "userConfig", "defaults", "result", "key", "el", "body", "dpr", "w", "h", "countScale", "maxCount", "particleCount", "touch", "colors", "gradient", "c", "i", "mouseNormX", "mouseNormY", "wi", "wave", "numLayers", "layer", "layerOpacity", "baseY", "mouseEffect", "step", "x", "y", "dx", "dist", "baseColor", "m", "p", "currentOpacity", "dy", "force", "grad", "src_default"]
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(()=>{var l=class{constructor(e={}){let{canvas:i,config:t}=e||{};this.canvas=null,this.ctx=null,this.mouseX=0,this.mouseY=0,this.targetMouseX=-1e3,this.targetMouseY=-1e3,this.mouseActive=!1,this.width=0,this.height=0,this.config=this._mergeDefaults(t||{}),this.time=0,this.isRunning=!1,this.particles=[],this._resizeTimeout=null;let s=typeof document<"u"&&i?typeof i=="string"?document.querySelector(i):i:null;if(s){this.canvas=s;let o=s.getBoundingClientRect();this.width=Math.round(o.width||window.innerWidth),this.height=Math.round(o.height||window.innerHeight)}else this._createAutoCanvas();try{if(!this.canvas)throw new Error("No canvas element available.");let o=this.canvas.getContext("2d");if(!o)throw new Error("Could not get 2D rendering context.");this.ctx=o}catch(o){console.error("[WaveParticles] Canvas error:",o);return}this._onMouseMove=this._onMouseMove.bind(this),this._onMouseLeave=this._onMouseLeave.bind(this),this._onTouchMove=this._onTouchMove.bind(this),this._onTouchEnd=this._onTouchEnd.bind(this),this._onResize=this._onResize.bind(this),typeof document<"u"&&(window.addEventListener("resize",this._onResize),this.resize()),this.initParticles(),this.start()}_mergeDefaults(e){let i={waves:[{amplitude:60,frequency:.003,speed:.006,yOffset:.55,color:"rgba(248, 225, 231, 0.25)",lineWidth:1.5,mouseInfluence:.45},{amplitude:50,frequency:.004,speed:.008,yOffset:.6,color:"rgba(212, 165, 165, 0.2)",lineWidth:1,mouseInfluence:.5},{amplitude:70,frequency:.0025,speed:.005,yOffset:.65,color:"rgba(255, 255, 255, 0.2)",lineWidth:1.5,mouseInfluence:.4},{amplitude:40,frequency:.0035,speed:.01,yOffset:.7,color:"rgba(201, 169, 110, 0.12)",lineWidth:1,mouseInfluence:.55}],particles:{countScale:8e3,maxCount:180},colors:{backgroundGradient:["#fdfcfb","#f7ede2","#e8d5d0"],particleColorPrefixes:["rgba(248, 225, 231,","rgba(212, 165, 165,","rgba(201, 169, 110,","rgba(255, 255, 255,","rgba(232, 213, 208,"],mouseGlowStops:[{offset:0,color:"rgba(255, 255, 255, 0.08)"},{offset:.5,color:"rgba(248, 225, 231, 0.04)"},{offset:1,color:"rgba(248, 225, 231, 0)"}]}},t={};for(let s in i)i.hasOwnProperty(s)&&(e&&e[s]?typeof i[s]=="object"&&!Array.isArray(i[s])&&typeof e[s]=="object"?t[s]=Object.assign({},i[s],e[s]):s==="waves"?t.waves=Array.isArray(e.waves)?[...e.waves]:JSON.parse(JSON.stringify(i.waves)):t[s]=e[s]:t[s]=JSON.parse(JSON.stringify(i[s])));return t}_createAutoCanvas(){if(typeof document>"u")return;let e=document.createElement("canvas");e.id="wave-particles-canvas",Object.assign(e.style,{position:"fixed",top:"0",left:"0",width:"100%",height:"100%",zIndex:"-1",pointerEvents:"none"});let i=document.body;i&&(i.insertBefore(e,i.firstChild),this.canvas=e,this.width=window.innerWidth,this.height=window.innerHeight)}_onResize(){this._resizeTimeout&&clearTimeout(this._resizeTimeout),this._resizeTimeout=setTimeout(()=>{this.resize()},250)}resize(){if(!this.canvas)return;let e=this.canvas.getBoundingClientRect(),i
|
|
1
|
+
(()=>{var l=class{constructor(e={}){let{canvas:i,config:t}=e||{};this.canvas=null,this.ctx=null,this.mouseX=0,this.mouseY=0,this.targetMouseX=-1e3,this.targetMouseY=-1e3,this.mouseActive=!1,this.width=0,this.height=0,this.config=this._mergeDefaults(t||{}),this.time=0,this.isRunning=!1,this.particles=[],this._resizeTimeout=null;let s=typeof document<"u"&&i?typeof i=="string"?document.querySelector(i):i:null;if(s){this.canvas=s;let o=s.getBoundingClientRect();this.width=Math.round(o.width||window.innerWidth),this.height=Math.round(o.height||window.innerHeight)}else this._createAutoCanvas();try{if(!this.canvas)throw new Error("No canvas element available.");let o=this.canvas.getContext("2d");if(!o)throw new Error("Could not get 2D rendering context.");this.ctx=o}catch(o){console.error("[WaveParticles] Canvas error:",o);return}this._onMouseMove=this._onMouseMove.bind(this),this._onMouseLeave=this._onMouseLeave.bind(this),this._onTouchMove=this._onTouchMove.bind(this),this._onTouchEnd=this._onTouchEnd.bind(this),this._onResize=this._onResize.bind(this),typeof document<"u"&&(window.addEventListener("resize",this._onResize),this.resize()),this.initParticles(),this.start()}_mergeDefaults(e){let i={waves:[{amplitude:60,frequency:.003,speed:.006,yOffset:.55,color:"rgba(248, 225, 231, 0.25)",lineWidth:1.5,mouseInfluence:.45},{amplitude:50,frequency:.004,speed:.008,yOffset:.6,color:"rgba(212, 165, 165, 0.2)",lineWidth:1,mouseInfluence:.5},{amplitude:70,frequency:.0025,speed:.005,yOffset:.65,color:"rgba(255, 255, 255, 0.2)",lineWidth:1.5,mouseInfluence:.4},{amplitude:40,frequency:.0035,speed:.01,yOffset:.7,color:"rgba(201, 169, 110, 0.12)",lineWidth:1,mouseInfluence:.55}],particles:{countScale:8e3,maxCount:180},colors:{backgroundGradient:["#fdfcfb","#f7ede2","#e8d5d0"],particleColorPrefixes:["rgba(248, 225, 231,","rgba(212, 165, 165,","rgba(201, 169, 110,","rgba(255, 255, 255,","rgba(232, 213, 208,"],mouseGlowStops:[{offset:0,color:"rgba(255, 255, 255, 0.08)"},{offset:.5,color:"rgba(248, 225, 231, 0.04)"},{offset:1,color:"rgba(248, 225, 231, 0)"}]}},t={};for(let s in i)i.hasOwnProperty(s)&&(e&&e[s]?typeof i[s]=="object"&&!Array.isArray(i[s])&&typeof e[s]=="object"?t[s]=Object.assign({},i[s],e[s]):s==="waves"?t.waves=Array.isArray(e.waves)?[...e.waves]:JSON.parse(JSON.stringify(i.waves)):t[s]=e[s]:t[s]=JSON.parse(JSON.stringify(i[s])));return t}_createAutoCanvas(){if(typeof document>"u")return;let e=document.createElement("canvas");e.id="wave-particles-canvas",Object.assign(e.style,{position:"fixed",top:"0",left:"0",width:"100%",height:"100%",zIndex:"-1",pointerEvents:"none"});let i=document.body;i&&(i.insertBefore(e,i.firstChild),this.canvas=e,this.width=window.innerWidth,this.height=window.innerHeight)}_onResize(){this._resizeTimeout&&clearTimeout(this._resizeTimeout),this._resizeTimeout=setTimeout(()=>{this.resize()},250)}resize(){if(!this.canvas)return;let e=this.canvas.getBoundingClientRect(),i=window.devicePixelRatio||1,t=e.width||window.innerWidth,s=e.height||window.innerHeight;this.width=t,this.height=s,this.canvas.width=Math.round(t*i),this.canvas.height=Math.round(s*i),this.ctx||(this.ctx=this.canvas.getContext("2d")),this.ctx&&this.ctx.scale(i,i),this.isRunning&&this.initParticles()}initParticles(){let{countScale:e=8e3,maxCount:i=180}=this.config.particles,t=Math.min(Math.floor(this.width*this.height/(e>0?e:8e3)),i);this.particles=Array.from({length:t},()=>({x:Math.random()*this.width,y:Math.random()*this.height,size:Math.random()*4+1.5,speedX:(Math.random()-.5)*.4,speedY:(Math.random()-.5)*.4,opacity:Math.random()*.6+.3,pulse:Math.random()*Math.PI*2,pulseSpeed:Math.random()*.025+.01,colorPrefix:this.config.colors.particleColorPrefixes[Math.floor(Math.random()*this.config.colors.particleColorPrefixes.length)]}))}_onMouseMove(e){let i=this.canvas.getBoundingClientRect();i&&(this.targetMouseX=e.clientX-i.left,this.targetMouseY=e.clientY-i.top,this.mouseActive=!0)}_onMouseLeave(){this.mouseActive=!1}_onTouchMove(e){let i=e.touches[0],t=this.canvas.getBoundingClientRect();!i||!t||(this.targetMouseX=i.clientX-t.left,this.targetMouseY=i.clientY-t.top,this.mouseActive=!0)}_onTouchEnd(){this.mouseActive=!1}drawBackground(e){let i=this.config.colors.backgroundGradient,t=e.createRadialGradient(this.width/2,this.height/2,0,this.width/2,this.height/2,Math.max(this.width,this.height)*.8);i.forEach((s,o)=>t.addColorStop(o/(i.length-1||1),s)),e.fillStyle=t,e.fillRect(0,0,this.width,this.height)}drawWaves(e){let i=this.mouseActive?this.mouseX/this.width:-1,t=this.mouseActive?this.mouseY/this.height:-1;for(let s=0;s<this.config.waves.length;s++){let o=this.config.waves[s],r=Math.max(2,o.layers||2);for(let n=0;n<r;n++){let a=(1-n/r)*.5+.5;e.beginPath();let d=this.height*o.yOffset+n*3,f=this.mouseActive?(i-.5)*(o.mouseInfluence||.45)*150:0;e.moveTo(0,this.height);let m=Math.max(2,Math.floor(this.width/480));for(let h=0;h<=this.width;h+=m){let c=d;if(c+=Math.sin(h*o.frequency+this.time*o.speed+n*.1)*o.amplitude,c+=Math.sin(h*o.frequency*1.5+this.time*o.speed*.7+s)*o.amplitude*.3,this.mouseActive){let w=h-this.mouseX,g=Math.abs(w)/this.width;c+=Math.sin(g*Math.PI)*(t>0?this.mouseY-this.height/2:0)*.1*(o.mouseInfluence||.45)}c+=f*Math.sin(h/this.width*Math.PI),e.lineTo(h,c)}e.lineTo(this.width,this.height),e.closePath();let v=o.color||"rgba(139, 114, 86, 0.25)";e.fillStyle=v.replace(/([\d.]+)\)$/,h=>`${parseFloat(h)*a})`),o.lineWidth!=null&&(e.lineWidth=o.lineWidth),e.fill()}}}drawParticles(e){for(let t of this.particles){t.pulse+=t.pulseSpeed;let s=t.opacity*(Math.sin(t.pulse)*.3+.7);if(this.mouseActive){let r=t.x-this.mouseX,n=t.y-this.mouseY,a=Math.sqrt(r*r+n*n);if(a<150&&a>.1){let d=(1-a/150)*2;t.x+=r/a*d,t.y+=n/a*d}}t.x+=t.speedX,t.y+=t.speedY,t.x<0&&(t.x=this.width),t.x>this.width&&(t.x=0),t.y<0&&(t.y=this.height),t.y>this.height&&(t.y=0);let o=e.createRadialGradient(t.x,t.y,0,t.x,t.y,t.size*2);o.addColorStop(0,t.colorPrefix+s+")"),o.addColorStop(1,t.colorPrefix+"0)"),e.beginPath(),e.fillStyle=o,e.arc(t.x,t.y,t.size*2,0,Math.PI*2),e.fill(),e.beginPath(),e.fillStyle=t.colorPrefix+s*.8+")",e.arc(t.x,t.y,t.size*.5,0,Math.PI*2),e.fill()}}drawMouseGlow(e){if(!this.mouseActive)return;let i=e.createRadialGradient(this.mouseX,this.mouseY,0,this.mouseX,this.mouseY,200);this.config.colors.mouseGlowStops.forEach(s=>i.addColorStop(s.offset,s.color)),e.beginPath(),e.fillStyle=i,e.arc(this.mouseX,this.mouseY,200,0,Math.PI*2),e.fill()}animate(){this.ctx&&(this.time+=1,this.mouseX+=(this.targetMouseX-this.mouseX)*.08,this.mouseY+=(this.targetMouseY-this.mouseY)*.08,this.ctx.clearRect(0,0,this.width,this.height),this.drawBackground(this.ctx),this.drawWaves(this.ctx),this.drawParticles(this.ctx),this.drawMouseGlow(this.ctx),this.isRunning&&requestAnimationFrame(()=>this.animate()))}start(){this.isRunning||(typeof window<"u"&&(window.addEventListener("mousemove",this._onMouseMove,{passive:!0}),window.addEventListener("touchmove",this._onTouchMove,{passive:!1}),window.addEventListener("touchend",this._onTouchEnd,{passive:!0}),window.addEventListener("mouseleave",this._onMouseLeave)),this.isRunning=!0,this.animate())}stop(){this.isRunning=!1,typeof window<"u"&&(window.removeEventListener("mousemove",this._onMouseMove),window.removeEventListener("touchmove",this._onTouchMove),window.removeEventListener("touchend",this._onTouchEnd),window.removeEventListener("mouseleave",this._onMouseLeave))}destroy(){this.stop(),typeof window<"u"&&window.removeEventListener("resize",this._onResize),this.canvas&&this.canvas.id==="wave-particles-canvas"&&this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas),this.canvas=null,this.ctx=null}},u=l;typeof window<"u"?window.WaveParticles=u:typeof globalThis<"u"&&(globalThis.WaveParticles=u);})();
|
|
2
2
|
//# sourceMappingURL=wave-particles.iife.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.js", "../src/browser.js"],
|
|
4
|
-
"sourcesContent": ["/**\r\n * js-waves-particles\r\n * A canvas-based wave and particle animation library with mouse-reactive effects.\r\n * Developed by Luis 'PlatinumBlade' Moniz.\r\n */\r\n\r\nclass WaveParticles {\r\n /**\r\n * Initializes a new WaveParticles animation engine.\r\n *\r\n * @param {import('./index').WaveParticlesOptions} [options] - Configuration for the engine.\r\n * @param {HTMLCanvasElement|string} [options.canvas] - The target canvas element or a CSS selector (e.g., '#bg'). If omitted, a full-screen fixed canvas is automatically created.\r\n * @param {import('./index').WaveParticlesConfig} [options.config] - Visual parameters for waves, particles, and colors.\r\n */\r\n constructor(options = {}) {\r\n const {canvas: providedCanvasOrSelector, config} = options || {};\r\n\r\n // --- State ---\r\n this.canvas = null;\r\n this.ctx = null;\r\n this.mouseX = 0;\r\n this.mouseY = 0;\r\n this.targetMouseX = -1000;\r\n this.targetMouseY = -1000;\r\n this.mouseActive = false;\r\n this.width = 0;\r\n this.height = 0;\r\n this.config = this._mergeDefaults(config || {});\r\n this.time = 0;\r\n this.isRunning = false;\r\n this.particles = [];\r\n this._resizeTimeout = null;\r\n\r\n const resolvedCanvas = typeof document !== 'undefined' && providedCanvasOrSelector ? (() => {\r\n if (typeof providedCanvasOrSelector === 'string') {\r\n return /** @type {HTMLCanvasElement|null} */ (document.querySelector(providedCanvasOrSelector));\r\n }\r\n\r\n return /** @type {HTMLCanvasElement | null} */ (providedCanvasOrSelector);\r\n })() : null;\r\n\r\n if (resolvedCanvas) {\r\n this.canvas = resolvedCanvas;\r\n const rect = resolvedCanvas.getBoundingClientRect();\r\n this.width = Math.round(rect.width || window.innerWidth);\r\n this.height = Math.round(rect.height || window.innerHeight);\r\n } else {\r\n this._createAutoCanvas();\r\n }\r\n\r\n try {\r\n if (!this.canvas) {\r\n throw new Error('No canvas element available.');\r\n }\r\n\r\n const ctx = this.canvas.getContext('2d');\r\n\r\n if (!ctx) {\r\n throw new Error('Could not get 2D rendering context.');\r\n }\r\n\r\n this.ctx = ctx;\r\n } catch (e) {\r\n console.error('[WaveParticles] Canvas error:', e);\r\n return;\r\n }\r\n\r\n this._onMouseMove = this._onMouseMove.bind(this);\r\n this._onMouseLeave = this._onMouseLeave.bind(this);\r\n this._onTouchMove = this._onTouchMove.bind(this);\r\n this._onTouchEnd = this._onTouchEnd.bind(this);\r\n this._onResize = this._onResize.bind(this);\r\n\r\n if (typeof document !== 'undefined') {\r\n window.addEventListener('resize', this._onResize);\r\n this.resize(); // Initial size setup\r\n }\r\n\r\n this.initParticles();\r\n this.start();\r\n }\r\n\r\n /**\r\n * Internal method to merge user configuration with library defaults.\r\n * @private\r\n */\r\n _mergeDefaults(userConfig) {\r\n const defaults = {\r\n waves: [\r\n {\r\n amplitude: 60,\r\n frequency: 0.003,\r\n speed: 0.006,\r\n yOffset: 0.55,\r\n color: 'rgba(248, 225, 231, 0.25)',\r\n lineWidth: 1.5,\r\n mouseInfluence: 0.45\r\n },\r\n {\r\n amplitude: 50,\r\n frequency: 0.004,\r\n speed: 0.008,\r\n yOffset: 0.60,\r\n color: 'rgba(212, 165, 165, 0.2)',\r\n lineWidth: 1,\r\n mouseInfluence: 0.5\r\n },\r\n {\r\n amplitude: 70,\r\n frequency: 0.0025,\r\n speed: 0.005,\r\n yOffset: 0.65,\r\n color: 'rgba(255, 255, 255, 0.2)',\r\n lineWidth: 1.5,\r\n mouseInfluence: 0.4\r\n },\r\n {\r\n amplitude: 40,\r\n frequency: 0.0035,\r\n speed: 0.010,\r\n yOffset: 0.70,\r\n color: 'rgba(201, 169, 110, 0.12)',\r\n lineWidth: 1,\r\n mouseInfluence: 0.55\r\n }\r\n ],\r\n particles: {countScale: 8000, maxCount: 180},\r\n colors: {\r\n backgroundGradient: ['#fdfcfb', '#f7ede2', '#e8d5d0'],\r\n particleColorPrefixes: [\r\n 'rgba(248, 225, 231,',\r\n 'rgba(212, 165, 165,',\r\n 'rgba(201, 169, 110,',\r\n 'rgba(255, 255, 255,',\r\n 'rgba(232, 213, 208,'\r\n ],\r\n mouseGlowStops: [\r\n {offset: 0, color: 'rgba(255, 255, 255, 0.08)'},\r\n {offset: 0.5, color: 'rgba(248, 225, 231, 0.04)'},\r\n {offset: 1, color: 'rgba(248, 225, 231, 0)'}\r\n ]\r\n }\r\n };\r\n\r\n const result = {};\r\n\r\n for (const key in defaults) {\r\n if (!defaults.hasOwnProperty(key)) {\r\n continue;\r\n }\r\n\r\n if (!(userConfig && userConfig[key])) {\r\n result[key] = JSON.parse(JSON.stringify(defaults[key]));\r\n } else if (typeof defaults[key] === 'object' && !Array.isArray(defaults[key]) && typeof userConfig[key] === 'object') {\r\n result[key] = Object.assign({}, defaults[key], userConfig[key]);\r\n } else if (key === 'waves') {\r\n result.waves = Array.isArray(userConfig.waves) ? [...userConfig.waves] : JSON.parse(JSON.stringify(defaults.waves));\r\n } else {\r\n result[key] = userConfig[key];\r\n }\r\n }\r\n \r\n return result;\r\n }\r\n\r\n /**\r\n * Automatically creates and injects a canvas element into the DOM if none was provided.\r\n * @private\r\n */\r\n _createAutoCanvas() {\r\n if (typeof document === 'undefined') {\r\n return;\r\n }\r\n\r\n const el = document.createElement('canvas');\r\n\r\n el.id = 'wave-particles-canvas';\r\n\r\n Object.assign(el.style, {\r\n position: 'fixed',\r\n top: '0',\r\n left: '0',\r\n width: '100%',\r\n height: '100%',\r\n zIndex: '-1',\r\n pointerEvents: 'none'\r\n });\r\n\r\n const body = document.body;\r\n\r\n if (!body) {\r\n return;\r\n }\r\n\r\n body.insertBefore(el, body.firstChild);\r\n this.canvas = el;\r\n this.width = window.innerWidth;\r\n this.height = window.innerHeight;\r\n }\r\n\r\n /**\r\n * Event handler for window resize events, including debouncing to prevent performance issues.\r\n * @private\r\n */\r\n _onResize() {\r\n if (this._resizeTimeout) clearTimeout(this._resizeTimeout);\r\n // Debounce resize to prevent flashing and high CPU usage\r\n this._resizeTimeout = setTimeout(() => {\r\n this.resize();\r\n }, 250);\r\n }\r\n\r\n /**\r\n * Recalculates canvas dimensions based on its display size and Device Pixel Ratio.\r\n * Re-initializes particles to match the new surface area.\r\n */\r\n resize() {\r\n if (!this.canvas) {\r\n return;\r\n }\r\n\r\n const rect = this.canvas.getBoundingClientRect();\r\n\r\n let w, h;\r\n\r\n const attrW = parseInt(this.canvas.getAttribute('width'), 10);\r\n const attrH = parseInt(this.canvas.getAttribute('height'), 10);\r\n\r\n if (attrW && attrH) {\r\n w = Math.round(rect.width || attrW);\r\n h = Math.round(rect.height || attrH);\r\n } else {\r\n w = window.innerWidth;\r\n h = window.innerHeight;\r\n\r\n if (!this.canvas.parentNode && document.body) {\r\n document.body.insertBefore(this.canvas, document.body.firstChild);\r\n }\r\n }\r\n \r\n this.width = w;\r\n this.height = h;\r\n\r\n const dpr = window.devicePixelRatio || 1;\r\n\r\n this.canvas.width = Math.round(w * dpr);\r\n this.canvas.height = Math.round(h * dpr);\r\n\r\n if (this.ctx && this.isRunning) {\r\n this.initParticles();\r\n }\r\n }\r\n\r\n /**\r\n * Generates a new set of particles based on the current canvas area and configuration.\r\n */\r\n initParticles() {\r\n const {countScale = 8000, maxCount = 180} = this.config.particles;\r\n\r\n const particleCount = Math.min(Math.floor((this.width * this.height) / (countScale > 0 ? countScale : 8000)), maxCount);\r\n\r\n this.particles = Array.from({length: particleCount}, () => ({\r\n x: Math.random() * this.width,\r\n y: Math.random() * this.height,\r\n size: Math.random() * 4 + 1.5,\r\n speedX: (Math.random() - 0.5) * 0.4,\r\n speedY: (Math.random() - 0.5) * 0.4,\r\n opacity: Math.random() * 0.6 + 0.3,\r\n pulse: Math.random() * Math.PI * 2,\r\n pulseSpeed: Math.random() * 0.025 + 0.01,\r\n colorPrefix: this.config.colors.particleColorPrefixes[Math.floor(Math.random() * this.config.colors.particleColorPrefixes.length)]\r\n }));\r\n }\r\n\r\n _onMouseMove(e) {\r\n const rect = this.canvas.getBoundingClientRect();\r\n \r\n if (!rect) {\r\n return;\r\n }\r\n\r\n // Calculate position relative to the canvas in CSS pixels\r\n this.targetMouseX = e.clientX - rect.left;\r\n this.targetMouseY = e.clientY - rect.top;\r\n this.mouseActive = true;\r\n }\r\n\r\n _onMouseLeave() {\r\n this.mouseActive = false;\r\n }\r\n\r\n _onTouchMove(e) {\r\n const touch = e.touches[0];\r\n const rect = this.canvas.getBoundingClientRect();\r\n \r\n if (!touch || !rect) {\r\n return;\r\n }\r\n\r\n this.targetMouseX = touch.clientX - rect.left;\r\n this.targetMouseY = touch.clientY - rect.top;\r\n this.mouseActive = true;\r\n }\r\n\r\n _onTouchEnd() {\r\n this.mouseActive = false;\r\n }\r\n\r\n /**\r\n * Renders the radial gradient background across the entire canvas.\r\n * @param {CanvasRenderingContext2D} ctx\r\n */\r\n drawBackground(ctx) {\r\n const colors = this.config.colors.backgroundGradient;\r\n \r\n const gradient = ctx.createRadialGradient(this.width / 2, this.height / 2, 0, this.width / 2, this.height / 2, Math.max(this.width, this.height) * 0.8);\r\n \r\n colors.forEach((c, i) => gradient.addColorStop(i / (colors.length - 1 || 1), c));\r\n \r\n ctx.fillStyle = gradient;\r\n \r\n ctx.fillRect(0, 0, this.width, this.height);\r\n }\r\n\r\n /**\r\n * Renders all wave layers, accounting for mouse interaction and layer offsets.\r\n * @param {CanvasRenderingContext2D} ctx\r\n */\r\n drawWaves(ctx) {\r\n const mouseNormX = this.mouseActive ? this.mouseX / this.width : -1;\r\n const mouseNormY = this.mouseActive ? this.mouseY / this.height : -1;\r\n\r\n for (let wi = 0; wi < this.config.waves.length; wi++) {\r\n const wave = this.config.waves[wi];\r\n const numLayers = Math.max(2, wave.layers || 2);\r\n\r\n for (let layer = 0; layer < numLayers; layer++) {\r\n const layerOpacity = (1 - layer / numLayers) * 0.5 + 0.5;\r\n \r\n ctx.beginPath();\r\n \r\n const baseY = this.height * wave.yOffset + layer * 3;\r\n \r\n const mouseEffect = this.mouseActive ? (mouseNormX - 0.5) * (wave.mouseInfluence || 0.45) * 150 : 0;\r\n \r\n ctx.moveTo(0, this.height);\r\n\r\n const step = Math.max(2, Math.floor(this.width / 480));\r\n \r\n for (let x = 0; x <= this.width; x += step) {\r\n let y = baseY;\r\n \r\n y += Math.sin(x * wave.frequency + this.time * wave.speed + layer * 0.1) * wave.amplitude;\r\n y += Math.sin(x * wave.frequency * 1.5 + this.time * wave.speed * 0.7 + wi) * wave.amplitude * 0.3;\r\n \r\n if (this.mouseActive) {\r\n const dx = x - this.mouseX;\r\n const dist = Math.abs(dx) / this.width;\r\n y += Math.sin(dist * Math.PI) * (mouseNormY > 0 ? (this.mouseY - this.height / 2) : 0) * 0.1 * (wave.mouseInfluence || 0.45);\r\n }\r\n \r\n y += mouseEffect * Math.sin((x / this.width) * Math.PI);\r\n \r\n ctx.lineTo(x, y);\r\n }\r\n \r\n ctx.lineTo(this.width, this.height);\r\n \r\n ctx.closePath();\r\n \r\n const baseColor = wave.color || 'rgba(139, 114, 86, 0.25)';\r\n \r\n ctx.fillStyle = baseColor.replace(/([\\d.]+)\\)$/, (m) => `${parseFloat(m) * layerOpacity})`);\r\n \r\n if (wave.lineWidth != null) {\r\n ctx.lineWidth = wave.lineWidth;\r\n }\r\n \r\n ctx.fill();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Updates and renders the particle system, including movement and mouse repulsion logic.\r\n * @param {CanvasRenderingContext2D} ctx\r\n */\r\n drawParticles(ctx) {\r\n const maxDist = 150;\r\n \r\n for (const p of this.particles) {\r\n p.pulse += p.pulseSpeed;\r\n \r\n const currentOpacity = p.opacity * (Math.sin(p.pulse) * 0.3 + 0.7);\r\n \r\n if (this.mouseActive) {\r\n const dx = p.x - this.mouseX, dy = p.y - this.mouseY, dist = Math.sqrt(dx * dx + dy * dy);\r\n \r\n if (dist < maxDist && dist > 0.1) {\r\n const force = (1 - dist / maxDist) * 2;\r\n p.x += (dx / dist) * force;\r\n p.y += (dy / dist) * force;\r\n }\r\n }\r\n \r\n p.x += p.speedX;\r\n p.y += p.speedY;\r\n \r\n if (p.x < 0) {\r\n p.x = this.width;\r\n }\r\n \r\n if (p.x > this.width) {\r\n p.x = 0;\r\n }\r\n \r\n if (p.y < 0) {\r\n p.y = this.height;\r\n }\r\n \r\n if (p.y > this.height) {\r\n p.y = 0;\r\n }\r\n\r\n const grad = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.size * 2);\r\n \r\n grad.addColorStop(0, p.colorPrefix + currentOpacity + ')');\r\n \r\n grad.addColorStop(1, p.colorPrefix + '0)');\r\n \r\n ctx.beginPath();\r\n \r\n ctx.fillStyle = grad;\r\n \r\n ctx.arc(p.x, p.y, p.size * 2, 0, Math.PI * 2);\r\n \r\n ctx.fill();\r\n \r\n ctx.beginPath();\r\n \r\n ctx.fillStyle = p.colorPrefix + (currentOpacity * 0.8) + ')';\r\n \r\n ctx.arc(p.x, p.y, p.size * 0.5, 0, Math.PI * 2);\r\n \r\n ctx.fill();\r\n }\r\n }\r\n\r\n /**\r\n * Renders a soft glow effect centered at the current mouse position.\r\n * @param {CanvasRenderingContext2D} ctx\r\n */\r\n drawMouseGlow(ctx) {\r\n if (!this.mouseActive) {\r\n return;\r\n }\r\n \r\n const gradient = ctx.createRadialGradient(this.mouseX, this.mouseY, 0, this.mouseX, this.mouseY, 200);\r\n \r\n const stops = this.config.colors.mouseGlowStops;\r\n \r\n stops.forEach((s) => gradient.addColorStop(s.offset, s.color));\r\n \r\n ctx.beginPath();\r\n \r\n ctx.fillStyle = gradient;\r\n \r\n ctx.arc(this.mouseX, this.mouseY, 200, 0, Math.PI * 2);\r\n \r\n ctx.fill();\r\n }\r\n\r\n /**\r\n * The main animation loop. Updates state and draws the next frame.\r\n */\r\n animate() {\r\n if (!this.ctx) {\r\n return;\r\n }\r\n \r\n this.time += 1;\r\n \r\n this.mouseX += (this.targetMouseX - this.mouseX) * 0.08;\r\n this.mouseY += (this.targetMouseY - this.mouseY) * 0.08;\r\n \r\n this.ctx.clearRect(0, 0, this.width, this.height);\r\n \r\n this.drawBackground(this.ctx);\r\n this.drawWaves(this.ctx);\r\n this.drawParticles(this.ctx);\r\n this.drawMouseGlow(this.ctx);\r\n \r\n if (this.isRunning) {\r\n requestAnimationFrame(() => this.animate());\r\n }\r\n }\r\n\r\n /**\r\n * Starts or resumes the animation loop and attaches interaction listeners.\r\n */\r\n start() {\r\n if (this.isRunning) {\r\n return;\r\n }\r\n \r\n if (typeof window !== 'undefined') {\r\n window.addEventListener('mousemove', this._onMouseMove, {passive: true});\r\n window.addEventListener('touchmove', this._onTouchMove, {passive: false});\r\n window.addEventListener('touchend', this._onTouchEnd, {passive: true});\r\n window.addEventListener('mouseleave', this._onMouseLeave);\r\n }\r\n \r\n this.isRunning = true;\r\n \r\n this.animate();\r\n }\r\n\r\n /**\r\n * Pauses the animation loop and detaches interaction listeners to save resources.\r\n */\r\n stop() {\r\n this.isRunning = false;\r\n \r\n if (typeof window !== 'undefined') {\r\n window.removeEventListener('mousemove', this._onMouseMove);\r\n window.removeEventListener('touchmove', this._onTouchMove);\r\n window.removeEventListener('touchend', this._onTouchEnd);\r\n window.removeEventListener('mouseleave', this._onMouseLeave);\r\n }\r\n }\r\n\r\n /**\r\n * Stops the animation, removes event listeners, and cleans up any auto-injected DOM elements.\r\n */\r\n destroy() {\r\n this.stop();\r\n \r\n if (typeof window !== 'undefined') {\r\n window.removeEventListener('resize', this._onResize);\r\n }\r\n \r\n if (this.canvas && this.canvas.id === 'wave-particles-canvas' && this.canvas.parentNode) {\r\n this.canvas.parentNode.removeChild(this.canvas);\r\n }\r\n \r\n this.canvas = null;\r\n this.ctx = null;\r\n }\r\n}\r\n\r\nexport default WaveParticles;\r\n", "import WaveParticles from './index.js';\r\n\r\n/**\r\n * Browser-only entry point for IIFE / script-tag usage.\r\n * Exposes the WaveParticles class directly on the global object.\r\n */\r\nif (typeof window !== 'undefined') {\r\n window.WaveParticles = WaveParticles;\r\n} else if (typeof globalThis !== 'undefined') {\r\n globalThis.WaveParticles = WaveParticles;\r\n}\r\n"],
|
|
5
|
-
"mappings": "MAMA,IAAMA,EAAN,KAAoB,CAQhB,YAAYC,EAAU,CAAC,EAAG,CACtB,GAAM,
|
|
6
|
-
"names": ["WaveParticles", "options", "providedCanvasOrSelector", "config", "resolvedCanvas", "rect", "ctx", "e", "userConfig", "defaults", "result", "key", "el", "body", "
|
|
4
|
+
"sourcesContent": ["/**\r\n * js-waves-particles\r\n * A canvas-based wave and particle animation library with mouse-reactive effects.\r\n * Developed by Luis 'PlatinumBlade' Moniz.\r\n */\r\n\r\nclass WaveParticles {\r\n /**\r\n * Initializes a new WaveParticles animation engine.\r\n *\r\n * @param {import('./index').WaveParticlesOptions} [options] - Configuration for the engine.\r\n * @param {HTMLCanvasElement|string} [options.canvas] - The target canvas element or a CSS selector (e.g., '#bg'). If omitted, a full-screen fixed canvas is automatically created.\r\n * @param {import('./index').WaveParticlesConfig} [options.config] - Visual parameters for waves, particles, and colors.\r\n */\r\n constructor(options = {}) {\r\n const { canvas: providedCanvasOrSelector, config } = options || {};\r\n\r\n // --- State ---\r\n this.canvas = null;\r\n this.ctx = null;\r\n this.mouseX = 0;\r\n this.mouseY = 0;\r\n this.targetMouseX = -1000;\r\n this.targetMouseY = -1000;\r\n this.mouseActive = false;\r\n this.width = 0;\r\n this.height = 0;\r\n this.config = this._mergeDefaults(config || {});\r\n this.time = 0;\r\n this.isRunning = false;\r\n this.particles = [];\r\n this._resizeTimeout = null;\r\n\r\n const resolvedCanvas = typeof document !== 'undefined' && providedCanvasOrSelector ? (() => {\r\n if (typeof providedCanvasOrSelector === 'string') {\r\n return /** @type {HTMLCanvasElement|null} */ (document.querySelector(providedCanvasOrSelector));\r\n }\r\n\r\n return /** @type {HTMLCanvasElement | null} */ (providedCanvasOrSelector);\r\n })() : null;\r\n\r\n if (resolvedCanvas) {\r\n this.canvas = resolvedCanvas;\r\n const rect = resolvedCanvas.getBoundingClientRect();\r\n this.width = Math.round(rect.width || window.innerWidth);\r\n this.height = Math.round(rect.height || window.innerHeight);\r\n } else {\r\n this._createAutoCanvas();\r\n }\r\n\r\n try {\r\n if (!this.canvas) {\r\n throw new Error('No canvas element available.');\r\n }\r\n\r\n const ctx = this.canvas.getContext('2d');\r\n\r\n if (!ctx) {\r\n throw new Error('Could not get 2D rendering context.');\r\n }\r\n\r\n this.ctx = ctx;\r\n } catch (e) {\r\n console.error('[WaveParticles] Canvas error:', e);\r\n return;\r\n }\r\n\r\n this._onMouseMove = this._onMouseMove.bind(this);\r\n this._onMouseLeave = this._onMouseLeave.bind(this);\r\n this._onTouchMove = this._onTouchMove.bind(this);\r\n this._onTouchEnd = this._onTouchEnd.bind(this);\r\n this._onResize = this._onResize.bind(this);\r\n\r\n if (typeof document !== 'undefined') {\r\n window.addEventListener('resize', this._onResize);\r\n this.resize(); // Initial size setup\r\n }\r\n\r\n this.initParticles();\r\n this.start();\r\n }\r\n\r\n /**\r\n * Internal method to merge user configuration with library defaults.\r\n * @private\r\n */\r\n _mergeDefaults(userConfig) {\r\n const defaults = {\r\n waves: [\r\n {\r\n amplitude: 60,\r\n frequency: 0.003,\r\n speed: 0.006,\r\n yOffset: 0.55,\r\n color: 'rgba(248, 225, 231, 0.25)',\r\n lineWidth: 1.5,\r\n mouseInfluence: 0.45\r\n },\r\n {\r\n amplitude: 50,\r\n frequency: 0.004,\r\n speed: 0.008,\r\n yOffset: 0.60,\r\n color: 'rgba(212, 165, 165, 0.2)',\r\n lineWidth: 1,\r\n mouseInfluence: 0.5\r\n },\r\n {\r\n amplitude: 70,\r\n frequency: 0.0025,\r\n speed: 0.005,\r\n yOffset: 0.65,\r\n color: 'rgba(255, 255, 255, 0.2)',\r\n lineWidth: 1.5,\r\n mouseInfluence: 0.4\r\n },\r\n {\r\n amplitude: 40,\r\n frequency: 0.0035,\r\n speed: 0.010,\r\n yOffset: 0.70,\r\n color: 'rgba(201, 169, 110, 0.12)',\r\n lineWidth: 1,\r\n mouseInfluence: 0.55\r\n }\r\n ],\r\n particles: { countScale: 8000, maxCount: 180 },\r\n colors: {\r\n backgroundGradient: ['#fdfcfb', '#f7ede2', '#e8d5d0'],\r\n particleColorPrefixes: [\r\n 'rgba(248, 225, 231,',\r\n 'rgba(212, 165, 165,',\r\n 'rgba(201, 169, 110,',\r\n 'rgba(255, 255, 255,',\r\n 'rgba(232, 213, 208,'\r\n ],\r\n mouseGlowStops: [\r\n { offset: 0, color: 'rgba(255, 255, 255, 0.08)' },\r\n { offset: 0.5, color: 'rgba(248, 225, 231, 0.04)' },\r\n { offset: 1, color: 'rgba(248, 225, 231, 0)' }\r\n ]\r\n }\r\n };\r\n\r\n const result = {};\r\n\r\n for (const key in defaults) {\r\n if (!defaults.hasOwnProperty(key)) {\r\n continue;\r\n }\r\n\r\n if (!(userConfig && userConfig[key])) {\r\n result[key] = JSON.parse(JSON.stringify(defaults[key]));\r\n } else if (typeof defaults[key] === 'object' && !Array.isArray(defaults[key]) && typeof userConfig[key] === 'object') {\r\n result[key] = Object.assign({}, defaults[key], userConfig[key]);\r\n } else if (key === 'waves') {\r\n result.waves = Array.isArray(userConfig.waves) ? [...userConfig.waves] : JSON.parse(JSON.stringify(defaults.waves));\r\n } else {\r\n result[key] = userConfig[key];\r\n }\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Automatically creates and injects a canvas element into the DOM if none was provided.\r\n * @private\r\n */\r\n _createAutoCanvas() {\r\n if (typeof document === 'undefined') {\r\n return;\r\n }\r\n\r\n const el = document.createElement('canvas');\r\n\r\n el.id = 'wave-particles-canvas';\r\n\r\n Object.assign(el.style, {\r\n position: 'fixed',\r\n top: '0',\r\n left: '0',\r\n width: '100%',\r\n height: '100%',\r\n zIndex: '-1',\r\n pointerEvents: 'none'\r\n });\r\n\r\n const body = document.body;\r\n\r\n if (!body) {\r\n return;\r\n }\r\n\r\n body.insertBefore(el, body.firstChild);\r\n this.canvas = el;\r\n this.width = window.innerWidth;\r\n this.height = window.innerHeight;\r\n }\r\n\r\n /**\r\n * Event handler for window resize events, including debouncing to prevent performance issues.\r\n * @private\r\n */\r\n _onResize() {\r\n if (this._resizeTimeout) clearTimeout(this._resizeTimeout);\r\n // Debounce resize to prevent flashing and high CPU usage\r\n this._resizeTimeout = setTimeout(() => {\r\n this.resize();\r\n }, 250);\r\n }\r\n\r\n /**\r\n * Recalculates canvas dimensions based on its display size and Device Pixel Ratio.\r\n * Re-initializes particles to match the new surface area.\r\n */\r\n resize() {\r\n if (!this.canvas) {\r\n return;\r\n }\r\n\r\n const rect = this.canvas.getBoundingClientRect();\r\n const dpr = window.devicePixelRatio || 1;\r\n\r\n // Use CSS pixels for logical dimensions.\r\n // Fallback to window size if canvas is not in layout (prevents the \"multiplication\" bug).\r\n const w = rect.width || window.innerWidth;\r\n const h = rect.height || window.innerHeight;\r\n\r\n this.width = w;\r\n this.height = h;\r\n\r\n // Set physical pixels for the canvas buffer\r\n this.canvas.width = Math.round(w * dpr);\r\n this.canvas.height = Math.round(h * dpr);\r\n\r\n // Scale the context to ensure all drawing operations use CSS pixels.\r\n // Re-get context if it was lost or not yet initialized.\r\n if (!this.ctx) {\r\n this.ctx = this.canvas.getContext('2d');\r\n }\r\n\r\n if (this.ctx) {\r\n this.ctx.scale(dpr, dpr);\r\n }\r\n\r\n if (this.isRunning) {\r\n this.initParticles();\r\n }\r\n }\r\n\r\n /**\r\n * Generates a new set of particles based on the current canvas area and configuration.\r\n */\r\n initParticles() {\r\n const { countScale = 8000, maxCount = 180 } = this.config.particles;\r\n\r\n const particleCount = Math.min(Math.floor((this.width * this.height) / (countScale > 0 ? countScale : 8000)), maxCount);\r\n\r\n this.particles = Array.from({ length: particleCount }, () => ({\r\n x: Math.random() * this.width,\r\n y: Math.random() * this.height,\r\n size: Math.random() * 4 + 1.5,\r\n speedX: (Math.random() - 0.5) * 0.4,\r\n speedY: (Math.random() - 0.5) * 0.4,\r\n opacity: Math.random() * 0.6 + 0.3,\r\n pulse: Math.random() * Math.PI * 2,\r\n pulseSpeed: Math.random() * 0.025 + 0.01,\r\n colorPrefix: this.config.colors.particleColorPrefixes[Math.floor(Math.random() * this.config.colors.particleColorPrefixes.length)]\r\n }));\r\n }\r\n\r\n _onMouseMove(e) {\r\n const rect = this.canvas.getBoundingClientRect();\r\n\r\n if (!rect) {\r\n return;\r\n }\r\n\r\n // Calculate position relative to the canvas in CSS pixels\r\n this.targetMouseX = e.clientX - rect.left;\r\n this.targetMouseY = e.clientY - rect.top;\r\n this.mouseActive = true;\r\n }\r\n\r\n _onMouseLeave() {\r\n this.mouseActive = false;\r\n }\r\n\r\n _onTouchMove(e) {\r\n const touch = e.touches[0];\r\n const rect = this.canvas.getBoundingClientRect();\r\n\r\n if (!touch || !rect) {\r\n return;\r\n }\r\n\r\n this.targetMouseX = touch.clientX - rect.left;\r\n this.targetMouseY = touch.clientY - rect.top;\r\n this.mouseActive = true;\r\n }\r\n\r\n _onTouchEnd() {\r\n this.mouseActive = false;\r\n }\r\n\r\n /**\r\n * Renders the radial gradient background across the entire canvas.\r\n * @param {CanvasRenderingContext2D} ctx\r\n */\r\n drawBackground(ctx) {\r\n const colors = this.config.colors.backgroundGradient;\r\n\r\n const gradient = ctx.createRadialGradient(this.width / 2, this.height / 2, 0, this.width / 2, this.height / 2, Math.max(this.width, this.height) * 0.8);\r\n\r\n colors.forEach((c, i) => gradient.addColorStop(i / (colors.length - 1 || 1), c));\r\n\r\n ctx.fillStyle = gradient;\r\n\r\n ctx.fillRect(0, 0, this.width, this.height);\r\n }\r\n\r\n /**\r\n * Renders all wave layers, accounting for mouse interaction and layer offsets.\r\n * @param {CanvasRenderingContext2D} ctx\r\n */\r\n drawWaves(ctx) {\r\n const mouseNormX = this.mouseActive ? this.mouseX / this.width : -1;\r\n const mouseNormY = this.mouseActive ? this.mouseY / this.height : -1;\r\n\r\n for (let wi = 0; wi < this.config.waves.length; wi++) {\r\n const wave = this.config.waves[wi];\r\n const numLayers = Math.max(2, wave.layers || 2);\r\n\r\n for (let layer = 0; layer < numLayers; layer++) {\r\n const layerOpacity = (1 - layer / numLayers) * 0.5 + 0.5;\r\n\r\n ctx.beginPath();\r\n\r\n const baseY = this.height * wave.yOffset + layer * 3;\r\n\r\n const mouseEffect = this.mouseActive ? (mouseNormX - 0.5) * (wave.mouseInfluence || 0.45) * 150 : 0;\r\n\r\n ctx.moveTo(0, this.height);\r\n\r\n const step = Math.max(2, Math.floor(this.width / 480));\r\n\r\n for (let x = 0; x <= this.width; x += step) {\r\n let y = baseY;\r\n\r\n y += Math.sin(x * wave.frequency + this.time * wave.speed + layer * 0.1) * wave.amplitude;\r\n y += Math.sin(x * wave.frequency * 1.5 + this.time * wave.speed * 0.7 + wi) * wave.amplitude * 0.3;\r\n\r\n if (this.mouseActive) {\r\n const dx = x - this.mouseX;\r\n const dist = Math.abs(dx) / this.width;\r\n y += Math.sin(dist * Math.PI) * (mouseNormY > 0 ? (this.mouseY - this.height / 2) : 0) * 0.1 * (wave.mouseInfluence || 0.45);\r\n }\r\n\r\n y += mouseEffect * Math.sin((x / this.width) * Math.PI);\r\n\r\n ctx.lineTo(x, y);\r\n }\r\n\r\n ctx.lineTo(this.width, this.height);\r\n\r\n ctx.closePath();\r\n\r\n const baseColor = wave.color || 'rgba(139, 114, 86, 0.25)';\r\n\r\n ctx.fillStyle = baseColor.replace(/([\\d.]+)\\)$/, (m) => `${parseFloat(m) * layerOpacity})`);\r\n\r\n if (wave.lineWidth != null) {\r\n ctx.lineWidth = wave.lineWidth;\r\n }\r\n\r\n ctx.fill();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Updates and renders the particle system, including movement and mouse repulsion logic.\r\n * @param {CanvasRenderingContext2D} ctx\r\n */\r\n drawParticles(ctx) {\r\n const maxDist = 150;\r\n\r\n for (const p of this.particles) {\r\n p.pulse += p.pulseSpeed;\r\n\r\n const currentOpacity = p.opacity * (Math.sin(p.pulse) * 0.3 + 0.7);\r\n\r\n if (this.mouseActive) {\r\n const dx = p.x - this.mouseX, dy = p.y - this.mouseY, dist = Math.sqrt(dx * dx + dy * dy);\r\n\r\n if (dist < maxDist && dist > 0.1) {\r\n const force = (1 - dist / maxDist) * 2;\r\n p.x += (dx / dist) * force;\r\n p.y += (dy / dist) * force;\r\n }\r\n }\r\n\r\n p.x += p.speedX;\r\n p.y += p.speedY;\r\n\r\n if (p.x < 0) {\r\n p.x = this.width;\r\n }\r\n\r\n if (p.x > this.width) {\r\n p.x = 0;\r\n }\r\n\r\n if (p.y < 0) {\r\n p.y = this.height;\r\n }\r\n\r\n if (p.y > this.height) {\r\n p.y = 0;\r\n }\r\n\r\n const grad = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.size * 2);\r\n\r\n grad.addColorStop(0, p.colorPrefix + currentOpacity + ')');\r\n\r\n grad.addColorStop(1, p.colorPrefix + '0)');\r\n\r\n ctx.beginPath();\r\n\r\n ctx.fillStyle = grad;\r\n\r\n ctx.arc(p.x, p.y, p.size * 2, 0, Math.PI * 2);\r\n\r\n ctx.fill();\r\n\r\n ctx.beginPath();\r\n\r\n ctx.fillStyle = p.colorPrefix + (currentOpacity * 0.8) + ')';\r\n\r\n ctx.arc(p.x, p.y, p.size * 0.5, 0, Math.PI * 2);\r\n\r\n ctx.fill();\r\n }\r\n }\r\n\r\n /**\r\n * Renders a soft glow effect centered at the current mouse position.\r\n * @param {CanvasRenderingContext2D} ctx\r\n */\r\n drawMouseGlow(ctx) {\r\n if (!this.mouseActive) {\r\n return;\r\n }\r\n\r\n const gradient = ctx.createRadialGradient(this.mouseX, this.mouseY, 0, this.mouseX, this.mouseY, 200);\r\n\r\n const stops = this.config.colors.mouseGlowStops;\r\n\r\n stops.forEach((s) => gradient.addColorStop(s.offset, s.color));\r\n\r\n ctx.beginPath();\r\n\r\n ctx.fillStyle = gradient;\r\n\r\n ctx.arc(this.mouseX, this.mouseY, 200, 0, Math.PI * 2);\r\n\r\n ctx.fill();\r\n }\r\n\r\n /**\r\n * The main animation loop. Updates state and draws the next frame.\r\n */\r\n animate() {\r\n if (!this.ctx) {\r\n return;\r\n }\r\n\r\n this.time += 1;\r\n\r\n this.mouseX += (this.targetMouseX - this.mouseX) * 0.08;\r\n this.mouseY += (this.targetMouseY - this.mouseY) * 0.08;\r\n\r\n this.ctx.clearRect(0, 0, this.width, this.height);\r\n\r\n this.drawBackground(this.ctx);\r\n this.drawWaves(this.ctx);\r\n this.drawParticles(this.ctx);\r\n this.drawMouseGlow(this.ctx);\r\n\r\n if (this.isRunning) {\r\n requestAnimationFrame(() => this.animate());\r\n }\r\n }\r\n\r\n /**\r\n * Starts or resumes the animation loop and attaches interaction listeners.\r\n */\r\n start() {\r\n if (this.isRunning) {\r\n return;\r\n }\r\n\r\n if (typeof window !== 'undefined') {\r\n window.addEventListener('mousemove', this._onMouseMove, { passive: true });\r\n window.addEventListener('touchmove', this._onTouchMove, { passive: false });\r\n window.addEventListener('touchend', this._onTouchEnd, { passive: true });\r\n window.addEventListener('mouseleave', this._onMouseLeave);\r\n }\r\n\r\n this.isRunning = true;\r\n\r\n this.animate();\r\n }\r\n\r\n /**\r\n * Pauses the animation loop and detaches interaction listeners to save resources.\r\n */\r\n stop() {\r\n this.isRunning = false;\r\n\r\n if (typeof window !== 'undefined') {\r\n window.removeEventListener('mousemove', this._onMouseMove);\r\n window.removeEventListener('touchmove', this._onTouchMove);\r\n window.removeEventListener('touchend', this._onTouchEnd);\r\n window.removeEventListener('mouseleave', this._onMouseLeave);\r\n }\r\n }\r\n\r\n /**\r\n * Stops the animation, removes event listeners, and cleans up any auto-injected DOM elements.\r\n */\r\n destroy() {\r\n this.stop();\r\n\r\n if (typeof window !== 'undefined') {\r\n window.removeEventListener('resize', this._onResize);\r\n }\r\n\r\n if (this.canvas && this.canvas.id === 'wave-particles-canvas' && this.canvas.parentNode) {\r\n this.canvas.parentNode.removeChild(this.canvas);\r\n }\r\n\r\n this.canvas = null;\r\n this.ctx = null;\r\n }\r\n}\r\n\r\nexport default WaveParticles;\r\n", "import WaveParticles from './index.js';\r\n\r\n/**\r\n * Browser-only entry point for IIFE / script-tag usage.\r\n * Exposes the WaveParticles class directly on the global object.\r\n */\r\nif (typeof window !== 'undefined') {\r\n window.WaveParticles = WaveParticles;\r\n} else if (typeof globalThis !== 'undefined') {\r\n globalThis.WaveParticles = WaveParticles;\r\n}\r\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["WaveParticles", "options", "providedCanvasOrSelector", "config", "resolvedCanvas", "rect", "ctx", "e", "userConfig", "defaults", "result", "key", "el", "body", "dpr", "w", "h", "countScale", "maxCount", "particleCount", "touch", "colors", "gradient", "c", "i", "mouseNormX", "mouseNormY", "wi", "wave", "numLayers", "layer", "layerOpacity", "baseY", "mouseEffect", "step", "x", "y", "dx", "dist", "baseColor", "m", "p", "currentOpacity", "dy", "force", "grad", "src_default", "src_default"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "js-waves-particles",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "A lightweight, canvas-based wave and particle animation library with mouse-reactive effects. Perfect for adding a subtle, organic feel to backgrounds in web projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/wave-particles.iife.js",
|
package/src/index.js
CHANGED
|
@@ -13,7 +13,7 @@ class WaveParticles {
|
|
|
13
13
|
* @param {import('./index').WaveParticlesConfig} [options.config] - Visual parameters for waves, particles, and colors.
|
|
14
14
|
*/
|
|
15
15
|
constructor(options = {}) {
|
|
16
|
-
const {canvas: providedCanvasOrSelector, config} = options || {};
|
|
16
|
+
const { canvas: providedCanvasOrSelector, config } = options || {};
|
|
17
17
|
|
|
18
18
|
// --- State ---
|
|
19
19
|
this.canvas = null;
|
|
@@ -124,7 +124,7 @@ class WaveParticles {
|
|
|
124
124
|
mouseInfluence: 0.55
|
|
125
125
|
}
|
|
126
126
|
],
|
|
127
|
-
particles: {countScale: 8000, maxCount: 180},
|
|
127
|
+
particles: { countScale: 8000, maxCount: 180 },
|
|
128
128
|
colors: {
|
|
129
129
|
backgroundGradient: ['#fdfcfb', '#f7ede2', '#e8d5d0'],
|
|
130
130
|
particleColorPrefixes: [
|
|
@@ -135,9 +135,9 @@ class WaveParticles {
|
|
|
135
135
|
'rgba(232, 213, 208,'
|
|
136
136
|
],
|
|
137
137
|
mouseGlowStops: [
|
|
138
|
-
{offset: 0, color: 'rgba(255, 255, 255, 0.08)'},
|
|
139
|
-
{offset: 0.5, color: 'rgba(248, 225, 231, 0.04)'},
|
|
140
|
-
{offset: 1, color: 'rgba(248, 225, 231, 0)'}
|
|
138
|
+
{ offset: 0, color: 'rgba(255, 255, 255, 0.08)' },
|
|
139
|
+
{ offset: 0.5, color: 'rgba(248, 225, 231, 0.04)' },
|
|
140
|
+
{ offset: 1, color: 'rgba(248, 225, 231, 0)' }
|
|
141
141
|
]
|
|
142
142
|
}
|
|
143
143
|
};
|
|
@@ -159,7 +159,7 @@ class WaveParticles {
|
|
|
159
159
|
result[key] = userConfig[key];
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
|
-
|
|
162
|
+
|
|
163
163
|
return result;
|
|
164
164
|
}
|
|
165
165
|
|
|
@@ -220,33 +220,31 @@ class WaveParticles {
|
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
const rect = this.canvas.getBoundingClientRect();
|
|
223
|
+
const dpr = window.devicePixelRatio || 1;
|
|
223
224
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
if (attrW && attrH) {
|
|
230
|
-
w = Math.round(rect.width || attrW);
|
|
231
|
-
h = Math.round(rect.height || attrH);
|
|
232
|
-
} else {
|
|
233
|
-
w = window.innerWidth;
|
|
234
|
-
h = window.innerHeight;
|
|
225
|
+
// Use CSS pixels for logical dimensions.
|
|
226
|
+
// Fallback to window size if canvas is not in layout (prevents the "multiplication" bug).
|
|
227
|
+
const w = rect.width || window.innerWidth;
|
|
228
|
+
const h = rect.height || window.innerHeight;
|
|
235
229
|
|
|
236
|
-
if (!this.canvas.parentNode && document.body) {
|
|
237
|
-
document.body.insertBefore(this.canvas, document.body.firstChild);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
230
|
this.width = w;
|
|
242
231
|
this.height = h;
|
|
243
232
|
|
|
244
|
-
|
|
245
|
-
|
|
233
|
+
// Set physical pixels for the canvas buffer
|
|
246
234
|
this.canvas.width = Math.round(w * dpr);
|
|
247
235
|
this.canvas.height = Math.round(h * dpr);
|
|
248
236
|
|
|
249
|
-
|
|
237
|
+
// Scale the context to ensure all drawing operations use CSS pixels.
|
|
238
|
+
// Re-get context if it was lost or not yet initialized.
|
|
239
|
+
if (!this.ctx) {
|
|
240
|
+
this.ctx = this.canvas.getContext('2d');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (this.ctx) {
|
|
244
|
+
this.ctx.scale(dpr, dpr);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (this.isRunning) {
|
|
250
248
|
this.initParticles();
|
|
251
249
|
}
|
|
252
250
|
}
|
|
@@ -255,11 +253,11 @@ class WaveParticles {
|
|
|
255
253
|
* Generates a new set of particles based on the current canvas area and configuration.
|
|
256
254
|
*/
|
|
257
255
|
initParticles() {
|
|
258
|
-
const {countScale = 8000, maxCount = 180} = this.config.particles;
|
|
256
|
+
const { countScale = 8000, maxCount = 180 } = this.config.particles;
|
|
259
257
|
|
|
260
258
|
const particleCount = Math.min(Math.floor((this.width * this.height) / (countScale > 0 ? countScale : 8000)), maxCount);
|
|
261
259
|
|
|
262
|
-
this.particles = Array.from({length: particleCount}, () => ({
|
|
260
|
+
this.particles = Array.from({ length: particleCount }, () => ({
|
|
263
261
|
x: Math.random() * this.width,
|
|
264
262
|
y: Math.random() * this.height,
|
|
265
263
|
size: Math.random() * 4 + 1.5,
|
|
@@ -274,7 +272,7 @@ class WaveParticles {
|
|
|
274
272
|
|
|
275
273
|
_onMouseMove(e) {
|
|
276
274
|
const rect = this.canvas.getBoundingClientRect();
|
|
277
|
-
|
|
275
|
+
|
|
278
276
|
if (!rect) {
|
|
279
277
|
return;
|
|
280
278
|
}
|
|
@@ -292,7 +290,7 @@ class WaveParticles {
|
|
|
292
290
|
_onTouchMove(e) {
|
|
293
291
|
const touch = e.touches[0];
|
|
294
292
|
const rect = this.canvas.getBoundingClientRect();
|
|
295
|
-
|
|
293
|
+
|
|
296
294
|
if (!touch || !rect) {
|
|
297
295
|
return;
|
|
298
296
|
}
|
|
@@ -312,13 +310,13 @@ class WaveParticles {
|
|
|
312
310
|
*/
|
|
313
311
|
drawBackground(ctx) {
|
|
314
312
|
const colors = this.config.colors.backgroundGradient;
|
|
315
|
-
|
|
313
|
+
|
|
316
314
|
const gradient = ctx.createRadialGradient(this.width / 2, this.height / 2, 0, this.width / 2, this.height / 2, Math.max(this.width, this.height) * 0.8);
|
|
317
|
-
|
|
315
|
+
|
|
318
316
|
colors.forEach((c, i) => gradient.addColorStop(i / (colors.length - 1 || 1), c));
|
|
319
|
-
|
|
317
|
+
|
|
320
318
|
ctx.fillStyle = gradient;
|
|
321
|
-
|
|
319
|
+
|
|
322
320
|
ctx.fillRect(0, 0, this.width, this.height);
|
|
323
321
|
}
|
|
324
322
|
|
|
@@ -336,46 +334,46 @@ class WaveParticles {
|
|
|
336
334
|
|
|
337
335
|
for (let layer = 0; layer < numLayers; layer++) {
|
|
338
336
|
const layerOpacity = (1 - layer / numLayers) * 0.5 + 0.5;
|
|
339
|
-
|
|
337
|
+
|
|
340
338
|
ctx.beginPath();
|
|
341
|
-
|
|
339
|
+
|
|
342
340
|
const baseY = this.height * wave.yOffset + layer * 3;
|
|
343
|
-
|
|
341
|
+
|
|
344
342
|
const mouseEffect = this.mouseActive ? (mouseNormX - 0.5) * (wave.mouseInfluence || 0.45) * 150 : 0;
|
|
345
|
-
|
|
343
|
+
|
|
346
344
|
ctx.moveTo(0, this.height);
|
|
347
345
|
|
|
348
346
|
const step = Math.max(2, Math.floor(this.width / 480));
|
|
349
|
-
|
|
347
|
+
|
|
350
348
|
for (let x = 0; x <= this.width; x += step) {
|
|
351
349
|
let y = baseY;
|
|
352
|
-
|
|
350
|
+
|
|
353
351
|
y += Math.sin(x * wave.frequency + this.time * wave.speed + layer * 0.1) * wave.amplitude;
|
|
354
352
|
y += Math.sin(x * wave.frequency * 1.5 + this.time * wave.speed * 0.7 + wi) * wave.amplitude * 0.3;
|
|
355
|
-
|
|
353
|
+
|
|
356
354
|
if (this.mouseActive) {
|
|
357
355
|
const dx = x - this.mouseX;
|
|
358
356
|
const dist = Math.abs(dx) / this.width;
|
|
359
357
|
y += Math.sin(dist * Math.PI) * (mouseNormY > 0 ? (this.mouseY - this.height / 2) : 0) * 0.1 * (wave.mouseInfluence || 0.45);
|
|
360
358
|
}
|
|
361
|
-
|
|
359
|
+
|
|
362
360
|
y += mouseEffect * Math.sin((x / this.width) * Math.PI);
|
|
363
|
-
|
|
361
|
+
|
|
364
362
|
ctx.lineTo(x, y);
|
|
365
363
|
}
|
|
366
|
-
|
|
364
|
+
|
|
367
365
|
ctx.lineTo(this.width, this.height);
|
|
368
|
-
|
|
366
|
+
|
|
369
367
|
ctx.closePath();
|
|
370
|
-
|
|
368
|
+
|
|
371
369
|
const baseColor = wave.color || 'rgba(139, 114, 86, 0.25)';
|
|
372
|
-
|
|
370
|
+
|
|
373
371
|
ctx.fillStyle = baseColor.replace(/([\d.]+)\)$/, (m) => `${parseFloat(m) * layerOpacity})`);
|
|
374
|
-
|
|
372
|
+
|
|
375
373
|
if (wave.lineWidth != null) {
|
|
376
374
|
ctx.lineWidth = wave.lineWidth;
|
|
377
375
|
}
|
|
378
|
-
|
|
376
|
+
|
|
379
377
|
ctx.fill();
|
|
380
378
|
}
|
|
381
379
|
}
|
|
@@ -387,61 +385,61 @@ class WaveParticles {
|
|
|
387
385
|
*/
|
|
388
386
|
drawParticles(ctx) {
|
|
389
387
|
const maxDist = 150;
|
|
390
|
-
|
|
388
|
+
|
|
391
389
|
for (const p of this.particles) {
|
|
392
390
|
p.pulse += p.pulseSpeed;
|
|
393
|
-
|
|
391
|
+
|
|
394
392
|
const currentOpacity = p.opacity * (Math.sin(p.pulse) * 0.3 + 0.7);
|
|
395
|
-
|
|
393
|
+
|
|
396
394
|
if (this.mouseActive) {
|
|
397
395
|
const dx = p.x - this.mouseX, dy = p.y - this.mouseY, dist = Math.sqrt(dx * dx + dy * dy);
|
|
398
|
-
|
|
396
|
+
|
|
399
397
|
if (dist < maxDist && dist > 0.1) {
|
|
400
398
|
const force = (1 - dist / maxDist) * 2;
|
|
401
399
|
p.x += (dx / dist) * force;
|
|
402
400
|
p.y += (dy / dist) * force;
|
|
403
401
|
}
|
|
404
402
|
}
|
|
405
|
-
|
|
403
|
+
|
|
406
404
|
p.x += p.speedX;
|
|
407
405
|
p.y += p.speedY;
|
|
408
|
-
|
|
406
|
+
|
|
409
407
|
if (p.x < 0) {
|
|
410
408
|
p.x = this.width;
|
|
411
409
|
}
|
|
412
|
-
|
|
410
|
+
|
|
413
411
|
if (p.x > this.width) {
|
|
414
412
|
p.x = 0;
|
|
415
413
|
}
|
|
416
|
-
|
|
414
|
+
|
|
417
415
|
if (p.y < 0) {
|
|
418
416
|
p.y = this.height;
|
|
419
417
|
}
|
|
420
|
-
|
|
418
|
+
|
|
421
419
|
if (p.y > this.height) {
|
|
422
420
|
p.y = 0;
|
|
423
421
|
}
|
|
424
422
|
|
|
425
423
|
const grad = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.size * 2);
|
|
426
|
-
|
|
424
|
+
|
|
427
425
|
grad.addColorStop(0, p.colorPrefix + currentOpacity + ')');
|
|
428
|
-
|
|
426
|
+
|
|
429
427
|
grad.addColorStop(1, p.colorPrefix + '0)');
|
|
430
|
-
|
|
428
|
+
|
|
431
429
|
ctx.beginPath();
|
|
432
|
-
|
|
430
|
+
|
|
433
431
|
ctx.fillStyle = grad;
|
|
434
|
-
|
|
432
|
+
|
|
435
433
|
ctx.arc(p.x, p.y, p.size * 2, 0, Math.PI * 2);
|
|
436
|
-
|
|
434
|
+
|
|
437
435
|
ctx.fill();
|
|
438
|
-
|
|
436
|
+
|
|
439
437
|
ctx.beginPath();
|
|
440
|
-
|
|
438
|
+
|
|
441
439
|
ctx.fillStyle = p.colorPrefix + (currentOpacity * 0.8) + ')';
|
|
442
|
-
|
|
440
|
+
|
|
443
441
|
ctx.arc(p.x, p.y, p.size * 0.5, 0, Math.PI * 2);
|
|
444
|
-
|
|
442
|
+
|
|
445
443
|
ctx.fill();
|
|
446
444
|
}
|
|
447
445
|
}
|
|
@@ -454,19 +452,19 @@ class WaveParticles {
|
|
|
454
452
|
if (!this.mouseActive) {
|
|
455
453
|
return;
|
|
456
454
|
}
|
|
457
|
-
|
|
455
|
+
|
|
458
456
|
const gradient = ctx.createRadialGradient(this.mouseX, this.mouseY, 0, this.mouseX, this.mouseY, 200);
|
|
459
|
-
|
|
457
|
+
|
|
460
458
|
const stops = this.config.colors.mouseGlowStops;
|
|
461
|
-
|
|
459
|
+
|
|
462
460
|
stops.forEach((s) => gradient.addColorStop(s.offset, s.color));
|
|
463
|
-
|
|
461
|
+
|
|
464
462
|
ctx.beginPath();
|
|
465
|
-
|
|
463
|
+
|
|
466
464
|
ctx.fillStyle = gradient;
|
|
467
|
-
|
|
465
|
+
|
|
468
466
|
ctx.arc(this.mouseX, this.mouseY, 200, 0, Math.PI * 2);
|
|
469
|
-
|
|
467
|
+
|
|
470
468
|
ctx.fill();
|
|
471
469
|
}
|
|
472
470
|
|
|
@@ -477,19 +475,19 @@ class WaveParticles {
|
|
|
477
475
|
if (!this.ctx) {
|
|
478
476
|
return;
|
|
479
477
|
}
|
|
480
|
-
|
|
478
|
+
|
|
481
479
|
this.time += 1;
|
|
482
|
-
|
|
480
|
+
|
|
483
481
|
this.mouseX += (this.targetMouseX - this.mouseX) * 0.08;
|
|
484
482
|
this.mouseY += (this.targetMouseY - this.mouseY) * 0.08;
|
|
485
|
-
|
|
483
|
+
|
|
486
484
|
this.ctx.clearRect(0, 0, this.width, this.height);
|
|
487
|
-
|
|
485
|
+
|
|
488
486
|
this.drawBackground(this.ctx);
|
|
489
487
|
this.drawWaves(this.ctx);
|
|
490
488
|
this.drawParticles(this.ctx);
|
|
491
489
|
this.drawMouseGlow(this.ctx);
|
|
492
|
-
|
|
490
|
+
|
|
493
491
|
if (this.isRunning) {
|
|
494
492
|
requestAnimationFrame(() => this.animate());
|
|
495
493
|
}
|
|
@@ -502,16 +500,16 @@ class WaveParticles {
|
|
|
502
500
|
if (this.isRunning) {
|
|
503
501
|
return;
|
|
504
502
|
}
|
|
505
|
-
|
|
503
|
+
|
|
506
504
|
if (typeof window !== 'undefined') {
|
|
507
|
-
window.addEventListener('mousemove', this._onMouseMove, {passive: true});
|
|
508
|
-
window.addEventListener('touchmove', this._onTouchMove, {passive: false});
|
|
509
|
-
window.addEventListener('touchend', this._onTouchEnd, {passive: true});
|
|
505
|
+
window.addEventListener('mousemove', this._onMouseMove, { passive: true });
|
|
506
|
+
window.addEventListener('touchmove', this._onTouchMove, { passive: false });
|
|
507
|
+
window.addEventListener('touchend', this._onTouchEnd, { passive: true });
|
|
510
508
|
window.addEventListener('mouseleave', this._onMouseLeave);
|
|
511
509
|
}
|
|
512
|
-
|
|
510
|
+
|
|
513
511
|
this.isRunning = true;
|
|
514
|
-
|
|
512
|
+
|
|
515
513
|
this.animate();
|
|
516
514
|
}
|
|
517
515
|
|
|
@@ -520,7 +518,7 @@ class WaveParticles {
|
|
|
520
518
|
*/
|
|
521
519
|
stop() {
|
|
522
520
|
this.isRunning = false;
|
|
523
|
-
|
|
521
|
+
|
|
524
522
|
if (typeof window !== 'undefined') {
|
|
525
523
|
window.removeEventListener('mousemove', this._onMouseMove);
|
|
526
524
|
window.removeEventListener('touchmove', this._onTouchMove);
|
|
@@ -534,15 +532,15 @@ class WaveParticles {
|
|
|
534
532
|
*/
|
|
535
533
|
destroy() {
|
|
536
534
|
this.stop();
|
|
537
|
-
|
|
535
|
+
|
|
538
536
|
if (typeof window !== 'undefined') {
|
|
539
537
|
window.removeEventListener('resize', this._onResize);
|
|
540
538
|
}
|
|
541
|
-
|
|
539
|
+
|
|
542
540
|
if (this.canvas && this.canvas.id === 'wave-particles-canvas' && this.canvas.parentNode) {
|
|
543
541
|
this.canvas.parentNode.removeChild(this.canvas);
|
|
544
542
|
}
|
|
545
|
-
|
|
543
|
+
|
|
546
544
|
this.canvas = null;
|
|
547
545
|
this.ctx = null;
|
|
548
546
|
}
|