react-animated-waves 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +19 -16
package/README.md
CHANGED
|
@@ -99,7 +99,7 @@ function VoiceWaveform() {
|
|
|
99
99
|
<Waves
|
|
100
100
|
amplitude={40}
|
|
101
101
|
intensity={intensity}
|
|
102
|
-
smoothing={0.
|
|
102
|
+
smoothing={0.8}
|
|
103
103
|
speed={1.3}
|
|
104
104
|
colors={["#436EDB", "#8B5CF6"]}
|
|
105
105
|
height={180}
|
|
@@ -132,7 +132,7 @@ The `Waves` component accepts the following props:
|
|
|
132
132
|
| `colors` | The colors for the waveform. Accepts any CSS color string (e.g., `#436EDB`, `rgb(67, 110, 219)`, `#436EDB80`). | `['#436EDB']` |
|
|
133
133
|
| `amplitude` | Maximum height of the waveform in pixels. | `20` |
|
|
134
134
|
| `speed` | Speed of the waveform animation. | `1` |
|
|
135
|
-
| `smoothing` |
|
|
135
|
+
| `smoothing` | How gradually amplitude approaches its target; higher values change more slowly. | `0.9` |
|
|
136
136
|
| `frequency` | Sine wave frequency of the waveform. | `1` |
|
|
137
137
|
| `waveCount` | Number of primary wave layers. | `3` |
|
|
138
138
|
| `lineCount` | Number of secondary mesh lines. | `30` |
|
|
@@ -154,4 +154,4 @@ Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to report issu
|
|
|
154
154
|
|
|
155
155
|
[MIT](LICENSE) © [Rohit Agrawal](https://github.com/agrawal-rohit)
|
|
156
156
|
|
|
157
|
-
[demo]: https://
|
|
157
|
+
[demo]: https://react-animated-waves.rohit-agrawal.com/
|
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ type WaveLayer = {
|
|
|
12
12
|
type WavesProps = Omit<React.CanvasHTMLAttributes<HTMLCanvasElement>, "height"> & {
|
|
13
13
|
/** The colors for the waveform. Accepts any CSS color string (e.g., `#436EDB`, `rgb(67, 110, 219)`, `#436EDB80`). */colors?: string[]; /** Maximum height of the waveform in pixels. */
|
|
14
14
|
amplitude?: number; /** Speed of the waveform animation. */
|
|
15
|
-
speed?: number; /**
|
|
15
|
+
speed?: number; /** How gradually amplitude approaches its target; higher values change more slowly. */
|
|
16
16
|
smoothing?: number; /** Sine wave frequency of the waveform. */
|
|
17
17
|
frequency?: number; /** Number of primary wave layers. */
|
|
18
18
|
waveCount?: number; /** Number of secondary mesh lines. */
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{forwardRef as e,memo as t,useCallback as n,useEffect as r,useImperativeHandle as i,useLayoutEffect as a,useMemo as o,useRef as s}from"react";import{TinyColor as c}from"@ctrl/tinycolor";import{jsx as l}from"react/jsx-runtime";const u=(e,t)=>new c(e).setAlpha(Math.max(0,t)).toRgbString(),d=(e,t,n)=>{let r=`${t.join(`|`)}:${n.toFixed(3)}`,i=e.get(r);if(i)return i;let a=t.map((e,r)=>({offset:t.length>1?r/(t.length-1):0,color:u(e,n)}));return e.set(r,a),a},f=e=>e===`auto`||e===void 0?globalThis.window===void 0?1:globalThis.window.devicePixelRatio||1:e,p=(e,t)=>{if(e===void 0)return t>0?t:150;if(typeof e==`number`)return e;if(e.endsWith(`%`))return t>0?t*Number.parseFloat(e)/100:150;let n=Number.parseFloat(e);return Number.isFinite(n)?n:150},m=(e,t,n)=>{if(!e)return null;let r=e.parentElement,i=r?.clientWidth||e.clientWidth||300,a=p(t,r?.clientHeight||0),o=f(n),s=Math.max(1,i),c=Math.max(1,Math.floor(a));e.width=Math.max(1,Math.floor(s*o)),e.height=Math.max(1,Math.floor(c*o)),e.style.width=`${s}px`,e.style.height=`${c}px`;let l=e.getContext(`2d`);return l&&`setTransform`in l&&l.setTransform(o,0,0,o,0,0),{width:s,height:c}},h=(e,t,n,r,i,a)=>{let o=d(r,i,a),s=e.createLinearGradient(0,0,t,n);for(let e of o)s.addColorStop(e.offset,e.color);return s},g=(e,t,n,r,i,a,o,s)=>{e.strokeStyle=a,e.beginPath();for(let a=0;a<t;a++){let c=Math.sin(a/t*Math.PI)**s,l=r*Math.sin(i*a+o)*c;e.lineTo(a,n/2+l)}e.stroke()},_=({ctx:e,colors:t,config:n,gradientCache:r,layoutWidth:i,layoutHeight:a,oscillatingAmplitude:o,timestamp:s})=>{for(let c of n.layers){let l=o*c.amplitudeMultiplier,u=s*c.speed+c.phaseOffset;g(e,i,a,l,c.frequency,h(e,i,a,r,t,c.alpha),u,n.pinch);for(let o=0;o<c.meshCount;o++)g(e,i,a,l-o*.1,c.frequency+o*25e-5,h(e,i,a,r,t,c.alpha*.6-o*.01),s*c.speed+o*.015+c.phaseOffset,n.pinch)}},v=[`#436EDB`],y=[{amplitudeMultiplier:1,frequency:.02,alpha:.6,speed:.001,meshCount:30,phaseOffset:0},{amplitudeMultiplier:.6,frequency:.03,alpha:.4,speed:.004,meshCount:30,phaseOffset:0},{amplitudeMultiplier:.3,frequency:.04,alpha:.2,speed:.007,meshCount:30,phaseOffset:0}],b=(e,t,n)=>{let r=y[t]??y.at(-1);return{amplitudeMultiplier:e.amplitudeMultiplier??r.amplitudeMultiplier,frequency:e.frequency??r.frequency,alpha:e.alpha??r.alpha,speed:e.speed??r.speed,meshCount:e.meshCount??n,phaseOffset:e.phaseOffset??0}},x=(e,t,n,r,i)=>e===3&&t===1&&n===1&&r===1&&i===30?y.map(e=>({...e,meshCount:i})):Array.from({length:e},(a,o)=>{let s=e>1?o/(e-1):0;return{amplitudeMultiplier:1-s*.7,frequency:(.02+s*.02)*t,alpha:(.6-s*.4)*n,speed:(.001+s*.006)*r,meshCount:i,phaseOffset:0}}),S=({speed:e=1,smoothing:t=.
|
|
1
|
+
import{forwardRef as e,memo as t,useCallback as n,useEffect as r,useImperativeHandle as i,useLayoutEffect as a,useMemo as o,useRef as s}from"react";import{TinyColor as c}from"@ctrl/tinycolor";import{jsx as l}from"react/jsx-runtime";const u=(e,t)=>new c(e).setAlpha(Math.max(0,t)).toRgbString(),d=(e,t,n)=>{let r=`${t.join(`|`)}:${n.toFixed(3)}`,i=e.get(r);if(i)return i;let a=t.map((e,r)=>({offset:t.length>1?r/(t.length-1):0,color:u(e,n)}));return e.set(r,a),a},f=e=>e===`auto`||e===void 0?globalThis.window===void 0?1:globalThis.window.devicePixelRatio||1:e,p=(e,t)=>{if(e===void 0)return t>0?t:150;if(typeof e==`number`)return e;if(e.endsWith(`%`))return t>0?t*Number.parseFloat(e)/100:150;let n=Number.parseFloat(e);return Number.isFinite(n)?n:150},m=(e,t,n)=>{if(!e)return null;let r=e.parentElement,i=r?.clientWidth||e.clientWidth||300,a=p(t,r?.clientHeight||0),o=f(n),s=Math.max(1,i),c=Math.max(1,Math.floor(a));e.width=Math.max(1,Math.floor(s*o)),e.height=Math.max(1,Math.floor(c*o)),e.style.width=`${s}px`,e.style.height=`${c}px`;let l=e.getContext(`2d`);return l&&`setTransform`in l&&l.setTransform(o,0,0,o,0,0),{width:s,height:c}},h=(e,t,n,r,i,a)=>{let o=d(r,i,a),s=e.createLinearGradient(0,0,t,n);for(let e of o)s.addColorStop(e.offset,e.color);return s},g=(e,t,n,r,i,a,o,s)=>{e.strokeStyle=a,e.beginPath();for(let a=0;a<t;a++){let c=Math.sin(a/t*Math.PI)**s,l=r*Math.sin(i*a+o)*c;e.lineTo(a,n/2+l)}e.stroke()},_=({ctx:e,colors:t,config:n,gradientCache:r,layoutWidth:i,layoutHeight:a,oscillatingAmplitude:o,timestamp:s})=>{for(let c of n.layers){let l=o*c.amplitudeMultiplier,u=s*c.speed+c.phaseOffset;g(e,i,a,l,c.frequency,h(e,i,a,r,t,c.alpha),u,n.pinch);for(let o=0;o<c.meshCount;o++)g(e,i,a,l-o*.1,c.frequency+o*25e-5,h(e,i,a,r,t,c.alpha*.6-o*.01),s*c.speed+o*.015+c.phaseOffset,n.pinch)}},v=[`#436EDB`],y=[{amplitudeMultiplier:1,frequency:.02,alpha:.6,speed:.001,meshCount:30,phaseOffset:0},{amplitudeMultiplier:.6,frequency:.03,alpha:.4,speed:.004,meshCount:30,phaseOffset:0},{amplitudeMultiplier:.3,frequency:.04,alpha:.2,speed:.007,meshCount:30,phaseOffset:0}],b=(e,t,n)=>{let r=y[t]??y.at(-1);return{amplitudeMultiplier:e.amplitudeMultiplier??r.amplitudeMultiplier,frequency:e.frequency??r.frequency,alpha:e.alpha??r.alpha,speed:e.speed??r.speed,meshCount:e.meshCount??n,phaseOffset:e.phaseOffset??0}},x=(e,t,n,r,i)=>e===3&&t===1&&n===1&&r===1&&i===30?y.map(e=>({...e,meshCount:i})):Array.from({length:e},(a,o)=>{let s=e>1?o/(e-1):0;return{amplitudeMultiplier:1-s*.7,frequency:(.02+s*.02)*t,alpha:(.6-s*.4)*n,speed:(.001+s*.006)*r,meshCount:i,phaseOffset:0}}),S=({speed:e=1,smoothing:t=.9,frequency:n=1,waveCount:r=3,lineCount:i=30,pinch:a=6,opacity:o=1,amplitudeOscillation:s=.05,layers:c}={})=>{let l=c?.length?c.map((e,t)=>b(e,t,i)):x(r,n,o,e,i);return{smoothing:t,amplitudeOscillation:s,timeScale:.0015*e,pinch:a,layers:l}},C=e=>e===void 0?1:Math.min(1,Math.max(0,e)),w=e(({amplitude:e=20,colors:t=v,speed:c,smoothing:u,frequency:d,waveCount:f,lineCount:p,pinch:h,opacity:g,amplitudeOscillation:y,height:b,pixelRatio:x=`auto`,respectReducedMotion:w=!0,paused:T=!1,intensity:E,layers:D,...O},k)=>{let A=s(null),j=s(e),M=s(e),N=s(new Map),P=s({width:300,height:150}),F=s(null),I=o(()=>S({speed:c,smoothing:u,frequency:d,waveCount:f,lineCount:p,pinch:h,opacity:g,amplitudeOscillation:y,layers:D}),[c,u,d,f,p,h,g,y,D]),L=C(E);i(k,()=>A.current,[]),r(()=>{M.current=e*L},[e,L]);let R=n(()=>{let e=m(A.current,b,x);e&&(P.current=e)},[b,x]);return a(()=>{R();let e=A.current?.parentElement;if(!e||typeof ResizeObserver>`u`){F.current?.();return}let t=new ResizeObserver(()=>{R(),F.current?.()});return t.observe(e),()=>t.disconnect()},[R]),r(()=>{let e=0,n=A.current,r=n?.getContext(`2d`);if(!n||!r)return;N.current.clear();let i=globalThis.window!==void 0&&w?globalThis.window.matchMedia(`(prefers-reduced-motion: reduce)`):null,a=()=>!(T||typeof document<`u`&&document.hidden||i?.matches),o=()=>{let e=Date.now(),n=e*I.timeScale,i=1-I.smoothing;j.current+=i*(M.current-j.current);let a=I.amplitudeOscillation===0?j.current:j.current*(1+Math.sin(n)*I.amplitudeOscillation);r.clearRect(0,0,P.current.width,P.current.height),_({ctx:r,colors:t,config:I,gradientCache:N.current,layoutWidth:P.current.width,layoutHeight:P.current.height,oscillatingAmplitude:a,timestamp:e})},s=()=>{a()&&(o(),e=requestAnimationFrame(s))};F.current=o,R(),o(),a()&&(e=requestAnimationFrame(s));let c=()=>{!document.hidden&&a()&&(o(),e=requestAnimationFrame(s))},l=()=>{a()&&(o(),e=requestAnimationFrame(s))};return document.addEventListener(`visibilitychange`,c),i?.addEventListener(`change`,l),()=>{F.current=null,cancelAnimationFrame(e),document.removeEventListener(`visibilitychange`,c),i?.removeEventListener(`change`,l)}},[t,T,w,R,I]),l(`canvas`,{ref:A,"aria-hidden":O[`aria-hidden`]??!0,width:`100%`,height:`auto`,...O})});w.displayName=`Waves`;const T=t(w);export{T as Waves};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/color.ts","../src/canvas.ts","../src/config.ts","../src/waves.tsx"],"sourcesContent":["import { TinyColor } from \"@ctrl/tinycolor\";\n\n/**\n * Convert a CSS color string to an rgba(...) value with the given alpha.\n * @param color - The CSS color string to convert.\n * @param alpha - The alpha value to apply to the color.\n * @returns The rgba(...) value with the given alpha.\n */\nexport const colorWithAlpha = (color: string, alpha: number) =>\n\tnew TinyColor(color).setAlpha(Math.max(0, alpha)).toRgbString();\n\nexport type GradientStops = {\n\toffset: number;\n\tcolor: string;\n}[];\n\nexport type GradientCache = Map<string, GradientStops>;\n\n/**\n * Resolve linear-gradient stop data for a palette and opacity, reusing cached results.\n * @param cache - Gradient stop cache keyed by palette and alpha.\n * @param colors - The colors to build the gradient stops for.\n * @param alpha - The alpha value to apply to the colors.\n * @returns The gradient stops.\n */\nexport const getGradientStops = (\n\tcache: GradientCache,\n\tcolors: string[],\n\talpha: number,\n) => {\n\tconst key = `${colors.join(\"|\")}:${alpha.toFixed(3)}`;\n\tconst cached = cache.get(key);\n\tif (cached) return cached;\n\n\tconst stops = colors.map((color, index) => ({\n\t\toffset: colors.length > 1 ? index / (colors.length - 1) : 0,\n\t\tcolor: colorWithAlpha(color, alpha),\n\t}));\n\tcache.set(key, stops);\n\treturn stops;\n};\n","import { type GradientCache, getGradientStops } from \"./color\";\nimport type { resolveWaveConfig } from \"./config\";\nimport type { WavesProps } from \"./types\";\n\n/**\n * Resolve the device pixel ratio used for the canvas backing store.\n * @param pixelRatio - The pixel ratio to use for the canvas.\n * @returns The device pixel ratio.\n */\nexport const getPixelRatio = (pixelRatio: WavesProps[\"pixelRatio\"]) => {\n\tif (pixelRatio === \"auto\" || pixelRatio === undefined) {\n\t\treturn globalThis.window === undefined\n\t\t\t? 1\n\t\t\t: globalThis.window.devicePixelRatio || 1;\n\t}\n\n\treturn pixelRatio;\n};\n\n/**\n * Resolve the canvas height in CSS pixels from the height prop and parent size.\n * @param height - The height of the canvas.\n * @param parentHeight - The height of the parent element.\n * @returns The canvas height in CSS pixels.\n */\nexport const resolveCanvasHeight = (\n\theight: WavesProps[\"height\"],\n\tparentHeight: number,\n) => {\n\tif (height === undefined) return parentHeight > 0 ? parentHeight : 150;\n\n\tif (typeof height === \"number\") return height;\n\n\tif (height.endsWith(\"%\")) {\n\t\tif (parentHeight > 0)\n\t\t\treturn (parentHeight * Number.parseFloat(height)) / 100;\n\n\t\t// Parent may not be measured yet; fall back until ResizeObserver runs.\n\t\treturn 150;\n\t}\n\n\tconst parsedHeight = Number.parseFloat(height);\n\treturn Number.isFinite(parsedHeight) ? parsedHeight : 150;\n};\n\n/**\n * Sync canvas CSS and backing-store dimensions.\n * @param canvas - The canvas element to apply the layout to.\n * @param height - The height of the canvas.\n * @param pixelRatio - The pixel ratio to use for the canvas.\n * @returns The layout width and height.\n */\nexport const applyCanvasLayout = (\n\tcanvas: HTMLCanvasElement | null,\n\theight: WavesProps[\"height\"],\n\tpixelRatio: WavesProps[\"pixelRatio\"],\n) => {\n\tif (!canvas) return null;\n\n\tconst parent = canvas.parentElement;\n\tconst layoutWidthValue = parent?.clientWidth || canvas.clientWidth || 300;\n\tconst layoutHeightValueRaw = resolveCanvasHeight(\n\t\theight,\n\t\tparent?.clientHeight || 0,\n\t);\n\tconst ratio = getPixelRatio(pixelRatio);\n\tconst layoutWidth = Math.max(1, layoutWidthValue);\n\tconst layoutHeightValue = Math.max(1, Math.floor(layoutHeightValueRaw));\n\n\t// Backing-store pixels are scaled for retina; drawing uses layout coordinates.\n\tcanvas.width = Math.max(1, Math.floor(layoutWidth * ratio));\n\tcanvas.height = Math.max(1, Math.floor(layoutHeightValue * ratio));\n\tcanvas.style.width = `${layoutWidth}px`;\n\tcanvas.style.height = `${layoutHeightValue}px`;\n\n\tconst ctx = canvas.getContext(\"2d\");\n\tif (ctx && \"setTransform\" in ctx) {\n\t\tctx.setTransform(ratio, 0, 0, ratio, 0, 0);\n\t}\n\n\treturn {\n\t\twidth: layoutWidth,\n\t\theight: layoutHeightValue,\n\t};\n};\n\n/**\n * Create a diagonal canvas gradient for a wave layer.\n * @param ctx - The canvas context to use for the gradient.\n * @param width - The width of the canvas.\n * @param height - The height of the canvas.\n * @param cache - Gradient stop cache keyed by palette and alpha.\n * @param colors - The colors to use for the gradient stops.\n * @param alpha - The alpha value to use for the gradient stops.\n * @returns The canvas gradient.\n */\nconst createWaveGradient = (\n\tctx: CanvasRenderingContext2D,\n\twidth: number,\n\theight: number,\n\tcache: GradientCache,\n\tcolors: string[],\n\talpha: number,\n) => {\n\tconst stops = getGradientStops(cache, colors, alpha);\n\tconst gradient = ctx.createLinearGradient(0, 0, width, height);\n\tfor (const stop of stops) gradient.addColorStop(stop.offset, stop.color);\n\treturn gradient;\n};\n\n/**\n * Draw a single horizontal wave line using a sine curve and edge pinch envelope.\n * @param ctx - The canvas context to use for the waveform.\n * @param layoutWidth - The width of the waveform.\n * @param layoutHeight - The height of the waveform.\n * @param amplitude - The amplitude of the waveform.\n * @param frequency - The frequency of the waveform.\n * @param color - The color of the waveform.\n * @param phase - The phase of the waveform.\n * @param pinchExponent - The pinch exponent of the waveform.\n * @returns The drawn waveform.\n */\nconst drawWaveform = (\n\tctx: CanvasRenderingContext2D,\n\tlayoutWidth: number,\n\tlayoutHeight: number,\n\tamplitude: number,\n\tfrequency: number,\n\tcolor: string | CanvasGradient,\n\tphase: number,\n\tpinchExponent: number,\n) => {\n\tctx.strokeStyle = color;\n\tctx.beginPath();\n\n\tfor (let i = 0; i < layoutWidth; i++) {\n\t\t// Fade amplitude toward both edges so the wave tapers to a point.\n\t\tconst sineWave = Math.sin(Math.PI * (i / layoutWidth));\n\t\tconst pinch = sineWave ** pinchExponent;\n\t\tconst y = amplitude * Math.sin(frequency * i + phase) * pinch;\n\n\t\tctx.lineTo(i, layoutHeight / 2 + y);\n\t}\n\n\tctx.stroke();\n};\n\n/**\n * Draw each primary wave and its surrounding mesh of secondary lines.\n * @param ctx - The canvas context to use for the waveform.\n * @param colors - The colors to use for the waveform.\n * @param config - The configuration to use for the waveform.\n * @param gradientCache - The cache to use for the gradient stops.\n * @param layoutWidth - The width of the waveform.\n * @param layoutHeight - The height of the waveform.\n * @param oscillatingAmplitude - The oscillating amplitude of the waveform.\n * @param timestamp - The timestamp of the waveform.\n * @returns The drawn waveform.\n */\nexport const drawWaveLayers = ({\n\tctx,\n\tcolors,\n\tconfig,\n\tgradientCache,\n\tlayoutWidth,\n\tlayoutHeight,\n\toscillatingAmplitude,\n\ttimestamp,\n}: {\n\tctx: CanvasRenderingContext2D;\n\tcolors: string[];\n\tconfig: ReturnType<typeof resolveWaveConfig>;\n\tgradientCache: GradientCache;\n\tlayoutWidth: number;\n\tlayoutHeight: number;\n\toscillatingAmplitude: number;\n\ttimestamp: number;\n}) => {\n\tfor (const primary of config.layers) {\n\t\tconst primaryAmplitude = oscillatingAmplitude * primary.amplitudeMultiplier;\n\t\tconst primaryPhase = timestamp * primary.speed + primary.phaseOffset;\n\n\t\tdrawWaveform(\n\t\t\tctx,\n\t\t\tlayoutWidth,\n\t\t\tlayoutHeight,\n\t\t\tprimaryAmplitude,\n\t\t\tprimary.frequency,\n\t\t\tcreateWaveGradient(\n\t\t\t\tctx,\n\t\t\t\tlayoutWidth,\n\t\t\t\tlayoutHeight,\n\t\t\t\tgradientCache,\n\t\t\t\tcolors,\n\t\t\t\tprimary.alpha,\n\t\t\t),\n\t\t\tprimaryPhase,\n\t\t\tconfig.pinch,\n\t\t);\n\n\t\t// Secondary mesh lines create the dense woven look around each primary wave.\n\t\tfor (let meshIndex = 0; meshIndex < primary.meshCount; meshIndex++) {\n\t\t\tconst amp = primaryAmplitude - meshIndex * 0.1;\n\t\t\tconst freq = primary.frequency + meshIndex * 0.00025;\n\t\t\tconst alpha = primary.alpha * 0.6 - meshIndex * 0.01;\n\n\t\t\tdrawWaveform(\n\t\t\t\tctx,\n\t\t\t\tlayoutWidth,\n\t\t\t\tlayoutHeight,\n\t\t\t\tamp,\n\t\t\t\tfreq,\n\t\t\t\tcreateWaveGradient(\n\t\t\t\t\tctx,\n\t\t\t\t\tlayoutWidth,\n\t\t\t\t\tlayoutHeight,\n\t\t\t\t\tgradientCache,\n\t\t\t\t\tcolors,\n\t\t\t\t\talpha,\n\t\t\t\t),\n\t\t\t\ttimestamp * primary.speed + meshIndex * 0.015 + primary.phaseOffset,\n\t\t\t\tconfig.pinch,\n\t\t\t);\n\t\t}\n\t}\n};\n","import type { WaveLayer, WavesProps } from \"./types\";\n\n// Default values for the Waves component.\nexport const DEFAULT_COLORS = [\"#436EDB\"];\nexport const DEFAULT_AMPLITUDE = 20;\nconst BASE_TIME_SCALE = 0.0015;\nconst BASE_SMOOTHING = 0.1;\nconst BASE_FREQUENCY = 1;\nconst BASE_LINE_COUNT = 30;\nconst BASE_WAVE_COUNT = 3;\nconst BASE_PINCH = 6;\nconst BASE_OPACITY = 1;\nconst BASE_SPEED = 1;\nconst BASE_AMPLITUDE_OSCILLATION = 0.05;\n\n/** Default wave layers used by the Waves component. */\nconst DEFAULT_PRIMARY_LAYERS: Required<WaveLayer>[] = [\n\t{\n\t\tamplitudeMultiplier: 1,\n\t\tfrequency: 0.02,\n\t\talpha: 0.6,\n\t\tspeed: 0.001,\n\t\tmeshCount: BASE_LINE_COUNT,\n\t\tphaseOffset: 0,\n\t},\n\t{\n\t\tamplitudeMultiplier: 0.6,\n\t\tfrequency: 0.03,\n\t\talpha: 0.4,\n\t\tspeed: 0.004,\n\t\tmeshCount: BASE_LINE_COUNT,\n\t\tphaseOffset: 0,\n\t},\n\t{\n\t\tamplitudeMultiplier: 0.3,\n\t\tfrequency: 0.04,\n\t\talpha: 0.2,\n\t\tspeed: 0.007,\n\t\tmeshCount: BASE_LINE_COUNT,\n\t\tphaseOffset: 0,\n\t},\n];\n\nexport type ResolveWaveConfigInput = Pick<\n\tWavesProps,\n\t| \"speed\"\n\t| \"smoothing\"\n\t| \"frequency\"\n\t| \"waveCount\"\n\t| \"lineCount\"\n\t| \"pinch\"\n\t| \"opacity\"\n\t| \"amplitudeOscillation\"\n\t| \"layers\"\n>;\n\n/**\n * Merge a caller-provided layer with defaults.\n * @param layer - The caller-provided layer.\n * @param index - The index of the layer.\n * @param lineCount - The number of lines to draw.\n * @returns The merged layer.\n */\nconst resolveLayer = (\n\tlayer: WaveLayer,\n\tindex: number,\n\tlineCount: number,\n): Required<WaveLayer> => {\n\tconst fallback =\n\t\tDEFAULT_PRIMARY_LAYERS[index] ?? DEFAULT_PRIMARY_LAYERS.at(-1);\n\n\treturn {\n\t\tamplitudeMultiplier:\n\t\t\tlayer.amplitudeMultiplier ?? fallback.amplitudeMultiplier,\n\t\tfrequency: layer.frequency ?? fallback.frequency,\n\t\talpha: layer.alpha ?? fallback.alpha,\n\t\tspeed: layer.speed ?? fallback.speed,\n\t\tmeshCount: layer.meshCount ?? lineCount,\n\t\tphaseOffset: layer.phaseOffset ?? 0,\n\t};\n};\n\n/**\n * Generate evenly spaced waveform layers when the caller does not pass `layers`.\n * @param waveCount - The number of waves to generate.\n * @param frequencyScale - The frequency scale to apply to the waves.\n * @param opacityScale - The opacity scale to apply to the waves.\n * @param speedScale - The speed scale to apply to the waves.\n * @param lineCount - The number of lines to draw.\n * @returns The generated layers.\n */\nconst generateLayers = (\n\twaveCount: number,\n\tfrequencyScale: number,\n\topacityScale: number,\n\tspeedScale: number,\n\tlineCount: number,\n): Required<WaveLayer>[] => {\n\tif (\n\t\twaveCount === BASE_WAVE_COUNT &&\n\t\tfrequencyScale === 1 &&\n\t\topacityScale === 1 &&\n\t\tspeedScale === 1 &&\n\t\tlineCount === BASE_LINE_COUNT\n\t) {\n\t\treturn DEFAULT_PRIMARY_LAYERS.map((layer) => ({\n\t\t\t...layer,\n\t\t\tmeshCount: lineCount,\n\t\t}));\n\t}\n\n\treturn Array.from({ length: waveCount }, (_, index) => {\n\t\tconst t = waveCount > 1 ? index / (waveCount - 1) : 0;\n\n\t\treturn {\n\t\t\tamplitudeMultiplier: 1 - t * 0.7,\n\t\t\tfrequency: (0.02 + t * 0.02) * frequencyScale,\n\t\t\talpha: (0.6 - t * 0.4) * opacityScale,\n\t\t\tspeed: (0.001 + t * 0.006) * speedScale,\n\t\t\tmeshCount: lineCount,\n\t\t\tphaseOffset: 0,\n\t\t};\n\t});\n};\n\n/**\n * Normalize public props into renderer-ready animation settings.\n * @param speed - The speed of the waveform animation.\n * @param smoothing - The smoothing factor while approaching the target amplitude.\n * @param frequency - The sine wave frequency of the waveform.\n * @param waveCount - The number of waves to generate.\n * @param lineCount - The number of lines to draw.\n * @param pinch - The pinch intensity at the canvas edges.\n * @param opacity - The opacity of the waveform gradient stops.\n * @param amplitudeOscillation - The oscillation factor applied to the amplitude over time.\n * @param layers - The custom layer configuration overrides.\n * @returns The resolved wave configuration.\n */\nexport const resolveWaveConfig = ({\n\tspeed = BASE_SPEED,\n\tsmoothing = BASE_SMOOTHING,\n\tfrequency = BASE_FREQUENCY,\n\twaveCount = BASE_WAVE_COUNT,\n\tlineCount = BASE_LINE_COUNT,\n\tpinch = BASE_PINCH,\n\topacity = BASE_OPACITY,\n\tamplitudeOscillation = BASE_AMPLITUDE_OSCILLATION,\n\tlayers,\n}: ResolveWaveConfigInput = {}) => {\n\tconst resolvedLayers = layers?.length\n\t\t? layers.map((layer, index) => resolveLayer(layer, index, lineCount))\n\t\t: generateLayers(waveCount, frequency, opacity, speed, lineCount);\n\n\treturn {\n\t\tsmoothing,\n\t\tamplitudeOscillation,\n\t\ttimeScale: BASE_TIME_SCALE * speed,\n\t\tpinch,\n\t\tlayers: resolvedLayers,\n\t};\n};\n\n/**\n * Clamp a normalized activity level into the 0..1 range.\n * @param intensity - The normalized intensity of the waveform.\n * @returns The clamped intensity.\n */\nexport const clampIntensity = (intensity?: number) => {\n\tif (intensity === undefined) return 1;\n\treturn Math.min(1, Math.max(0, intensity));\n};\n","import {\n\tforwardRef,\n\tmemo,\n\tuseCallback,\n\tuseEffect,\n\tuseImperativeHandle,\n\tuseLayoutEffect,\n\tuseMemo,\n\tuseRef,\n} from \"react\";\nimport { applyCanvasLayout, drawWaveLayers } from \"./canvas\";\nimport type { GradientCache } from \"./color\";\nimport {\n\tclampIntensity,\n\tDEFAULT_AMPLITUDE,\n\tDEFAULT_COLORS,\n\tresolveWaveConfig,\n} from \"./config\";\nimport type { WavesProps } from \"./types\";\n\n/** Canvas-based animated wave renderer. */\nconst WavesComponent = forwardRef<HTMLCanvasElement, WavesProps>(\n\t(\n\t\t{\n\t\t\tamplitude = DEFAULT_AMPLITUDE,\n\t\t\tcolors = DEFAULT_COLORS,\n\t\t\tspeed,\n\t\t\tsmoothing,\n\t\t\tfrequency,\n\t\t\twaveCount,\n\t\t\tlineCount,\n\t\t\tpinch,\n\t\t\topacity,\n\t\t\tamplitudeOscillation,\n\t\t\theight,\n\t\t\tpixelRatio = \"auto\",\n\t\t\trespectReducedMotion = true,\n\t\t\tpaused = false,\n\t\t\tintensity,\n\t\t\tlayers,\n\t\t\t...props\n\t\t},\n\t\tref,\n\t) => {\n\t\tconst canvasRef = useRef<HTMLCanvasElement>(null);\n\t\tconst amplitudeRef = useRef(amplitude);\n\t\tconst targetAmplitudeRef = useRef(amplitude);\n\t\tconst gradientCacheRef = useRef<GradientCache>(new Map());\n\t\tconst layoutSizeRef = useRef({ width: 300, height: 150 });\n\t\tconst renderFrameRef = useRef<(() => void) | null>(null);\n\n\t\tconst waveConfig = useMemo(\n\t\t\t() =>\n\t\t\t\tresolveWaveConfig({\n\t\t\t\t\tspeed,\n\t\t\t\t\tsmoothing,\n\t\t\t\t\tfrequency,\n\t\t\t\t\twaveCount,\n\t\t\t\t\tlineCount,\n\t\t\t\t\tpinch,\n\t\t\t\t\topacity,\n\t\t\t\t\tamplitudeOscillation,\n\t\t\t\t\tlayers,\n\t\t\t\t}),\n\t\t\t[\n\t\t\t\tspeed,\n\t\t\t\tsmoothing,\n\t\t\t\tfrequency,\n\t\t\t\twaveCount,\n\t\t\t\tlineCount,\n\t\t\t\tpinch,\n\t\t\t\topacity,\n\t\t\t\tamplitudeOscillation,\n\t\t\t\tlayers,\n\t\t\t],\n\t\t);\n\n\t\tconst intensityMultiplier = clampIntensity(intensity);\n\n\t\t// Expose the canvas element to the parent component.\n\t\tuseImperativeHandle(ref, () => canvasRef.current as HTMLCanvasElement, []);\n\n\t\t// Update the target amplitude based on the intensity.\n\t\tuseEffect(() => {\n\t\t\ttargetAmplitudeRef.current = amplitude * intensityMultiplier;\n\t\t}, [amplitude, intensityMultiplier]);\n\n\t\t// Apply the canvas layout and update the layout size reference.\n\t\tconst resizeCanvas = useCallback(() => {\n\t\t\tconst layout = applyCanvasLayout(canvasRef.current, height, pixelRatio);\n\t\t\tif (layout) layoutSizeRef.current = layout;\n\t\t}, [height, pixelRatio]);\n\n\t\t// Resize the canvas when the layout size changes.\n\t\tuseLayoutEffect(() => {\n\t\t\tresizeCanvas();\n\n\t\t\t// If the parent element is not found or the ResizeObserver is not available, render the frame.\n\t\t\tconst canvas = canvasRef.current;\n\t\t\tconst parent = canvas?.parentElement;\n\t\t\tif (!parent || typeof ResizeObserver === \"undefined\") {\n\t\t\t\trenderFrameRef.current?.();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Create a new ResizeObserver to resize the canvas when the parent element changes size.\n\t\t\tconst observer = new ResizeObserver(() => {\n\t\t\t\tresizeCanvas();\n\t\t\t\trenderFrameRef.current?.();\n\t\t\t});\n\n\t\t\tobserver.observe(parent);\n\t\t\treturn () => observer.disconnect();\n\t\t}, [resizeCanvas]);\n\n\t\t// Render the frame when the canvas is resized.\n\t\tuseEffect(() => {\n\t\t\tlet animationFrameId = 0;\n\n\t\t\tconst canvas = canvasRef.current;\n\t\t\tconst ctx = canvas?.getContext(\"2d\");\n\t\t\tif (!canvas || !ctx) return;\n\n\t\t\tgradientCacheRef.current.clear();\n\n\t\t\t// Determine if the animation should run based on the paused state, document visibility, and reduced motion query.\n\t\t\tconst reducedMotionQuery =\n\t\t\t\tglobalThis.window !== undefined && respectReducedMotion\n\t\t\t\t\t? globalThis.window.matchMedia(\"(prefers-reduced-motion: reduce)\")\n\t\t\t\t\t: null;\n\n\t\t\tconst shouldAnimate = () => {\n\t\t\t\tif (paused) return false;\n\t\t\t\tif (typeof document !== \"undefined\" && document.hidden) return false;\n\t\t\t\tif (reducedMotionQuery?.matches) return false;\n\t\t\t\treturn true;\n\t\t\t};\n\n\t\t\tconst renderFrame = () => {\n\t\t\t\tconst timestamp = Date.now();\n\t\t\t\tconst time = timestamp * waveConfig.timeScale;\n\n\t\t\t\t// Smoothly approach the latest target amplitude from props/intensity.\n\t\t\t\tamplitudeRef.current +=\n\t\t\t\t\twaveConfig.smoothing *\n\t\t\t\t\t(targetAmplitudeRef.current - amplitudeRef.current);\n\n\t\t\t\t// Calculate the oscillating amplitude based on the time and the amplitude oscillation configuration.\n\t\t\t\tconst oscillatingAmplitude =\n\t\t\t\t\twaveConfig.amplitudeOscillation === 0\n\t\t\t\t\t\t? amplitudeRef.current\n\t\t\t\t\t\t: amplitudeRef.current *\n\t\t\t\t\t\t\t(1 + Math.sin(time) * waveConfig.amplitudeOscillation);\n\n\t\t\t\tctx.clearRect(\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t\tlayoutSizeRef.current.width,\n\t\t\t\t\tlayoutSizeRef.current.height,\n\t\t\t\t);\n\n\t\t\t\tdrawWaveLayers({\n\t\t\t\t\tctx,\n\t\t\t\t\tcolors,\n\t\t\t\t\tconfig: waveConfig,\n\t\t\t\t\tgradientCache: gradientCacheRef.current,\n\t\t\t\t\tlayoutWidth: layoutSizeRef.current.width,\n\t\t\t\t\tlayoutHeight: layoutSizeRef.current.height,\n\t\t\t\t\toscillatingAmplitude,\n\t\t\t\t\ttimestamp,\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tconst animate = () => {\n\t\t\t\tif (!shouldAnimate()) return;\n\n\t\t\t\trenderFrame();\n\t\t\t\tanimationFrameId = requestAnimationFrame(animate);\n\t\t\t};\n\n\t\t\t// Set the render frame reference to the render frame function.\n\t\t\trenderFrameRef.current = renderFrame;\n\t\t\tresizeCanvas();\n\t\t\trenderFrame();\n\n\t\t\t// Start the animation if the animation should run.\n\t\t\tif (shouldAnimate()) animationFrameId = requestAnimationFrame(animate);\n\n\t\t\tconst handleVisibilityChange = () => {\n\t\t\t\tif (!document.hidden && shouldAnimate()) {\n\t\t\t\t\trenderFrame();\n\t\t\t\t\tanimationFrameId = requestAnimationFrame(animate);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst handleReducedMotionChange = () => {\n\t\t\t\tif (shouldAnimate()) {\n\t\t\t\t\trenderFrame();\n\t\t\t\t\tanimationFrameId = requestAnimationFrame(animate);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tdocument.addEventListener(\"visibilitychange\", handleVisibilityChange);\n\t\t\treducedMotionQuery?.addEventListener(\"change\", handleReducedMotionChange);\n\n\t\t\treturn () => {\n\t\t\t\trenderFrameRef.current = null;\n\t\t\t\tcancelAnimationFrame(animationFrameId);\n\t\t\t\tdocument.removeEventListener(\n\t\t\t\t\t\"visibilitychange\",\n\t\t\t\t\thandleVisibilityChange,\n\t\t\t\t);\n\t\t\t\treducedMotionQuery?.removeEventListener(\n\t\t\t\t\t\"change\",\n\t\t\t\t\thandleReducedMotionChange,\n\t\t\t\t);\n\t\t\t};\n\t\t}, [colors, paused, respectReducedMotion, resizeCanvas, waveConfig]);\n\n\t\treturn (\n\t\t\t<canvas\n\t\t\t\tref={canvasRef}\n\t\t\t\taria-hidden={props[\"aria-hidden\"] ?? true}\n\t\t\t\twidth=\"100%\"\n\t\t\t\theight=\"auto\"\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t},\n);\n\nWavesComponent.displayName = \"Waves\";\n\nexport const Waves = memo(WavesComponent);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/color.ts","../src/canvas.ts","../src/config.ts","../src/waves.tsx"],"sourcesContent":["import { TinyColor } from \"@ctrl/tinycolor\";\n\n/**\n * Convert a CSS color string to an rgba(...) value with the given alpha.\n * @param color - The CSS color string to convert.\n * @param alpha - The alpha value to apply to the color.\n * @returns The rgba(...) value with the given alpha.\n */\nexport const colorWithAlpha = (color: string, alpha: number) =>\n\tnew TinyColor(color).setAlpha(Math.max(0, alpha)).toRgbString();\n\nexport type GradientStops = {\n\toffset: number;\n\tcolor: string;\n}[];\n\nexport type GradientCache = Map<string, GradientStops>;\n\n/**\n * Resolve linear-gradient stop data for a palette and opacity, reusing cached results.\n * @param cache - Gradient stop cache keyed by palette and alpha.\n * @param colors - The colors to build the gradient stops for.\n * @param alpha - The alpha value to apply to the colors.\n * @returns The gradient stops.\n */\nexport const getGradientStops = (\n\tcache: GradientCache,\n\tcolors: string[],\n\talpha: number,\n) => {\n\tconst key = `${colors.join(\"|\")}:${alpha.toFixed(3)}`;\n\tconst cached = cache.get(key);\n\tif (cached) return cached;\n\n\tconst stops = colors.map((color, index) => ({\n\t\toffset: colors.length > 1 ? index / (colors.length - 1) : 0,\n\t\tcolor: colorWithAlpha(color, alpha),\n\t}));\n\tcache.set(key, stops);\n\treturn stops;\n};\n","import { type GradientCache, getGradientStops } from \"./color\";\nimport type { resolveWaveConfig } from \"./config\";\nimport type { WavesProps } from \"./types\";\n\n/**\n * Resolve the device pixel ratio used for the canvas backing store.\n * @param pixelRatio - The pixel ratio to use for the canvas.\n * @returns The device pixel ratio.\n */\nexport const getPixelRatio = (pixelRatio: WavesProps[\"pixelRatio\"]) => {\n\tif (pixelRatio === \"auto\" || pixelRatio === undefined) {\n\t\treturn globalThis.window === undefined\n\t\t\t? 1\n\t\t\t: globalThis.window.devicePixelRatio || 1;\n\t}\n\n\treturn pixelRatio;\n};\n\n/**\n * Resolve the canvas height in CSS pixels from the height prop and parent size.\n * @param height - The height of the canvas.\n * @param parentHeight - The height of the parent element.\n * @returns The canvas height in CSS pixels.\n */\nexport const resolveCanvasHeight = (\n\theight: WavesProps[\"height\"],\n\tparentHeight: number,\n) => {\n\tif (height === undefined) return parentHeight > 0 ? parentHeight : 150;\n\n\tif (typeof height === \"number\") return height;\n\n\tif (height.endsWith(\"%\")) {\n\t\tif (parentHeight > 0)\n\t\t\treturn (parentHeight * Number.parseFloat(height)) / 100;\n\n\t\t// Parent may not be measured yet; fall back until ResizeObserver runs.\n\t\treturn 150;\n\t}\n\n\tconst parsedHeight = Number.parseFloat(height);\n\treturn Number.isFinite(parsedHeight) ? parsedHeight : 150;\n};\n\n/**\n * Sync canvas CSS and backing-store dimensions.\n * @param canvas - The canvas element to apply the layout to.\n * @param height - The height of the canvas.\n * @param pixelRatio - The pixel ratio to use for the canvas.\n * @returns The layout width and height.\n */\nexport const applyCanvasLayout = (\n\tcanvas: HTMLCanvasElement | null,\n\theight: WavesProps[\"height\"],\n\tpixelRatio: WavesProps[\"pixelRatio\"],\n) => {\n\tif (!canvas) return null;\n\n\tconst parent = canvas.parentElement;\n\tconst layoutWidthValue = parent?.clientWidth || canvas.clientWidth || 300;\n\tconst layoutHeightValueRaw = resolveCanvasHeight(\n\t\theight,\n\t\tparent?.clientHeight || 0,\n\t);\n\tconst ratio = getPixelRatio(pixelRatio);\n\tconst layoutWidth = Math.max(1, layoutWidthValue);\n\tconst layoutHeightValue = Math.max(1, Math.floor(layoutHeightValueRaw));\n\n\t// Backing-store pixels are scaled for retina; drawing uses layout coordinates.\n\tcanvas.width = Math.max(1, Math.floor(layoutWidth * ratio));\n\tcanvas.height = Math.max(1, Math.floor(layoutHeightValue * ratio));\n\tcanvas.style.width = `${layoutWidth}px`;\n\tcanvas.style.height = `${layoutHeightValue}px`;\n\n\tconst ctx = canvas.getContext(\"2d\");\n\tif (ctx && \"setTransform\" in ctx) {\n\t\tctx.setTransform(ratio, 0, 0, ratio, 0, 0);\n\t}\n\n\treturn {\n\t\twidth: layoutWidth,\n\t\theight: layoutHeightValue,\n\t};\n};\n\n/**\n * Create a diagonal canvas gradient for a wave layer.\n * @param ctx - The canvas context to use for the gradient.\n * @param width - The width of the canvas.\n * @param height - The height of the canvas.\n * @param cache - Gradient stop cache keyed by palette and alpha.\n * @param colors - The colors to use for the gradient stops.\n * @param alpha - The alpha value to use for the gradient stops.\n * @returns The canvas gradient.\n */\nconst createWaveGradient = (\n\tctx: CanvasRenderingContext2D,\n\twidth: number,\n\theight: number,\n\tcache: GradientCache,\n\tcolors: string[],\n\talpha: number,\n) => {\n\tconst stops = getGradientStops(cache, colors, alpha);\n\tconst gradient = ctx.createLinearGradient(0, 0, width, height);\n\tfor (const stop of stops) gradient.addColorStop(stop.offset, stop.color);\n\treturn gradient;\n};\n\n/**\n * Draw a single horizontal wave line using a sine curve and edge pinch envelope.\n * @param ctx - The canvas context to use for the waveform.\n * @param layoutWidth - The width of the waveform.\n * @param layoutHeight - The height of the waveform.\n * @param amplitude - The amplitude of the waveform.\n * @param frequency - The frequency of the waveform.\n * @param color - The color of the waveform.\n * @param phase - The phase of the waveform.\n * @param pinchExponent - The pinch exponent of the waveform.\n * @returns The drawn waveform.\n */\nconst drawWaveform = (\n\tctx: CanvasRenderingContext2D,\n\tlayoutWidth: number,\n\tlayoutHeight: number,\n\tamplitude: number,\n\tfrequency: number,\n\tcolor: string | CanvasGradient,\n\tphase: number,\n\tpinchExponent: number,\n) => {\n\tctx.strokeStyle = color;\n\tctx.beginPath();\n\n\tfor (let i = 0; i < layoutWidth; i++) {\n\t\t// Fade amplitude toward both edges so the wave tapers to a point.\n\t\tconst sineWave = Math.sin(Math.PI * (i / layoutWidth));\n\t\tconst pinch = sineWave ** pinchExponent;\n\t\tconst y = amplitude * Math.sin(frequency * i + phase) * pinch;\n\n\t\tctx.lineTo(i, layoutHeight / 2 + y);\n\t}\n\n\tctx.stroke();\n};\n\n/**\n * Draw each primary wave and its surrounding mesh of secondary lines.\n * @param ctx - The canvas context to use for the waveform.\n * @param colors - The colors to use for the waveform.\n * @param config - The configuration to use for the waveform.\n * @param gradientCache - The cache to use for the gradient stops.\n * @param layoutWidth - The width of the waveform.\n * @param layoutHeight - The height of the waveform.\n * @param oscillatingAmplitude - The oscillating amplitude of the waveform.\n * @param timestamp - The timestamp of the waveform.\n * @returns The drawn waveform.\n */\nexport const drawWaveLayers = ({\n\tctx,\n\tcolors,\n\tconfig,\n\tgradientCache,\n\tlayoutWidth,\n\tlayoutHeight,\n\toscillatingAmplitude,\n\ttimestamp,\n}: {\n\tctx: CanvasRenderingContext2D;\n\tcolors: string[];\n\tconfig: ReturnType<typeof resolveWaveConfig>;\n\tgradientCache: GradientCache;\n\tlayoutWidth: number;\n\tlayoutHeight: number;\n\toscillatingAmplitude: number;\n\ttimestamp: number;\n}) => {\n\tfor (const primary of config.layers) {\n\t\tconst primaryAmplitude = oscillatingAmplitude * primary.amplitudeMultiplier;\n\t\tconst primaryPhase = timestamp * primary.speed + primary.phaseOffset;\n\n\t\tdrawWaveform(\n\t\t\tctx,\n\t\t\tlayoutWidth,\n\t\t\tlayoutHeight,\n\t\t\tprimaryAmplitude,\n\t\t\tprimary.frequency,\n\t\t\tcreateWaveGradient(\n\t\t\t\tctx,\n\t\t\t\tlayoutWidth,\n\t\t\t\tlayoutHeight,\n\t\t\t\tgradientCache,\n\t\t\t\tcolors,\n\t\t\t\tprimary.alpha,\n\t\t\t),\n\t\t\tprimaryPhase,\n\t\t\tconfig.pinch,\n\t\t);\n\n\t\t// Secondary mesh lines create the dense woven look around each primary wave.\n\t\tfor (let meshIndex = 0; meshIndex < primary.meshCount; meshIndex++) {\n\t\t\tconst amp = primaryAmplitude - meshIndex * 0.1;\n\t\t\tconst freq = primary.frequency + meshIndex * 0.00025;\n\t\t\tconst alpha = primary.alpha * 0.6 - meshIndex * 0.01;\n\n\t\t\tdrawWaveform(\n\t\t\t\tctx,\n\t\t\t\tlayoutWidth,\n\t\t\t\tlayoutHeight,\n\t\t\t\tamp,\n\t\t\t\tfreq,\n\t\t\t\tcreateWaveGradient(\n\t\t\t\t\tctx,\n\t\t\t\t\tlayoutWidth,\n\t\t\t\t\tlayoutHeight,\n\t\t\t\t\tgradientCache,\n\t\t\t\t\tcolors,\n\t\t\t\t\talpha,\n\t\t\t\t),\n\t\t\t\ttimestamp * primary.speed + meshIndex * 0.015 + primary.phaseOffset,\n\t\t\t\tconfig.pinch,\n\t\t\t);\n\t\t}\n\t}\n};\n","import type { WaveLayer, WavesProps } from \"./types\";\n\n// Default values for the Waves component.\nexport const DEFAULT_COLORS = [\"#436EDB\"];\nexport const DEFAULT_AMPLITUDE = 20;\nconst BASE_TIME_SCALE = 0.0015;\nconst BASE_SMOOTHING = 0.9;\nconst BASE_FREQUENCY = 1;\nconst BASE_LINE_COUNT = 30;\nconst BASE_WAVE_COUNT = 3;\nconst BASE_PINCH = 6;\nconst BASE_OPACITY = 1;\nconst BASE_SPEED = 1;\nconst BASE_AMPLITUDE_OSCILLATION = 0.05;\n\n/** Default wave layers used by the Waves component. */\nconst DEFAULT_PRIMARY_LAYERS: Required<WaveLayer>[] = [\n\t{\n\t\tamplitudeMultiplier: 1,\n\t\tfrequency: 0.02,\n\t\talpha: 0.6,\n\t\tspeed: 0.001,\n\t\tmeshCount: BASE_LINE_COUNT,\n\t\tphaseOffset: 0,\n\t},\n\t{\n\t\tamplitudeMultiplier: 0.6,\n\t\tfrequency: 0.03,\n\t\talpha: 0.4,\n\t\tspeed: 0.004,\n\t\tmeshCount: BASE_LINE_COUNT,\n\t\tphaseOffset: 0,\n\t},\n\t{\n\t\tamplitudeMultiplier: 0.3,\n\t\tfrequency: 0.04,\n\t\talpha: 0.2,\n\t\tspeed: 0.007,\n\t\tmeshCount: BASE_LINE_COUNT,\n\t\tphaseOffset: 0,\n\t},\n];\n\nexport type ResolveWaveConfigInput = Pick<\n\tWavesProps,\n\t| \"speed\"\n\t| \"smoothing\"\n\t| \"frequency\"\n\t| \"waveCount\"\n\t| \"lineCount\"\n\t| \"pinch\"\n\t| \"opacity\"\n\t| \"amplitudeOscillation\"\n\t| \"layers\"\n>;\n\n/**\n * Merge a caller-provided layer with defaults.\n * @param layer - The caller-provided layer.\n * @param index - The index of the layer.\n * @param lineCount - The number of lines to draw.\n * @returns The merged layer.\n */\nconst resolveLayer = (\n\tlayer: WaveLayer,\n\tindex: number,\n\tlineCount: number,\n): Required<WaveLayer> => {\n\tconst fallback =\n\t\tDEFAULT_PRIMARY_LAYERS[index] ?? DEFAULT_PRIMARY_LAYERS.at(-1);\n\n\treturn {\n\t\tamplitudeMultiplier:\n\t\t\tlayer.amplitudeMultiplier ?? fallback.amplitudeMultiplier,\n\t\tfrequency: layer.frequency ?? fallback.frequency,\n\t\talpha: layer.alpha ?? fallback.alpha,\n\t\tspeed: layer.speed ?? fallback.speed,\n\t\tmeshCount: layer.meshCount ?? lineCount,\n\t\tphaseOffset: layer.phaseOffset ?? 0,\n\t};\n};\n\n/**\n * Generate evenly spaced waveform layers when the caller does not pass `layers`.\n * @param waveCount - The number of waves to generate.\n * @param frequencyScale - The frequency scale to apply to the waves.\n * @param opacityScale - The opacity scale to apply to the waves.\n * @param speedScale - The speed scale to apply to the waves.\n * @param lineCount - The number of lines to draw.\n * @returns The generated layers.\n */\nconst generateLayers = (\n\twaveCount: number,\n\tfrequencyScale: number,\n\topacityScale: number,\n\tspeedScale: number,\n\tlineCount: number,\n): Required<WaveLayer>[] => {\n\tif (\n\t\twaveCount === BASE_WAVE_COUNT &&\n\t\tfrequencyScale === 1 &&\n\t\topacityScale === 1 &&\n\t\tspeedScale === 1 &&\n\t\tlineCount === BASE_LINE_COUNT\n\t) {\n\t\treturn DEFAULT_PRIMARY_LAYERS.map((layer) => ({\n\t\t\t...layer,\n\t\t\tmeshCount: lineCount,\n\t\t}));\n\t}\n\n\treturn Array.from({ length: waveCount }, (_, index) => {\n\t\tconst t = waveCount > 1 ? index / (waveCount - 1) : 0;\n\n\t\treturn {\n\t\t\tamplitudeMultiplier: 1 - t * 0.7,\n\t\t\tfrequency: (0.02 + t * 0.02) * frequencyScale,\n\t\t\talpha: (0.6 - t * 0.4) * opacityScale,\n\t\t\tspeed: (0.001 + t * 0.006) * speedScale,\n\t\t\tmeshCount: lineCount,\n\t\t\tphaseOffset: 0,\n\t\t};\n\t});\n};\n\n/**\n * Normalize public props into renderer-ready animation settings.\n * @param speed - The speed of the waveform animation.\n * @param smoothing - How gradually amplitude approaches its target; higher values change more slowly.\n * @param frequency - The sine wave frequency of the waveform.\n * @param waveCount - The number of waves to generate.\n * @param lineCount - The number of lines to draw.\n * @param pinch - The pinch intensity at the canvas edges.\n * @param opacity - The opacity of the waveform gradient stops.\n * @param amplitudeOscillation - The oscillation factor applied to the amplitude over time.\n * @param layers - The custom layer configuration overrides.\n * @returns The resolved wave configuration.\n */\nexport const resolveWaveConfig = ({\n\tspeed = BASE_SPEED,\n\tsmoothing = BASE_SMOOTHING,\n\tfrequency = BASE_FREQUENCY,\n\twaveCount = BASE_WAVE_COUNT,\n\tlineCount = BASE_LINE_COUNT,\n\tpinch = BASE_PINCH,\n\topacity = BASE_OPACITY,\n\tamplitudeOscillation = BASE_AMPLITUDE_OSCILLATION,\n\tlayers,\n}: ResolveWaveConfigInput = {}) => {\n\tconst resolvedLayers = layers?.length\n\t\t? layers.map((layer, index) => resolveLayer(layer, index, lineCount))\n\t\t: generateLayers(waveCount, frequency, opacity, speed, lineCount);\n\n\treturn {\n\t\tsmoothing,\n\t\tamplitudeOscillation,\n\t\ttimeScale: BASE_TIME_SCALE * speed,\n\t\tpinch,\n\t\tlayers: resolvedLayers,\n\t};\n};\n\n/**\n * Clamp a normalized activity level into the 0..1 range.\n * @param intensity - The normalized intensity of the waveform.\n * @returns The clamped intensity.\n */\nexport const clampIntensity = (intensity?: number) => {\n\tif (intensity === undefined) return 1;\n\treturn Math.min(1, Math.max(0, intensity));\n};\n","import {\n\tforwardRef,\n\tmemo,\n\tuseCallback,\n\tuseEffect,\n\tuseImperativeHandle,\n\tuseLayoutEffect,\n\tuseMemo,\n\tuseRef,\n} from \"react\";\nimport { applyCanvasLayout, drawWaveLayers } from \"./canvas\";\nimport type { GradientCache } from \"./color\";\nimport {\n\tclampIntensity,\n\tDEFAULT_AMPLITUDE,\n\tDEFAULT_COLORS,\n\tresolveWaveConfig,\n} from \"./config\";\nimport type { WavesProps } from \"./types\";\n\n/** Canvas-based animated wave renderer. */\nconst WavesComponent = forwardRef<HTMLCanvasElement, WavesProps>(\n\t(\n\t\t{\n\t\t\tamplitude = DEFAULT_AMPLITUDE,\n\t\t\tcolors = DEFAULT_COLORS,\n\t\t\tspeed,\n\t\t\tsmoothing,\n\t\t\tfrequency,\n\t\t\twaveCount,\n\t\t\tlineCount,\n\t\t\tpinch,\n\t\t\topacity,\n\t\t\tamplitudeOscillation,\n\t\t\theight,\n\t\t\tpixelRatio = \"auto\",\n\t\t\trespectReducedMotion = true,\n\t\t\tpaused = false,\n\t\t\tintensity,\n\t\t\tlayers,\n\t\t\t...props\n\t\t},\n\t\tref,\n\t) => {\n\t\tconst canvasRef = useRef<HTMLCanvasElement>(null);\n\t\tconst amplitudeRef = useRef(amplitude);\n\t\tconst targetAmplitudeRef = useRef(amplitude);\n\t\tconst gradientCacheRef = useRef<GradientCache>(new Map());\n\t\tconst layoutSizeRef = useRef({ width: 300, height: 150 });\n\t\tconst renderFrameRef = useRef<(() => void) | null>(null);\n\n\t\tconst waveConfig = useMemo(\n\t\t\t() =>\n\t\t\t\tresolveWaveConfig({\n\t\t\t\t\tspeed,\n\t\t\t\t\tsmoothing,\n\t\t\t\t\tfrequency,\n\t\t\t\t\twaveCount,\n\t\t\t\t\tlineCount,\n\t\t\t\t\tpinch,\n\t\t\t\t\topacity,\n\t\t\t\t\tamplitudeOscillation,\n\t\t\t\t\tlayers,\n\t\t\t\t}),\n\t\t\t[\n\t\t\t\tspeed,\n\t\t\t\tsmoothing,\n\t\t\t\tfrequency,\n\t\t\t\twaveCount,\n\t\t\t\tlineCount,\n\t\t\t\tpinch,\n\t\t\t\topacity,\n\t\t\t\tamplitudeOscillation,\n\t\t\t\tlayers,\n\t\t\t],\n\t\t);\n\n\t\tconst intensityMultiplier = clampIntensity(intensity);\n\n\t\t// Expose the canvas element to the parent component.\n\t\tuseImperativeHandle(ref, () => canvasRef.current as HTMLCanvasElement, []);\n\n\t\t// Update the target amplitude based on the intensity.\n\t\tuseEffect(() => {\n\t\t\ttargetAmplitudeRef.current = amplitude * intensityMultiplier;\n\t\t}, [amplitude, intensityMultiplier]);\n\n\t\t// Apply the canvas layout and update the layout size reference.\n\t\tconst resizeCanvas = useCallback(() => {\n\t\t\tconst layout = applyCanvasLayout(canvasRef.current, height, pixelRatio);\n\t\t\tif (layout) layoutSizeRef.current = layout;\n\t\t}, [height, pixelRatio]);\n\n\t\t// Resize the canvas when the layout size changes.\n\t\tuseLayoutEffect(() => {\n\t\t\tresizeCanvas();\n\n\t\t\t// If the parent element is not found or the ResizeObserver is not available, render the frame.\n\t\t\tconst canvas = canvasRef.current;\n\t\t\tconst parent = canvas?.parentElement;\n\t\t\tif (!parent || typeof ResizeObserver === \"undefined\") {\n\t\t\t\trenderFrameRef.current?.();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Create a new ResizeObserver to resize the canvas when the parent element changes size.\n\t\t\tconst observer = new ResizeObserver(() => {\n\t\t\t\tresizeCanvas();\n\t\t\t\trenderFrameRef.current?.();\n\t\t\t});\n\n\t\t\tobserver.observe(parent);\n\t\t\treturn () => observer.disconnect();\n\t\t}, [resizeCanvas]);\n\n\t\t// Render the frame when the canvas is resized.\n\t\tuseEffect(() => {\n\t\t\tlet animationFrameId = 0;\n\n\t\t\tconst canvas = canvasRef.current;\n\t\t\tconst ctx = canvas?.getContext(\"2d\");\n\t\t\tif (!canvas || !ctx) return;\n\n\t\t\tgradientCacheRef.current.clear();\n\n\t\t\t// Determine if the animation should run based on the paused state, document visibility, and reduced motion query.\n\t\t\tconst reducedMotionQuery =\n\t\t\t\tglobalThis.window !== undefined && respectReducedMotion\n\t\t\t\t\t? globalThis.window.matchMedia(\"(prefers-reduced-motion: reduce)\")\n\t\t\t\t\t: null;\n\n\t\t\tconst shouldAnimate = () => {\n\t\t\t\tif (paused) return false;\n\t\t\t\tif (typeof document !== \"undefined\" && document.hidden) return false;\n\t\t\t\tif (reducedMotionQuery?.matches) return false;\n\t\t\t\treturn true;\n\t\t\t};\n\n\t\t\tconst renderFrame = () => {\n\t\t\t\tconst timestamp = Date.now();\n\t\t\t\tconst time = timestamp * waveConfig.timeScale;\n\n\t\t\t\t// Higher smoothing slows the approach; lower values snap to the target faster.\n\t\t\t\tconst amplitudeLerpFactor = 1 - waveConfig.smoothing;\n\t\t\t\tamplitudeRef.current +=\n\t\t\t\t\tamplitudeLerpFactor *\n\t\t\t\t\t(targetAmplitudeRef.current - amplitudeRef.current);\n\n\t\t\t\t// Calculate the oscillating amplitude based on the time and the amplitude oscillation configuration.\n\t\t\t\tconst oscillatingAmplitude =\n\t\t\t\t\twaveConfig.amplitudeOscillation === 0\n\t\t\t\t\t\t? amplitudeRef.current\n\t\t\t\t\t\t: amplitudeRef.current *\n\t\t\t\t\t\t\t(1 + Math.sin(time) * waveConfig.amplitudeOscillation);\n\n\t\t\t\tctx.clearRect(\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t\tlayoutSizeRef.current.width,\n\t\t\t\t\tlayoutSizeRef.current.height,\n\t\t\t\t);\n\n\t\t\t\tdrawWaveLayers({\n\t\t\t\t\tctx,\n\t\t\t\t\tcolors,\n\t\t\t\t\tconfig: waveConfig,\n\t\t\t\t\tgradientCache: gradientCacheRef.current,\n\t\t\t\t\tlayoutWidth: layoutSizeRef.current.width,\n\t\t\t\t\tlayoutHeight: layoutSizeRef.current.height,\n\t\t\t\t\toscillatingAmplitude,\n\t\t\t\t\ttimestamp,\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tconst animate = () => {\n\t\t\t\tif (!shouldAnimate()) return;\n\n\t\t\t\trenderFrame();\n\t\t\t\tanimationFrameId = requestAnimationFrame(animate);\n\t\t\t};\n\n\t\t\t// Set the render frame reference to the render frame function.\n\t\t\trenderFrameRef.current = renderFrame;\n\t\t\tresizeCanvas();\n\t\t\trenderFrame();\n\n\t\t\t// Start the animation if the animation should run.\n\t\t\tif (shouldAnimate()) animationFrameId = requestAnimationFrame(animate);\n\n\t\t\tconst handleVisibilityChange = () => {\n\t\t\t\tif (!document.hidden && shouldAnimate()) {\n\t\t\t\t\trenderFrame();\n\t\t\t\t\tanimationFrameId = requestAnimationFrame(animate);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst handleReducedMotionChange = () => {\n\t\t\t\tif (shouldAnimate()) {\n\t\t\t\t\trenderFrame();\n\t\t\t\t\tanimationFrameId = requestAnimationFrame(animate);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tdocument.addEventListener(\"visibilitychange\", handleVisibilityChange);\n\t\t\treducedMotionQuery?.addEventListener(\"change\", handleReducedMotionChange);\n\n\t\t\treturn () => {\n\t\t\t\trenderFrameRef.current = null;\n\t\t\t\tcancelAnimationFrame(animationFrameId);\n\t\t\t\tdocument.removeEventListener(\n\t\t\t\t\t\"visibilitychange\",\n\t\t\t\t\thandleVisibilityChange,\n\t\t\t\t);\n\t\t\t\treducedMotionQuery?.removeEventListener(\n\t\t\t\t\t\"change\",\n\t\t\t\t\thandleReducedMotionChange,\n\t\t\t\t);\n\t\t\t};\n\t\t}, [colors, paused, respectReducedMotion, resizeCanvas, waveConfig]);\n\n\t\treturn (\n\t\t\t<canvas\n\t\t\t\tref={canvasRef}\n\t\t\t\taria-hidden={props[\"aria-hidden\"] ?? true}\n\t\t\t\twidth=\"100%\"\n\t\t\t\theight=\"auto\"\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t},\n);\n\nWavesComponent.displayName = \"Waves\";\n\nexport const Waves = memo(WavesComponent);\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-animated-waves",
|
|
3
3
|
"description": "Canvas-based animated waves for ReactJS",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
7
7
|
"license": "MIT",
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
"react-dom": "^19.1.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@
|
|
27
|
-
"@
|
|
28
|
-
"@
|
|
29
|
-
"@commitlint/
|
|
30
|
-
"@
|
|
26
|
+
"@biomejs/biome": "2.5.0",
|
|
27
|
+
"@fontsource-variable/geist": "^5.2.8",
|
|
28
|
+
"@tailwindcss/vite": "^4.3.0",
|
|
29
|
+
"@commitlint/cli": "^21.0.2",
|
|
30
|
+
"@commitlint/config-conventional": "^21.0.2",
|
|
31
31
|
"@radix-ui/react-collapsible": "^1.1.13",
|
|
32
32
|
"@radix-ui/react-label": "^2.1.9",
|
|
33
33
|
"@radix-ui/react-popover": "^1.1.16",
|
|
@@ -38,31 +38,32 @@
|
|
|
38
38
|
"@radix-ui/react-slot": "^1.2.5",
|
|
39
39
|
"@radix-ui/react-switch": "^1.3.0",
|
|
40
40
|
"@radix-ui/react-tabs": "^1.1.14",
|
|
41
|
+
"@radix-ui/react-tooltip": "^1.2.10",
|
|
41
42
|
"@stryker-mutator/core": "^9.6.1",
|
|
42
43
|
"@stryker-mutator/typescript-checker": "^9.6.1",
|
|
43
44
|
"@stryker-mutator/vitest-runner": "^9.6.1",
|
|
44
45
|
"@testing-library/jest-dom": "^6.6.3",
|
|
45
46
|
"@testing-library/react": "^16.3.2",
|
|
46
|
-
"@types/node": "^25.9.
|
|
47
|
-
"@types/react": "^19.2.
|
|
47
|
+
"@types/node": "^25.9.2",
|
|
48
|
+
"@types/react": "^19.2.17",
|
|
48
49
|
"@types/react-color": "^3.0.13",
|
|
49
50
|
"@types/react-dom": "^19.1.4",
|
|
50
51
|
"@vitejs/plugin-react": "^6.0.2",
|
|
51
|
-
"@vitest/coverage-v8": "4.1.
|
|
52
|
+
"@vitest/coverage-v8": "4.1.8",
|
|
52
53
|
"class-variance-authority": "^0.7.1",
|
|
53
54
|
"clsx": "^2.1.1",
|
|
54
55
|
"git-cliff": "^2.13.1",
|
|
55
|
-
"happy-dom": "^20.
|
|
56
|
+
"happy-dom": "^20.10.2",
|
|
56
57
|
"husky": "^9.1.7",
|
|
57
|
-
"lint-staged": "^17.0.
|
|
58
|
-
"lucide-react": "^1.
|
|
58
|
+
"lint-staged": "^17.0.7",
|
|
59
|
+
"lucide-react": "^1.18.0",
|
|
59
60
|
"react-color": "^2.19.3",
|
|
60
61
|
"tailwind-merge": "^3.6.0",
|
|
61
|
-
"tailwindcss": "^4.
|
|
62
|
-
"tsdown": "^0.22.
|
|
62
|
+
"tailwindcss": "^4.3.0",
|
|
63
|
+
"tsdown": "^0.22.2",
|
|
63
64
|
"typescript": "^5.9.2",
|
|
64
|
-
"vite": "^8.0.
|
|
65
|
-
"vitest": "^4.1.
|
|
65
|
+
"vite": "^8.0.16",
|
|
66
|
+
"vitest": "^4.1.8"
|
|
66
67
|
},
|
|
67
68
|
"scripts": {
|
|
68
69
|
"build": "tsdown",
|
|
@@ -70,6 +71,8 @@
|
|
|
70
71
|
"test": "vitest",
|
|
71
72
|
"test-mutations": "stryker run",
|
|
72
73
|
"playground": "vite --config playground/vite.config.ts",
|
|
74
|
+
"playground:build": "vite build --config playground/vite.config.ts",
|
|
75
|
+
"playground:deploy": "npx wrangler deploy",
|
|
73
76
|
"cov": "vitest run --coverage --passWithNoTests",
|
|
74
77
|
"typecheck": "tsc --noEmit",
|
|
75
78
|
"lint": "pnpm exec biome lint --write",
|