diff --git a/README.md b/README.md
index b5366be..79fe160 100644
--- a/README.md
+++ b/README.md
@@ -66,11 +66,12 @@ All available properties are detailed below.
| `images` | An array of images that will be rendered as the snowflakes instead of the default circle shapes. | `undefined` |
| `radius` | The minimum and maximum radius of the snowflake in pixels.
The value for each snowflake will be randomly selected within this range. | `[0.5, 3.0]` |
| `rotationSpeed` | The minimum and maximum rotation speed of the snowflake (in degrees of rotation per frame).
The rotation speed determines how quickly the snowflake rotates when an image is being rendered.
The value for each snowflake will be randomly selected within this range. | `[-1.0, 1.0]` |
+| `enable3DRotation`| Enable 3D rotation effect (like falling leaves).
When enabled, snowflakes will rotate on X, Y, and Z axes, creating a more realistic 3D tumbling effect.
Works with both images and circles. | `false` |
| `snowflakeCount` | The number of snowflakes to be rendered. | `150` |
| `speed` | The minimum and maximum speed of the snowflake (in pixels per frame).
The speed determines how quickly the snowflake moves along the y axis (vertical speed).
The value for each snowflake will be randomly selected within this range. | `[1.0, 3.0]` |
| `style` | Any style properties that will be passed to the canvas element. | `undefined` |
| `wind` | The minimum and maximum wind of the snowflake (in pixels per frame).
The wind determines how quickly the snowflake moves along the x axis (horizontal speed).
The value for each snowflake will be randomly selected within this range. | `[-0.5, 2.0]` |
-| `opacity` | The minimum and maximum opacity of the snowflake.
The value for each snowflake will be randomly selected within this range. | `[1, 1]` |
+| `opacity` | The minimum and maximum opacity of the snowflake.
The value for each snowflake will be randomly selected within this range. | `[1, 1]` |
## Using Images
diff --git a/packages/demo/src/App.tsx b/packages/demo/src/App.tsx
index 35fd09e..6ddd651 100644
--- a/packages/demo/src/App.tsx
+++ b/packages/demo/src/App.tsx
@@ -14,7 +14,7 @@ snowflake.src = logo
const images = [snowflake]
const App = () => {
- const { color, snowflakeCount, radius, speed, wind, useImages, opacity } = useSettingsStore()
+ const { color, snowflakeCount, radius, speed, wind, useImages, opacity, enable3DRotation } = useSettingsStore()
return (
+
+ settings.update({ enable3DRotation: event.target.checked })}
+ />
+ }
+ label="Enable 3D Rotation"
+ />
+
{settings.useImages ? (
<>
diff --git a/packages/demo/src/settings.tsx b/packages/demo/src/settings.tsx
index 0f3f728..b06e5a8 100644
--- a/packages/demo/src/settings.tsx
+++ b/packages/demo/src/settings.tsx
@@ -16,6 +16,7 @@ export const useSettingsStore = create((set) => ({
rotationSpeed: [-1.0, 1.0],
opacity: [0.1, 0.2],
useImages: false,
+ enable3DRotation: false,
update: (changes) => set(changes),
setUseImages: (useImages) => {
if (useImages) {
diff --git a/packages/react-snowfall/lib/Snowfall.d.ts b/packages/react-snowfall/lib/Snowfall.d.ts
index ef3401a..347831d 100644
--- a/packages/react-snowfall/lib/Snowfall.d.ts
+++ b/packages/react-snowfall/lib/Snowfall.d.ts
@@ -6,5 +6,5 @@ export interface SnowfallProps extends Partial {
*/
style?: React.CSSProperties;
}
-export declare const Snowfall: ({ color, changeFrequency, radius, speed, wind, rotationSpeed, opacity, snowflakeCount, images, style, }?: SnowfallProps) => JSX.Element;
+export declare const Snowfall: ({ color, changeFrequency, radius, speed, wind, rotationSpeed, opacity, snowflakeCount, images, enable3DRotation, style, }?: SnowfallProps) => JSX.Element;
export default Snowfall;
diff --git a/packages/react-snowfall/lib/Snowfall.js b/packages/react-snowfall/lib/Snowfall.js
index dcfd9c5..303e8ff 100644
--- a/packages/react-snowfall/lib/Snowfall.js
+++ b/packages/react-snowfall/lib/Snowfall.js
@@ -2,7 +2,7 @@ import React, { useEffect, useRef } from 'react';
import { SnowfallCanvas } from './SnowfallCanvas.js';
import { defaultConfig } from './Snowflake.js';
import { useComponentSize, useDeepMemo, useSnowfallStyle } from './hooks.js';
-export const Snowfall = ({ color = defaultConfig.color, changeFrequency = defaultConfig.changeFrequency, radius = defaultConfig.radius, speed = defaultConfig.speed, wind = defaultConfig.wind, rotationSpeed = defaultConfig.rotationSpeed, opacity = defaultConfig.opacity, snowflakeCount = 150, images, style, } = {}) => {
+export const Snowfall = ({ color = defaultConfig.color, changeFrequency = defaultConfig.changeFrequency, radius = defaultConfig.radius, speed = defaultConfig.speed, wind = defaultConfig.wind, rotationSpeed = defaultConfig.rotationSpeed, opacity = defaultConfig.opacity, snowflakeCount = 150, images, enable3DRotation = defaultConfig.enable3DRotation, style, } = {}) => {
const mergedStyle = useSnowfallStyle(style);
const canvasRef = useRef(null);
const canvasSize = useComponentSize(canvasRef);
@@ -16,6 +16,7 @@ export const Snowfall = ({ color = defaultConfig.color, changeFrequency = defaul
images,
snowflakeCount,
opacity,
+ enable3DRotation,
});
// A reference to the config used for creating the initial instance
const configRef = useRef(config);
diff --git a/packages/react-snowfall/lib/Snowfall.js.map b/packages/react-snowfall/lib/Snowfall.js.map
index c5e33b3..44c508a 100644
--- a/packages/react-snowfall/lib/Snowfall.js.map
+++ b/packages/react-snowfall/lib/Snowfall.js.map
@@ -1 +1 @@
-{"version":3,"file":"Snowfall.js","sourceRoot":"","sources":["../src/Snowfall.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAChD,OAAO,EAAE,cAAc,EAAwB,MAAM,qBAAqB,CAAA;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC9C,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAS5E,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,EACvB,KAAK,GAAG,aAAa,CAAC,KAAK,EAC3B,eAAe,GAAG,aAAa,CAAC,eAAe,EAC/C,MAAM,GAAG,aAAa,CAAC,MAAM,EAC7B,KAAK,GAAG,aAAa,CAAC,KAAK,EAC3B,IAAI,GAAG,aAAa,CAAC,IAAI,EACzB,aAAa,GAAG,aAAa,CAAC,aAAa,EAC3C,OAAO,GAAG,aAAa,CAAC,OAAO,EAC/B,cAAc,GAAG,GAAG,EACpB,MAAM,EACN,KAAK,MACY,EAAE,EAAe,EAAE;IACpC,MAAM,WAAW,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAA;IAE3C,MAAM,SAAS,GAAG,MAAM,CAAoB,IAAI,CAAC,CAAA;IACjD,MAAM,UAAU,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAA;IAE9C,MAAM,MAAM,GAAG,WAAW,CAAuB;QAC/C,KAAK;QACL,eAAe;QACf,MAAM;QACN,KAAK;QACL,IAAI;QACJ,aAAa;QACb,MAAM;QACN,cAAc;QACd,OAAO;KACR,CAAC,CAAA;IAEF,mEAAmE;IACnE,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAA;IAEhC,MAAM,iBAAiB,GAAG,MAAM,EAAkB,CAAA;IAElD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,iBAAiB,CAAC,OAAO,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;YACpD,iBAAiB,CAAC,OAAO,GAAG,IAAI,cAAc,CAAC,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,OAAO,CAAC,CAAA;QACtF,CAAC;QAED,OAAO,GAAG,EAAE;;YACV,MAAA,iBAAiB,CAAC,OAAO,0CAAE,KAAK,EAAE,CAAA;YAClC,iBAAiB,CAAC,OAAO,GAAG,SAAS,CAAA;QACvC,CAAC,CAAA;IACH,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,iBAAiB,CAAC,OAAO,EAAE,CAAC;YAC9B,iBAAiB,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;QAChD,CAAC;IACH,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAEZ,OAAO,CACL,gCACE,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,UAAU,CAAC,MAAM,EACzB,KAAK,EAAE,UAAU,CAAC,KAAK,EACvB,KAAK,EAAE,WAAW,iBACN,gBAAgB,GAC5B,CACH,CAAA;AACH,CAAC,CAAA;AAED,eAAe,QAAQ,CAAA"}
\ No newline at end of file
+{"version":3,"file":"Snowfall.js","sourceRoot":"","sources":["../src/Snowfall.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAChD,OAAO,EAAE,cAAc,EAAwB,MAAM,qBAAqB,CAAA;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC9C,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAS5E,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,EACvB,KAAK,GAAG,aAAa,CAAC,KAAK,EAC3B,eAAe,GAAG,aAAa,CAAC,eAAe,EAC/C,MAAM,GAAG,aAAa,CAAC,MAAM,EAC7B,KAAK,GAAG,aAAa,CAAC,KAAK,EAC3B,IAAI,GAAG,aAAa,CAAC,IAAI,EACzB,aAAa,GAAG,aAAa,CAAC,aAAa,EAC3C,OAAO,GAAG,aAAa,CAAC,OAAO,EAC/B,cAAc,GAAG,GAAG,EACpB,MAAM,EACN,gBAAgB,GAAG,aAAa,CAAC,gBAAgB,EACjD,KAAK,MACY,EAAE,EAAe,EAAE;IACpC,MAAM,WAAW,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAA;IAE3C,MAAM,SAAS,GAAG,MAAM,CAAoB,IAAI,CAAC,CAAA;IACjD,MAAM,UAAU,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAA;IAE9C,MAAM,MAAM,GAAG,WAAW,CAAuB;QAC/C,KAAK;QACL,eAAe;QACf,MAAM;QACN,KAAK;QACL,IAAI;QACJ,aAAa;QACb,MAAM;QACN,cAAc;QACd,OAAO;QACP,gBAAgB;KACjB,CAAC,CAAA;IAEF,mEAAmE;IACnE,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAA;IAEhC,MAAM,iBAAiB,GAAG,MAAM,EAAkB,CAAA;IAElD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,iBAAiB,CAAC,OAAO,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;YACpD,iBAAiB,CAAC,OAAO,GAAG,IAAI,cAAc,CAAC,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,OAAO,CAAC,CAAA;QACtF,CAAC;QAED,OAAO,GAAG,EAAE;;YACV,MAAA,iBAAiB,CAAC,OAAO,0CAAE,KAAK,EAAE,CAAA;YAClC,iBAAiB,CAAC,OAAO,GAAG,SAAS,CAAA;QACvC,CAAC,CAAA;IACH,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,iBAAiB,CAAC,OAAO,EAAE,CAAC;YAC9B,iBAAiB,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;QAChD,CAAC;IACH,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAEZ,OAAO,CACL,gCACE,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,UAAU,CAAC,MAAM,EACzB,KAAK,EAAE,UAAU,CAAC,KAAK,EACvB,KAAK,EAAE,WAAW,iBACN,gBAAgB,GAC5B,CACH,CAAA;AACH,CAAC,CAAA;AAED,eAAe,QAAQ,CAAA"}
\ No newline at end of file
diff --git a/packages/react-snowfall/lib/SnowfallCanvas.js b/packages/react-snowfall/lib/SnowfallCanvas.js
index 7be8a9c..9827e4f 100644
--- a/packages/react-snowfall/lib/SnowfallCanvas.js
+++ b/packages/react-snowfall/lib/SnowfallCanvas.js
@@ -76,13 +76,22 @@ export class SnowfallCanvas {
}
return;
}
- // Not using images, draw circles in a single path
- ctx.beginPath();
- for (const snowflake of snowflakes) {
- snowflake.drawCircle(ctx);
+ // Not using images
+ // If 3D rotation is enabled, draw each circle individually with transform
+ if (this.config.enable3DRotation) {
+ for (const snowflake of snowflakes) {
+ snowflake.drawCircle3D(ctx, this.config.color);
+ }
+ }
+ else {
+ // Draw circles in a single path for better performance
+ ctx.beginPath();
+ for (const snowflake of snowflakes) {
+ snowflake.drawCircle(ctx);
+ }
+ ctx.fillStyle = this.config.color;
+ ctx.fill();
}
- ctx.fillStyle = this.config.color;
- ctx.fill();
}
/**
* The animation loop, will calculate the time since the last render and update
diff --git a/packages/react-snowfall/lib/SnowfallCanvas.js.map b/packages/react-snowfall/lib/SnowfallCanvas.js.map
index 33091af..7d6e25e 100644
--- a/packages/react-snowfall/lib/SnowfallCanvas.js.map
+++ b/packages/react-snowfall/lib/SnowfallCanvas.js.map
@@ -1 +1 @@
-{"version":3,"file":"SnowfallCanvas.js","sourceRoot":"","sources":["../src/SnowfallCanvas.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,SAAS,EAAE,EAAmB,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAW7C,MAAM,OAAO,cAAc;IAMzB,IAAI,GAAG;QACL,OAAO,uBAAA,IAAI,2BAAK,CAAA;IAClB,CAAC;IAGD,IAAI,MAAM;QACR,OAAO,uBAAA,IAAI,8BAAQ,CAAA;IACrB,CAAC;IACD,IAAI,MAAM,CAAC,MAAyB;QAClC,uBAAA,IAAI,0BAAW,MAAM,MAAA,CAAA;QACrB,uBAAA,IAAI,uBAAQ,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,MAAA,CAAA;IACrC,CAAC;IAED,YAAY,MAAyB,EAAE,MAAqC;QAlBpE,eAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACvB,eAAU,GAAgB,EAAE,CAAA;QAGpC,sCAAqC;QAKrC,yCAA0B;QAUxB,uBAAA,IAAI,0BAAW,MAAM,MAAA,CAAA;QACrB,uBAAA,IAAI,uBAAQ,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,MAAA,CAAA;QACnC,IAAI,CAAC,MAAM,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,GAAG,aAAa,EAAE,GAAG,MAAM,EAAE,CAAA;QAClE,IAAI,CAAC,UAAU,GAAG,EAAE,CAAA;QACpB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,IAAI,GAAG,EAAE,MAAM,CAAC,CAAA;QAC1F,IAAI,CAAC,IAAI,EAAE,CAAA;IACb,CAAC;IAED;;;;OAIG;IACH,YAAY,CAAC,MAAqC;QAChD,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAA;QAE3C,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,cAAc,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAA;QAE1E,IAAI,cAAc,GAAG,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC,UAAU,EAAE,GAAG,SAAS,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC,CAAA;QAC5G,CAAC;QAED,IAAI,cAAc,GAAG,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,CAAA;QACxE,CAAC;QAED,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACxC,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACrC,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,MAAM,CAAC,YAAY,GAAG,CAAC;QAC7B,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,CAAA;QAExC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM;YAAE,OAAM;QAE3B,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,MAAM,CAAA;QAE5C,wCAAwC;QACxC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,SAAS,CAAC,MAAM,CAAC,WAAW,EAAE,YAAY,EAAE,YAAY,CAAC,CAAA;QAC3D,CAAC;QAED,wBAAwB;QACxB,GAAG,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;QAClC,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,WAAW,EAAE,YAAY,CAAC,CAAA;QAE9C,gDAAgD;QAChD,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxD,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;gBACnC,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;YAC1B,CAAC;YACD,OAAM;QACR,CAAC;QAED,kDAAkD;QAClD,GAAG,CAAC,SAAS,EAAE,CAAA;QACf,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;QAC3B,CAAC;QACD,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,KAAM,CAAA;QAClC,GAAG,CAAC,IAAI,EAAE,CAAA;IACZ,CAAC;IAID;;;OAGG;IACK,IAAI;QACV,sFAAsF;QACtF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,CAAA;QAC7C,IAAI,CAAC,UAAU,GAAG,GAAG,CAAA;QAErB,qDAAqD;QACrD,MAAM,YAAY,GAAG,QAAQ,GAAG,eAAe,CAAA;QAE/C,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;QAEzB,IAAI,CAAC,cAAc,GAAG,qBAAqB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IAChE,CAAC;IAED,mCAAmC;IACnC,IAAI;QACF,IAAI,CAAC,IAAI,EAAE,CAAA;IACb,CAAC;IAED,2BAA2B;IAC3B,KAAK;QACH,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,oBAAoB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;YACzC,IAAI,CAAC,cAAc,GAAG,SAAS,CAAA;QACjC,CAAC;IACH,CAAC;CACF;;AAED,eAAe,cAAc,CAAA"}
\ No newline at end of file
+{"version":3,"file":"SnowfallCanvas.js","sourceRoot":"","sources":["../src/SnowfallCanvas.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/packages/react-snowfall/lib/Snowflake.d.ts b/packages/react-snowfall/lib/Snowflake.d.ts
index 5fc2842..601ec7c 100644
--- a/packages/react-snowfall/lib/Snowflake.d.ts
+++ b/packages/react-snowfall/lib/Snowflake.d.ts
@@ -64,6 +64,15 @@ export interface SnowflakeProps {
* The default value is `[1, 1]`.
*/
opacity: [number, number];
+ /**
+ * Enable 3D rotation effect (like falling leaves).
+ *
+ * When enabled, snowflakes will rotate on X and Y axes in addition to Z axis,
+ * creating a more realistic 3D tumbling effect.
+ *
+ * The default value is `false`.
+ */
+ enable3DRotation?: boolean;
}
export type SnowflakeConfig = Partial;
export declare const defaultConfig: SnowflakeProps;
@@ -90,6 +99,15 @@ declare class Snowflake {
private updateTargetParams;
update(offsetWidth: number, offsetHeight: number, framesPassed?: number): void;
private getImageOffscreenCanvas;
+ /**
+ * Applies 3D rotation transform to the canvas context.
+ * This method calculates and applies the transformation matrix for 3D rotation effects.
+ *
+ * @param ctx The canvas context to apply the transform to
+ * @param x The x position to translate to
+ * @param y The y position to translate to
+ */
+ private apply3DTransform;
/**
* Draws a circular snowflake to the canvas.
*
@@ -107,6 +125,15 @@ declare class Snowflake {
* @param ctx The canvas context to draw to
*/
drawCircle(ctx: CanvasRenderingContext2D): void;
+ /**
+ * Draws a circular snowflake with 3D rotation effect to the canvas.
+ *
+ * This method is used when 3D rotation is enabled and images are not being used.
+ *
+ * @param ctx The canvas context to draw to
+ * @param color The color to fill the circle with
+ */
+ drawCircle3D(ctx: CanvasRenderingContext2D, color: string): void;
/**
* Draws an image-based snowflake to the canvas.
*
diff --git a/packages/react-snowfall/lib/Snowflake.js b/packages/react-snowfall/lib/Snowflake.js
index 9888c02..e841b97 100644
--- a/packages/react-snowfall/lib/Snowflake.js
+++ b/packages/react-snowfall/lib/Snowflake.js
@@ -8,6 +8,7 @@ export const defaultConfig = {
changeFrequency: 200,
rotationSpeed: [-1.0, 1.0],
opacity: [1, 1],
+ enable3DRotation: false,
};
/**
* An individual snowflake that will update it's location every call to `update`
@@ -33,7 +34,7 @@ class Snowflake {
// Set custom config
this.updateConfig(config);
// Setting initial parameters
- const { radius, wind, speed, rotationSpeed, opacity } = this.config;
+ const { radius, wind, speed, rotationSpeed, opacity, enable3DRotation } = this.config;
this.params = {
x: random(0, canvas.offsetWidth),
y: random(-canvas.offsetHeight, 0),
@@ -47,6 +48,13 @@ class Snowflake {
nextRotationSpeed: random(...rotationSpeed),
opacity: random(...opacity),
hasNextOpacity: false,
+ // Initialize 3D rotation parameters
+ rotationX: enable3DRotation ? random(0, 360) : 0,
+ rotationY: enable3DRotation ? random(0, 360) : 0,
+ rotationSpeedX: enable3DRotation ? random(-2.0, 2.0) : 0,
+ rotationSpeedY: enable3DRotation ? random(-2.0, 2.0) : 0,
+ nextRotationSpeedX: enable3DRotation ? random(-2.0, 2.0) : 0,
+ nextRotationSpeedY: enable3DRotation ? random(-2.0, 2.0) : 0,
};
this.framesSinceLastUpdate = 0;
}
@@ -79,6 +87,10 @@ class Snowflake {
if (this.image) {
this.params.nextRotationSpeed = random(...this.config.rotationSpeed);
}
+ if (this.config.enable3DRotation) {
+ this.params.nextRotationSpeedX = random(-2.0, 2.0);
+ this.params.nextRotationSpeedY = random(-2.0, 2.0);
+ }
}
update(offsetWidth, offsetHeight, framesPassed = 1) {
const { x, y, rotation, rotationSpeed, nextRotationSpeed, wind, speed, nextWind, nextSpeed, radius } = this.params;
@@ -95,13 +107,22 @@ class Snowflake {
this.params.y = -radius;
}
// Apply rotation
- if (this.image) {
+ if (this.image || this.config.enable3DRotation) {
this.params.rotation = (rotation + rotationSpeed) % 360;
}
+ // Apply 3D rotation if enabled
+ if (this.config.enable3DRotation) {
+ this.params.rotationX = (this.params.rotationX + this.params.rotationSpeedX * framesPassed) % 360;
+ this.params.rotationY = (this.params.rotationY + this.params.rotationSpeedY * framesPassed) % 360;
+ }
// Update the wind, speed and rotation towards the desired values
this.params.speed = lerp(speed, nextSpeed, 0.01);
this.params.wind = lerp(wind, nextWind, 0.01);
this.params.rotationSpeed = lerp(rotationSpeed, nextRotationSpeed, 0.01);
+ if (this.config.enable3DRotation) {
+ this.params.rotationSpeedX = lerp(this.params.rotationSpeedX, this.params.nextRotationSpeedX, 0.01);
+ this.params.rotationSpeedY = lerp(this.params.rotationSpeedY, this.params.nextRotationSpeedY, 0.01);
+ }
if (this.framesSinceLastUpdate++ > this.config.changeFrequency) {
this.updateTargetParams();
this.framesSinceLastUpdate = 0;
@@ -125,6 +146,55 @@ class Snowflake {
}
return (_b = sizes[size]) !== null && _b !== void 0 ? _b : image;
}
+ /**
+ * Applies 3D rotation transform to the canvas context.
+ * This method calculates and applies the transformation matrix for 3D rotation effects.
+ *
+ * @param ctx The canvas context to apply the transform to
+ * @param x The x position to translate to
+ * @param y The y position to translate to
+ */
+ apply3DTransform(ctx, x, y) {
+ if (this.config.enable3DRotation) {
+ const { rotationX, rotationY } = this.params;
+ const rotation = this.params.rotation || 0;
+ // Convert degrees to radians
+ const radX = (rotationX * Math.PI) / 180;
+ const radY = (rotationY * Math.PI) / 180;
+ const radZ = (rotation * Math.PI) / 180;
+ // Calculate 3D rotation matrices
+ const cosX = Math.cos(radX);
+ const sinX = Math.sin(radX);
+ const cosY = Math.cos(radY);
+ const sinY = Math.sin(radY);
+ const cosZ = Math.cos(radZ);
+ const sinZ = Math.sin(radZ);
+ // Combined rotation matrix (Z * Y * X)
+ // This creates a 3D tumbling effect
+ const a = cosZ * cosY;
+ const b = cosZ * sinY * sinX - sinZ * cosX;
+ const c = cosZ * sinY * cosX + sinZ * sinX;
+ const d = sinZ * cosY;
+ // Apply perspective scaling based on rotation (simulates depth)
+ const perspectiveScale = 0.5 + 0.5 * cosX * cosY;
+ const scaleX = perspectiveScale;
+ const scaleY = perspectiveScale;
+ // Apply the transform
+ // The arguments for setTransform are: a, b, c, d, e, f
+ // a (scaleX), b (skewY), c (skewX), d (scaleY), e (translateX), f (translateY)
+ ctx.setTransform(a * scaleX, b * scaleX, c * scaleY, d * scaleY, x, y);
+ }
+ else {
+ // Original 2D rotation (only for images)
+ const rotation = this.params.rotation || 0;
+ const radian = (rotation * Math.PI) / 180;
+ const cos = Math.cos(radian);
+ const sin = Math.sin(radian);
+ // The arguments for setTransform are: a, b, c, d, e, f
+ // a (scaleX), b (skewY), c (skewX), d (scaleY), e (translateX), f (translateY)
+ ctx.setTransform(cos, sin, -sin, cos, x, y);
+ }
+ }
/**
* Draws a circular snowflake to the canvas.
*
@@ -142,9 +212,37 @@ class Snowflake {
* @param ctx The canvas context to draw to
*/
drawCircle(ctx) {
+ // If 3D rotation is enabled, we need to draw individually with transform
+ // This method is called when 3D rotation is disabled (for performance)
ctx.moveTo(this.params.x, this.params.y);
ctx.arc(this.params.x, this.params.y, this.params.radius, 0, twoPi);
}
+ /**
+ * Draws a circular snowflake with 3D rotation effect to the canvas.
+ *
+ * This method is used when 3D rotation is enabled and images are not being used.
+ *
+ * @param ctx The canvas context to draw to
+ * @param color The color to fill the circle with
+ */
+ drawCircle3D(ctx, color) {
+ const { x, y, radius } = this.params;
+ ctx.save();
+ // Apply 3D rotation transform
+ if (this.config.enable3DRotation) {
+ this.apply3DTransform(ctx, x, y);
+ }
+ else {
+ // No transform needed for circles without 3D rotation
+ ctx.translate(x, y);
+ }
+ // Draw the circle
+ ctx.beginPath();
+ ctx.arc(0, 0, radius, 0, twoPi);
+ ctx.fillStyle = color;
+ ctx.fill();
+ ctx.restore();
+ }
/**
* Draws an image-based snowflake to the canvas.
*
@@ -153,26 +251,20 @@ class Snowflake {
* @param ctx The canvas context to draw to
*/
drawImage(ctx) {
- const { x, y, rotation, radius } = this.params;
- const radian = (rotation * Math.PI) / 180;
- const cos = Math.cos(radian);
- const sin = Math.sin(radian);
- // Save the current state to avoid affecting other drawings if changing the opacity
+ const { x, y, radius } = this.params;
+ // Save the current state to avoid affecting other drawings
+ ctx.save();
+ // Set opacity if needed
if (this.params.opacity !== 1) {
- ctx.save();
- ctx.globalAlpha = this.params.opacity; // Set the global alpha to the snowflake's opacity
+ ctx.globalAlpha = this.params.opacity;
}
- // Translate to the location that we will be drawing the snowflake, including any rotation that needs to be applied
- // The arguments for setTransform are: a, b, c, d, e, f
- // a (scaleX), b (skewY), c (skewX), d (scaleY), e (translateX), f (translateY)
- ctx.setTransform(cos, sin, -sin, cos, x, y);
+ // Apply 3D or 2D rotation transform
+ this.apply3DTransform(ctx, x, y);
// Draw the image with the center of the image at the center of the current location
const image = this.getImageOffscreenCanvas(this.image, radius);
ctx.drawImage(image, -(radius / 2), -(radius / 2), radius, radius);
- // Reset the transform to avoid affecting other drawings if we were changing the opacity
- if (this.params.opacity !== 1) {
- ctx.restore();
- }
+ // Restore the transform
+ ctx.restore();
}
}
Snowflake.offscreenCanvases = new WeakMap();
diff --git a/packages/react-snowfall/lib/Snowflake.js.map b/packages/react-snowfall/lib/Snowflake.js.map
index e5361c8..f6e8b2c 100644
--- a/packages/react-snowfall/lib/Snowflake.js.map
+++ b/packages/react-snowfall/lib/Snowflake.js.map
@@ -1 +1 @@
-{"version":3,"file":"Snowflake.js","sourceRoot":"","sources":["../src/Snowflake.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"Snowflake.js","sourceRoot":"","sources":["../src/Snowflake.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/packages/react-snowfall/src/Snowfall.tsx b/packages/react-snowfall/src/Snowfall.tsx
index 3217448..bac4b90 100644
--- a/packages/react-snowfall/src/Snowfall.tsx
+++ b/packages/react-snowfall/src/Snowfall.tsx
@@ -20,6 +20,7 @@ export const Snowfall = ({
opacity = defaultConfig.opacity,
snowflakeCount = 150,
images,
+ enable3DRotation = defaultConfig.enable3DRotation,
style,
}: SnowfallProps = {}): JSX.Element => {
const mergedStyle = useSnowfallStyle(style)
@@ -37,6 +38,7 @@ export const Snowfall = ({
images,
snowflakeCount,
opacity,
+ enable3DRotation,
})
// A reference to the config used for creating the initial instance
diff --git a/packages/react-snowfall/src/SnowfallCanvas.ts b/packages/react-snowfall/src/SnowfallCanvas.ts
index 2be11d2..860ba90 100644
--- a/packages/react-snowfall/src/SnowfallCanvas.ts
+++ b/packages/react-snowfall/src/SnowfallCanvas.ts
@@ -89,13 +89,21 @@ export class SnowfallCanvas {
return
}
- // Not using images, draw circles in a single path
- ctx.beginPath()
- for (const snowflake of snowflakes) {
- snowflake.drawCircle(ctx)
+ // Not using images
+ // If 3D rotation is enabled, draw each circle individually with transform
+ if (this.config.enable3DRotation) {
+ for (const snowflake of snowflakes) {
+ snowflake.drawCircle3D(ctx, this.config.color!)
+ }
+ } else {
+ // Draw circles in a single path for better performance
+ ctx.beginPath()
+ for (const snowflake of snowflakes) {
+ snowflake.drawCircle(ctx)
+ }
+ ctx.fillStyle = this.config.color!
+ ctx.fill()
}
- ctx.fillStyle = this.config.color!
- ctx.fill()
}
private animationFrame: number | undefined
diff --git a/packages/react-snowfall/src/Snowflake.ts b/packages/react-snowfall/src/Snowflake.ts
index e9fe5e2..8e46a47 100644
--- a/packages/react-snowfall/src/Snowflake.ts
+++ b/packages/react-snowfall/src/Snowflake.ts
@@ -67,6 +67,15 @@ export interface SnowflakeProps {
* The default value is `[1, 1]`.
*/
opacity: [number, number]
+ /**
+ * Enable 3D rotation effect (like falling leaves).
+ *
+ * When enabled, snowflakes will rotate on X and Y axes in addition to Z axis,
+ * creating a more realistic 3D tumbling effect.
+ *
+ * The default value is `false`.
+ */
+ enable3DRotation?: boolean
}
export type SnowflakeConfig = Partial
@@ -79,6 +88,7 @@ export const defaultConfig: SnowflakeProps = {
changeFrequency: 200,
rotationSpeed: [-1.0, 1.0],
opacity: [1, 1],
+ enable3DRotation: false,
}
interface SnowflakeParams {
@@ -94,6 +104,13 @@ interface SnowflakeParams {
nextRotationSpeed: number
opacity: number
hasNextOpacity: boolean
+ // 3D rotation parameters
+ rotationX: number
+ rotationY: number
+ rotationSpeedX: number
+ rotationSpeedY: number
+ nextRotationSpeedX: number
+ nextRotationSpeedY: number
}
/**
@@ -131,7 +148,7 @@ class Snowflake {
this.updateConfig(config)
// Setting initial parameters
- const { radius, wind, speed, rotationSpeed, opacity } = this.config
+ const { radius, wind, speed, rotationSpeed, opacity, enable3DRotation } = this.config
this.params = {
x: random(0, canvas.offsetWidth),
@@ -146,6 +163,13 @@ class Snowflake {
nextRotationSpeed: random(...rotationSpeed),
opacity: random(...opacity),
hasNextOpacity: false,
+ // Initialize 3D rotation parameters
+ rotationX: enable3DRotation ? random(0, 360) : 0,
+ rotationY: enable3DRotation ? random(0, 360) : 0,
+ rotationSpeedX: enable3DRotation ? random(-2.0, 2.0) : 0,
+ rotationSpeedY: enable3DRotation ? random(-2.0, 2.0) : 0,
+ nextRotationSpeedX: enable3DRotation ? random(-2.0, 2.0) : 0,
+ nextRotationSpeedY: enable3DRotation ? random(-2.0, 2.0) : 0,
}
this.framesSinceLastUpdate = 0
@@ -184,6 +208,10 @@ class Snowflake {
if (this.image) {
this.params.nextRotationSpeed = random(...this.config.rotationSpeed)
}
+ if (this.config.enable3DRotation) {
+ this.params.nextRotationSpeedX = random(-2.0, 2.0)
+ this.params.nextRotationSpeedY = random(-2.0, 2.0)
+ }
}
public update(offsetWidth: number, offsetHeight: number, framesPassed = 1): void {
@@ -202,15 +230,26 @@ class Snowflake {
}
// Apply rotation
- if (this.image) {
+ if (this.image || this.config.enable3DRotation) {
this.params.rotation = (rotation + rotationSpeed) % 360
}
+ // Apply 3D rotation if enabled
+ if (this.config.enable3DRotation) {
+ this.params.rotationX = (this.params.rotationX + this.params.rotationSpeedX * framesPassed) % 360
+ this.params.rotationY = (this.params.rotationY + this.params.rotationSpeedY * framesPassed) % 360
+ }
+
// Update the wind, speed and rotation towards the desired values
this.params.speed = lerp(speed, nextSpeed, 0.01)
this.params.wind = lerp(wind, nextWind, 0.01)
this.params.rotationSpeed = lerp(rotationSpeed, nextRotationSpeed, 0.01)
+ if (this.config.enable3DRotation) {
+ this.params.rotationSpeedX = lerp(this.params.rotationSpeedX, this.params.nextRotationSpeedX, 0.01)
+ this.params.rotationSpeedY = lerp(this.params.rotationSpeedY, this.params.nextRotationSpeedY, 0.01)
+ }
+
if (this.framesSinceLastUpdate++ > this.config.changeFrequency) {
this.updateTargetParams()
this.framesSinceLastUpdate = 0
@@ -237,6 +276,56 @@ class Snowflake {
return sizes[size] ?? image
}
+ /**
+ * Applies 3D rotation transform to the canvas context.
+ * This method calculates and applies the transformation matrix for 3D rotation effects.
+ *
+ * @param ctx The canvas context to apply the transform to
+ * @param x The x position to translate to
+ * @param y The y position to translate to
+ */
+ private apply3DTransform(ctx: CanvasRenderingContext2D, x: number, y: number): void {
+ if (this.config.enable3DRotation) {
+ const { rotationX, rotationY } = this.params
+ const rotation = this.params.rotation || 0
+
+ // Convert degrees to radians
+ const radX = (rotationX * Math.PI) / 180
+ const radY = (rotationY * Math.PI) / 180
+ const radZ = (rotation * Math.PI) / 180
+
+ // Calculate 3D rotation matrices
+ const cosX = Math.cos(radX)
+ const sinX = Math.sin(radX)
+ const cosY = Math.cos(radY)
+ const sinY = Math.sin(radY)
+ const cosZ = Math.cos(radZ)
+ const sinZ = Math.sin(radZ)
+
+ // Combined rotation matrix (Z * Y * X)
+ // This creates a 3D tumbling effect
+ const a = cosZ * cosY
+ const b = cosZ * sinY * sinX - sinZ * cosX
+ const c = cosZ * sinY * cosX + sinZ * sinX
+ const d = sinZ * cosY
+
+ // Apply the transform
+ // The arguments for setTransform are: a, b, c, d, e, f
+ // a (scaleX), b (skewY), c (skewX), d (scaleY), e (translateX), f (translateY)
+ ctx.setTransform(a, b, c, d, x, y)
+ } else {
+ // Original 2D rotation (only for images)
+ const rotation = this.params.rotation || 0
+ const radian = (rotation * Math.PI) / 180
+ const cos = Math.cos(radian)
+ const sin = Math.sin(radian)
+
+ // The arguments for setTransform are: a, b, c, d, e, f
+ // a (scaleX), b (skewY), c (skewX), d (scaleY), e (translateX), f (translateY)
+ ctx.setTransform(cos, sin, -sin, cos, x, y)
+ }
+ }
+
/**
* Draws a circular snowflake to the canvas.
*
@@ -254,10 +343,42 @@ class Snowflake {
* @param ctx The canvas context to draw to
*/
public drawCircle(ctx: CanvasRenderingContext2D): void {
+ // If 3D rotation is enabled, we need to draw individually with transform
+ // This method is called when 3D rotation is disabled (for performance)
ctx.moveTo(this.params.x, this.params.y)
ctx.arc(this.params.x, this.params.y, this.params.radius, 0, twoPi)
}
+ /**
+ * Draws a circular snowflake with 3D rotation effect to the canvas.
+ *
+ * This method is used when 3D rotation is enabled and images are not being used.
+ *
+ * @param ctx The canvas context to draw to
+ * @param color The color to fill the circle with
+ */
+ public drawCircle3D(ctx: CanvasRenderingContext2D, color: string): void {
+ const { x, y, radius } = this.params
+
+ ctx.save()
+
+ // Apply 3D rotation transform
+ if (this.config.enable3DRotation) {
+ this.apply3DTransform(ctx, x, y)
+ } else {
+ // No transform needed for circles without 3D rotation
+ ctx.translate(x, y)
+ }
+
+ // Draw the circle
+ ctx.beginPath()
+ ctx.arc(0, 0, radius, 0, twoPi)
+ ctx.fillStyle = color
+ ctx.fill()
+
+ ctx.restore()
+ }
+
/**
* Draws an image-based snowflake to the canvas.
*
@@ -266,31 +387,25 @@ class Snowflake {
* @param ctx The canvas context to draw to
*/
public drawImage(ctx: CanvasRenderingContext2D): void {
- const { x, y, rotation, radius } = this.params
+ const { x, y, radius } = this.params
- const radian = (rotation * Math.PI) / 180
- const cos = Math.cos(radian)
- const sin = Math.sin(radian)
+ // Save the current state to avoid affecting other drawings
+ ctx.save()
- // Save the current state to avoid affecting other drawings if changing the opacity
+ // Set opacity if needed
if (this.params.opacity !== 1) {
- ctx.save()
- ctx.globalAlpha = this.params.opacity // Set the global alpha to the snowflake's opacity
+ ctx.globalAlpha = this.params.opacity
}
- // Translate to the location that we will be drawing the snowflake, including any rotation that needs to be applied
- // The arguments for setTransform are: a, b, c, d, e, f
- // a (scaleX), b (skewY), c (skewX), d (scaleY), e (translateX), f (translateY)
- ctx.setTransform(cos, sin, -sin, cos, x, y)
+ // Apply 3D or 2D rotation transform
+ this.apply3DTransform(ctx, x, y)
// Draw the image with the center of the image at the center of the current location
const image = this.getImageOffscreenCanvas(this.image!, radius)
ctx.drawImage(image, -(radius / 2), -(radius / 2), radius, radius)
- // Reset the transform to avoid affecting other drawings if we were changing the opacity
- if (this.params.opacity !== 1) {
- ctx.restore()
- }
+ // Restore the transform
+ ctx.restore()
}
}