diff --git a/src/bubbleCursor.js b/src/bubbleCursor.js index 7e17a80..0096253 100644 --- a/src/bubbleCursor.js +++ b/src/bubbleCursor.js @@ -8,6 +8,8 @@ export function bubbleCursor(options) { let particles = []; let canvas, context, animationFrame; + let active = true; + let canvImages = []; const prefersReducedMotion = window.matchMedia( @@ -102,6 +104,8 @@ export function bubbleCursor(options) { } function addParticle(x, y, img) { + if (!active) return; + particles.push(new Particle(x, y, img)); } @@ -143,6 +147,13 @@ export function bubbleCursor(options) { window.addEventListener("resize", onWindowResize); }; + function pause() { + active = false; + } + function resume() { + active = true; + } + function Particle(x, y, canvasItem) { const lifeSpan = Math.floor(Math.random() * 60 + 60); this.initialLifeSpan = lifeSpan; // @@ -188,6 +199,8 @@ export function bubbleCursor(options) { return { - destroy: destroy + destroy: destroy, + resume: resume, + pause: pause } } diff --git a/src/characterCursor.js b/src/characterCursor.js index 8259ff8..27d17cf 100644 --- a/src/characterCursor.js +++ b/src/characterCursor.js @@ -10,56 +10,58 @@ export function characterCursor(options) { "#B59194", "#D2A1B8", ]; - let cursorOffset = options?.cursorOffset || {x: 0, y: 0}; + let cursorOffset = options?.cursorOffset || { x: 0, y: 0 }; let width = window.innerWidth; let height = window.innerHeight; let cursor = { x: width / 2, y: width / 2 }; let particles = []; let canvas, context, animationFrame; + let active = true; + let font = options?.font || "15px serif" - let randomPositiveOrNegativeOne = function() { + let randomPositiveOrNegativeOne = function () { return (Math.random() < 0.5 ? -1 : 1); } // Generates the lifespan for individual characters - let characterLifeSpanFunction = options?.characterLifeSpanFunction || - function() { - return Math.floor(Math.random() * 60 + 80); - } + let characterLifeSpanFunction = options?.characterLifeSpanFunction || + function () { + return Math.floor(Math.random() * 60 + 80); + } // Defines the original velocity for the character - let initialCharacterVelocityFunction = options?.initialCharacterVelocityFunction || - function() { - return { - x: (Math.random() < 0.5 ? -1 : 1) * Math.random() * 5, - y: (Math.random() < 0.5 ? -1 : 1) * Math.random() * 5, - } - }; + let initialCharacterVelocityFunction = options?.initialCharacterVelocityFunction || + function () { + return { + x: (Math.random() < 0.5 ? -1 : 1) * Math.random() * 5, + y: (Math.random() < 0.5 ? -1 : 1) * Math.random() * 5, + } + }; // Defines how the velocity should change (additively) let characterVelocityChangeFunctions = options?.characterVelocityChangeFunctions || { - x_func: function(age, lifeSpan) { - return (Math.random() < 0.5 ? -1 : 1)/30; - }, - y_func: function(age, lifeSpan) { - return (Math.random() < 0.5 ? -1 : 1)/ 15; - }, - }; + x_func: function (age, lifeSpan) { + return (Math.random() < 0.5 ? -1 : 1) / 30; + }, + y_func: function (age, lifeSpan) { + return (Math.random() < 0.5 ? -1 : 1) / 15; + }, + }; - let characterScalingFunction = options?.characterScalingFunction || - function(age, lifeSpan) { - let lifeLeft = lifeSpan - age; - return Math.max(lifeLeft / lifeSpan * 2, 0); - } + let characterScalingFunction = options?.characterScalingFunction || + function (age, lifeSpan) { + let lifeLeft = lifeSpan - age; + return Math.max(lifeLeft / lifeSpan * 2, 0); + } // Produces new angles for the character - let characterNewRotationDegreesFunction = options?.characterNewRotationDegreesFunction || - function(age, lifeSpan) { - let lifeLeft = lifeSpan - age; - return lifeLeft / 5; - }; + let characterNewRotationDegreesFunction = options?.characterNewRotationDegreesFunction || + function (age, lifeSpan) { + let lifeLeft = lifeSpan - age; + return lifeLeft / 5; + }; let canvImages = []; @@ -186,6 +188,8 @@ export function characterCursor(options) { } function addParticle(x, y, img) { + if (!active) return; + particles.push(new Particle(x, y, img)); } @@ -218,6 +222,13 @@ export function characterCursor(options) { animationFrame = requestAnimationFrame(loop); } + function pause() { + active = false; + } + function resume() { + active = true; + } + function destroy() { canvas.remove(); cancelAnimationFrame(animationFrame); @@ -238,9 +249,9 @@ export function characterCursor(options) { this.initialLifeSpan = lifeSpan; // this.lifeSpan = lifeSpan; //ms this.velocity = initialCharacterVelocityFunction(); - this.position = { - x: x + cursorOffset.x, - y: y + cursorOffset.y + this.position = { + x: x + cursorOffset.x, + y: y + cursorOffset.y }; this.canv = canvasItem; @@ -278,6 +289,8 @@ export function characterCursor(options) { init(); return { - destroy: destroy + destroy: destroy, + resume: resume, + pause: pause } } diff --git a/src/emojiCursor.js b/src/emojiCursor.js index a224108..19d1649 100644 --- a/src/emojiCursor.js +++ b/src/emojiCursor.js @@ -13,6 +13,8 @@ export function emojiCursor(options) { const canvImages = []; let canvas, context, animationFrame; + let active = true; + const prefersReducedMotion = window.matchMedia( "(prefers-reduced-motion: reduce)" ); @@ -151,6 +153,7 @@ export function emojiCursor(options) { } function addParticle(x, y, img) { + if(!active) return; particles.push(new Particle(x, y, img)); } @@ -192,6 +195,12 @@ export function emojiCursor(options) { window.addEventListener("resize", onWindowResize); } + function pause() { + active = false; + } + function resume() { + active = true; + } /** * Particles */ @@ -230,5 +239,7 @@ export function emojiCursor(options) { return { destroy: destroy, + resume: resume, + pause: pause }; } diff --git a/src/fairyDustCursor.js b/src/fairyDustCursor.js index 07b235a..09c8c5d 100644 --- a/src/fairyDustCursor.js +++ b/src/fairyDustCursor.js @@ -15,6 +15,8 @@ export function fairyDustCursor(options) { const canvImages = []; let canvas, context, animationFrame; + let active = true; + const char = "*"; const prefersReducedMotion = window.matchMedia( @@ -151,6 +153,8 @@ export function fairyDustCursor(options) { } function addParticle(x, y, color) { + if (!active) return; + particles.push(new Particle(x, y, color)); } @@ -183,6 +187,13 @@ export function fairyDustCursor(options) { animationFrame = requestAnimationFrame(loop); } + function pause() { + active = false; + } + function resume() { + active = true; + } + function destroy() { canvas.remove(); cancelAnimationFrame(animationFrame); @@ -225,6 +236,8 @@ export function fairyDustCursor(options) { init(); return { - destroy: destroy + destroy: destroy, + resume: resume, + pause: pause } } diff --git a/src/ghostCursor.js b/src/ghostCursor.js index fd92bb9..e48689b 100644 --- a/src/ghostCursor.js +++ b/src/ghostCursor.js @@ -13,6 +13,8 @@ export function ghostCursor(options) { let particles = []; let canvas, context, animationFrame; + let active = true; + let baseImage = new Image(); if (options && options.image) { baseImage.src = options.image; @@ -96,7 +98,7 @@ export function ghostCursor(options) { let getDelay = () => Math.floor(Math.random() * (maxDelay - minDelay + 1)) + minDelay; let lastTimeParticleAdded = Date.now(), - interval = getDelay(); + interval = getDelay(); function onMouseMove(e) { if (randomDelay) { @@ -118,6 +120,8 @@ export function ghostCursor(options) { } function addParticle(x, y, image) { + if (!active) return; + particles.push(new Particle(x, y, image)); } @@ -125,7 +129,7 @@ export function ghostCursor(options) { if (particles.length == 0) { return; } - + context.clearRect(0, 0, width, height); // Update @@ -150,6 +154,13 @@ export function ghostCursor(options) { animationFrame = requestAnimationFrame(loop); } + function pause() { + active = false; + } + function resume() { + active = true; + } + function destroy() { canvas.remove(); cancelAnimationFrame(animationFrame); @@ -186,6 +197,8 @@ export function ghostCursor(options) { init(); return { - destroy: destroy + destroy: destroy, + resume: resume, + pause: pause } } diff --git a/src/rainbowCursor.js b/src/rainbowCursor.js index 08f1fd5..ca514b7 100644 --- a/src/rainbowCursor.js +++ b/src/rainbowCursor.js @@ -8,6 +8,8 @@ export function rainbowCursor(options) { let particles = []; let canvas, context, animationFrame; + let active = true; + const totalParticles = options?.length || 20; const colors = options?.colors || [ "#FE0000", @@ -103,6 +105,8 @@ export function rainbowCursor(options) { } function addParticle(x, y, image) { + if (!active) return; + particles.push(new Particle(x, y, image)); } @@ -155,6 +159,13 @@ export function rainbowCursor(options) { animationFrame = requestAnimationFrame(loop); } + function pause() { + active = false; + } + function resume() { + active = true; + } + function destroy() { canvas.remove(); cancelAnimationFrame(animationFrame); @@ -169,6 +180,8 @@ export function rainbowCursor(options) { init(); return { - destroy: destroy + destroy: destroy, + resume: resume, + pause: pause } } diff --git a/src/snowflakeCursor.js b/src/snowflakeCursor.js index 1584ce6..c4e81d1 100644 --- a/src/snowflakeCursor.js +++ b/src/snowflakeCursor.js @@ -9,6 +9,8 @@ export function snowflakeCursor(options) { let particles = []; let canvas, context, animationFrame; + let active = true; + let canvImages = []; const prefersReducedMotion = window.matchMedia( @@ -131,6 +133,8 @@ export function snowflakeCursor(options) { } function addParticle(x, y, img) { + if (!active) return; + particles.push(new Particle(x, y, img)); } @@ -163,6 +167,13 @@ export function snowflakeCursor(options) { animationFrame = requestAnimationFrame(loop); } + function pause() { + active = false; + } + function resume() { + active = true; + } + function destroy() { canvas.remove(); cancelAnimationFrame(animationFrame); @@ -219,6 +230,8 @@ export function snowflakeCursor(options) { init(); return { - destroy: destroy + destroy: destroy, + resume: resume, + pause: pause } } diff --git a/src/trailingCursor.js b/src/trailingCursor.js index be7024b..f43bc17 100644 --- a/src/trailingCursor.js +++ b/src/trailingCursor.js @@ -11,6 +11,8 @@ export function trailingCursor(options) { let particles = []; let canvas, context, animationFrame; + let active = true; + const totalParticles = options?.particles || 15; const rate = options?.rate || 0.4; const baseImageSrc = options?.baseImageSrc || "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAATCAYAAACk9eypAAAAAXNSR0IArs4c6QAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADKADAAQAAAABAAAAEwAAAAAChpcNAAAACXBIWXMAAAsTAAALEwEAmpwYAAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpMwidZAAABqElEQVQoFY3SPUvDQBgH8BREpRHExYiDgmLFl6WC+AYmWeyLg4i7buJX8DMpOujgyxGvUYeCgzhUQUSKKLUS0+ZyptXh8Z5Ti621ekPyJHl+uftfomhaf9Ei5JyxXKfynyEA6EYcLHpwyflT958GAQ7DTABNHd8EbtDbEH2BD5QEQmi2mM8P/Iq+A0SzszEg+3sPjDnDdVEtQKQbMUidHD3xVzf6A9UDEmEm+8h9KTqTVUjT+vB53aHrCbAPiceYq1dQI1Aqv4EhMll0jzv+Y0yiRgCnLRSYyDQHVoqUXe4uKL9l+L7GXC4vkMhE6eW/AOJs9k583ORDUyXMZ8F5SVHVVnllmPNKSFagAJ5DofaqGXw/gHBYg51dIldkmknY3tguv3jOtHR4+MqAzaraJXbEhqHhcQlwGSOi5pytVQHZLN5s0WNe8HPrLYlFsO20RPHkImxsbmHdLJFI76th7Z4SeuF53hTeFLvhRCJRCTKZKxgdnRDbW+iozFJbBMw14/ElwGYc0egMBMFzT21f5Rog33Z7dX02GBm7WV5ZfT5Nn5bE3zuCDe9UxdTpNvK+5AAAAABJRU5ErkJggg=="; @@ -101,6 +103,8 @@ export function trailingCursor(options) { } function addParticle(x, y, image) { + if (!active) return; + particles.push(new Particle(x, y, image)); } @@ -126,6 +130,13 @@ export function trailingCursor(options) { animationFrame = requestAnimationFrame(loop); } + function pause() { + active = false; + } + function resume() { + active = true; + } + function destroy() { canvas.remove(); cancelAnimationFrame(animationFrame); @@ -153,6 +164,8 @@ export function trailingCursor(options) { init(); return { - destroy: destroy + destroy: destroy, + resume: resume, + pause: pause } } diff --git a/types.d.ts b/types.d.ts index 1473b2c..1dd1604 100644 --- a/types.d.ts +++ b/types.d.ts @@ -2,6 +2,11 @@ export type CursorEffectResult = { destroy(): void; } +type PausableResult = { + pause(): void; + resume(): void; +} + type DefaultOptions = { readonly element?: HTMLElement; } @@ -78,15 +83,15 @@ export type TrailingCursorOptions = { readonly baseImageSrc?: number | string; } & DefaultOptions; -export function bubbleCursor(options?: BubbleCursorOptions): CursorEffectResult; -export function characterCursor(options?: CharacterCursorOptions): CursorEffectResult; +export function bubbleCursor(options?: BubbleCursorOptions): CursorEffectResult & PausableResult; +export function characterCursor(options?: CharacterCursorOptions): CursorEffectResult & PausableResult; export function clockCursor(options?: ClockCursorOptions): CursorEffectResult; -export function emojiCursor(options?: EmojiCursorOptions): CursorEffectResult; -export function fairyDustCursor(options?: FairyDustCursorOptions): CursorEffectResult; +export function emojiCursor(options?: EmojiCursorOptions): CursorEffectResult & PausableResult; +export function fairyDustCursor(options?: FairyDustCursorOptions): CursorEffectResult & PausableResult; export function followingDotCursor(options?: FollowingDotCursorOptions): CursorEffectResult; -export function ghostCursor(options?: GhostCursorOptions): CursorEffectResult; -export function rainbowCursor(options?: RainbowCursorOptions): CursorEffectResult; -export function snowflakeCursor(options?: SnowflakeCursorOptions): CursorEffectResult; +export function ghostCursor(options?: GhostCursorOptions): CursorEffectResult & PausableResult; +export function rainbowCursor(options?: RainbowCursorOptions): CursorEffectResult & PausableResult; +export function snowflakeCursor(options?: SnowflakeCursorOptions): CursorEffectResult & PausableResult; export function springyEmojiCursor(options?: SpringyEmojiCursorOptions): CursorEffectResult; export function textFlag(options?: TextFlagOptions): CursorEffectResult; -export function trailingCursor(options?: TrailingCursorOptions): CursorEffectResult; +export function trailingCursor(options?: TrailingCursorOptions): CursorEffectResult & PausableResult;