Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v2.0.1 #3

Merged
merged 1 commit into from
Dec 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 97 additions & 1 deletion dist/dynamowaves.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,97 @@
export * from '../src/dynamowaves';
// dynamoves.d.ts

// Interface for wave generation options
interface WaveGenerationOptions {
width: number;
height: number;
points: number;
variance: number;
vertical?: boolean;
}

// Interface for wave point structure
interface WavePoint {
cpX: number;
cpY: number;
x: number;
y: number;
}

// Type for the wave direction
type WaveDirection = 'top' | 'bottom' | 'left' | 'right';

// Interface for intersection observer options
interface WaveObserverOptions {
root: Element | null;
rootMargin: string;
threshold: number;
}

declare class DynamoWave extends HTMLElement {
// Properties
private isAnimating: boolean;
private animationFrameId: number | null;
private elapsedTime: number;
private startTime: number | null;
private isGeneratingWave: boolean;
private currentPath: string | null;
private targetPath: string | null;
private pendingTargetPath: string | null;
private intersectionObserver: IntersectionObserver | null;
private observerOptions: WaveObserverOptions | null;
private points: number;
private variance: number;
private duration: number;
private vertical: boolean;
private width: number;
private height: number;
private svg: SVGSVGElement;
private path: SVGPathElement;

constructor();

// Lifecycle methods
connectedCallback(): void;
disconnectedCallback(): void;

// Public methods
play(customDuration?: number | null): void;
pause(): void;
generateNewWave(duration?: number): void;

// Private methods
private setupIntersectionObserver(observeConfig: string): void;
private animateWave(duration: number, onComplete?: (() => void) | null): void;
}

// Global declaration for custom element
declare global {
interface HTMLElementTagNameMap {
'dynamo-wave': DynamoWave;
}
}

// Component attributes interface
interface DynamoWaveAttributes {
'data-wave-face'?: WaveDirection;
'data-wave-points'?: string;
'data-variance'?: string;
'data-wave-speed'?: string;
'data-wave-animate'?: string;
'data-wave-observe'?: string;
}

// Extend HTMLElement interface to include our attributes
declare global {
interface HTMLElementTagNameMap {
'dynamo-wave': DynamoWave;
}

namespace JSX {
interface IntrinsicElements {
'dynamo-wave': Partial<DynamoWaveAttributes>;
}
}
}

export { DynamoWave, type DynamoWaveAttributes, type WaveDirection, type WaveGenerationOptions, type WaveObserverOptions, type WavePoint };
104 changes: 102 additions & 2 deletions dist/dynamowaves.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,26 @@
})((function () { 'use strict';

class DynamoWave extends HTMLElement {
/**
* Constructs a new instance of the class.
*
* @constructor
*
* @property {boolean} isAnimating - Indicates whether the animation is currently running.
* @property {number|null} animationFrameId - The ID of the current animation frame request.
* @property {number} elapsedTime - The elapsed time since the animation started.
* @property {number|null} startTime - The start time of the animation.
*
* @property {boolean} isGeneratingWave - Indicates whether a wave is currently being generated.
*
* @property {Path2D|null} currentPath - The current wave path.
* @property {Path2D|null} targetPath - The target wave path.
* @property {Path2D|null} pendingTargetPath - The next wave path to be generated.
*
* @property {IntersectionObserver|null} intersectionObserver - The Intersection Observer instance.
* @property {Object|null} observerOptions - The options for the Intersection Observer.
*/

constructor() {
super();
this.isAnimating = false;
Expand All @@ -23,6 +43,14 @@
this.observerOptions = null;
}

/**
* Called when the custom element is appended to the DOM.
* Initializes the wave properties, constructs the SVG element,
* and sets up animation and observation if specified.
*
* @method connectedCallback
* @returns {void}
*/
connectedCallback() {
const classes = this.className;
const id = this.id ?? Math.random().toString(36).substring(7);
Expand Down Expand Up @@ -66,7 +94,7 @@
style="${flipX ? "transform:scaleX(-1);" : ""}${flipY ? "transform:scaleY(-1);" : ""}${styles || ""}"
id="${id}"
aria-hidden="true"
role="img"
role="presentation"
>
<path d="${this.currentPath}" style="stroke:inherit; fill: inherit"></path>
</svg>
Expand Down Expand Up @@ -96,6 +124,13 @@
}

// Public method to play the animation
/**
* Starts the wave animation. If a custom duration is provided, it will be used for the animation;
* otherwise, the instance's default duration will be used. The animation will continue looping
* until `stop` is called.
*
* @param {number|null} [customDuration=null] - Optional custom duration for the animation in milliseconds.
*/
play(customDuration = null) {
if (this.isAnimating) return;
this.isAnimating = true;
Expand Down Expand Up @@ -144,6 +179,11 @@
}

// Public method to pause the animation
/**
* Pauses the animation if it is currently running.
* Sets the `isAnimating` flag to false, cancels the animation frame,
* and saves the current elapsed time.
*/
pause() {
if (!this.isAnimating) return;
this.isAnimating = false;
Expand All @@ -155,6 +195,10 @@
this.startTime = null;
}

/**
* Called when the element is disconnected from the document's DOM.
* Cleans up the intersection observer if it exists.
*/
disconnectedCallback() {
// Clean up intersection observer when element is removed
if (this.intersectionObserver) {
Expand All @@ -163,6 +207,22 @@
}
}

/**
* Sets up an IntersectionObserver to monitor the visibility of the element.
*
* @param {string} observeConfig - Configuration string for observation.
* Format: "mode:rootMargin".
* "mode" can be "once" for one-time observation.
* "rootMargin" is an optional margin around the root.
*
* @example
* // Observe with default root margin and trigger only once
* setupIntersectionObserver('once:0px');
*
* @example
* // Observe with custom root margin and continuous triggering
* setupIntersectionObserver('continuous:10px');
*/
setupIntersectionObserver(observeConfig) {
// Parse observation configuration
const [mode, rootMargin = '0px'] = observeConfig.split(':');
Expand Down Expand Up @@ -198,6 +258,12 @@
}

// Public method to morph to a new wave
/**
* Generates a new wave animation with the specified duration.
* Prevents multiple simultaneous wave generations by setting a flag.
*
* @param {number} [duration=800] - The duration of the wave animation in milliseconds. Minimum value is 1.
*/
generateNewWave(duration = 800) {
// Prevent multiple simultaneous wave generations
if (this.isGeneratingWave || this.animationFrameId) {
Expand Down Expand Up @@ -232,6 +298,12 @@
}

// Core animation logic
/**
* Animates the wave transition from the current path to the target path over a specified duration.
*
* @param {number} duration - The duration of the animation in milliseconds.
* @param {Function} [onComplete=null] - Optional callback function to be called upon animation completion.
*/
animateWave(duration, onComplete = null) {
// Ensure we have valid start and target paths
const startPoints = parsePath(this.currentPath);
Expand Down Expand Up @@ -294,7 +366,18 @@

// Custom element definition
customElements.define("dynamo-wave", DynamoWave);
// Existing helper functions remain the same (generateWave, parsePath, interpolateWave)

/**
* Generates an SVG path string representing a wave pattern.
*
* @param {Object} options - The options for generating the wave.
* @param {number} options.width - The width of the wave.
* @param {number} options.height - The height of the wave.
* @param {number} options.points - The number of points in the wave.
* @param {number} options.variance - The variance factor for the wave's randomness.
* @param {boolean} [options.vertical=false] - Whether the wave should be vertical.
* @returns {string} The SVG path string representing the wave.
*/
function generateWave({ width, height, points, variance, vertical = false }) {
const anchors = [];
const step = vertical ? height / (points - 1) : width / (points - 1);
Expand Down Expand Up @@ -329,6 +412,12 @@
return path;
}

/**
* Parses a path string containing quadratic Bezier curve commands and extracts the control points and end points.
*
* @param {string} pathString - The path string containing 'Q' commands followed by control point and end point coordinates.
* @returns {Array<Object>} An array of objects, each containing the control point (cpX, cpY) and end point (x, y) coordinates.
*/
function parsePath(pathString) {
const points = [];
const regex = /Q\s([\d.]+)\s([\d.]+),\s([\d.]+)\s([\d.]+)/g;
Expand All @@ -345,6 +434,17 @@
return points;
}

/**
* Interpolates between two sets of points to create a smooth wave transition.
*
* @param {Array<Object>} currentPoints - The current set of points.
* @param {Array<Object>} targetPoints - The target set of points.
* @param {number} progress - The progress of the interpolation (0 to 1).
* @param {boolean} [vertical=false] - Whether the wave is vertical or horizontal.
* @param {number} height - The height of the wave container.
* @param {number} width - The width of the wave container.
* @returns {string} - The SVG path data for the interpolated wave.
*/
function interpolateWave(currentPoints, targetPoints, progress, vertical = false, height, width) {
const interpolatedPoints = currentPoints.map((current, i) => {
const target = targetPoints[i];
Expand Down
Loading