Install
$ npm install blobs
import * as blobs2 from "blobs/v2";
import * as blobs2Animate from "blobs/v2/animate";
OR
<script src="https://unpkg.com/blobs/v2"></script>
<script src="https://unpkg.com/blobs/v2/animate"></script>
SVG Path
const svgPath = blobs2.svgPath({
seed: Math.random(),
extraPoints: 8,
randomness: 4,
size: 256,
});
doSomething(svgPath);
SVG
const svgString = blobs2.svg(
{
seed: Math.random(),
extraPoints: 8,
randomness: 4,
size: 256,
},
{
fill: "white", // π¨ NOT SANITIZED
stroke: "black", // π¨ NOT SANITIZED
strokeWidth: 4,
},
);
container.innerHTML = svgString;
Canvas
const path = blobs2.canvasPath(
{
seed: Math.random(),
extraPoints: 16,
randomness: 2,
size: 128,
},
{
offsetX: 16,
offsetY: 32,
},
);
ctx.stroke(path);
Canvas Animation
const ctx = /* ... */;
const animation = blobs2Animate.canvasPath();
TODO wiggle
// Set up "requestAnimationFrame" rendering loop.
const renderAnimation = () => {
ctx.clearRect(0, 0, width, height);
ctx.fill(animation.renderFrame());
requestAnimationFrame(renderAnimation);
};
requestAnimationFrame(renderAnimation);
// Keyframe loop.
const loopAnimation = () => {
animation.transition({
duration: 4000,
timingFunction: "ease",
callback: loopAnimation,
blobOptions: {...},
});
};
// Initial frame.
animation.transition({
duration: 0, // Render immediately.
callback: loopAnimation,
blobOptions: {...},
});
// Toggle play/pause animation on canvas click.
ctx.canvas.onclick = () => {
animation.playPause();
};
Complete API
"blobs/v2"
export interface BlobOptions {
// A given seed will always produce the same blob.
// Use `Math.random()` for pseudorandom behavior.
seed: string | number;
// Actual number of points will be `3 + extraPoints`.
extraPoints: number;
// Increases the amount of variation in point position.
randomness: number;
// Size of the bounding box.
size: number;
}
export interface CanvasOptions {
// Coordinates of top-left corner of the blob.
offsetX?: number;
offsetY?: number;
}
export const canvasPath: (blobOptions: BlobOptions, canvasOptions?: CanvasOptions) => Path2D;
export interface SvgOptions {
fill?: string; // Default: "#ec576b".
stroke?: string; // Default: "none".
strokeWidth?: number; // Default: 0.
}
export const svg: (blobOptions: BlobOptions, svgOptions?: SvgOptions) => string;
export const svgPath: (blobOptions: BlobOptions) => string;
"blobs/v2/animate"
interface Keyframe {
// Duration of the keyframe animation in milliseconds.
duration: number;
// Delay before animation begins in milliseconds.
// Default: 0.
delay?: number;
// Controls the speed of the animation over time.
// Default: "linear".
timingFunction?:
| "linear"
| "easeEnd"
| "easeStart"
| "ease"
| "elasticEnd0"
| "elasticEnd1"
| "elasticEnd2"
| "elasticEnd3";
// Called after keyframe end-state is reached or passed.
// Called exactly once when the keyframe end-state is rendered.
// Not called if the keyframe is preempted by a new transition.
callback?: () => void;
// Standard options, refer to "blobs/v2" documentation.
canvasOptions?: {
offsetX?: number;
offsetY?: number;
};
}
export interface CanvasKeyframe extends Keyframe {
// Standard options, refer to "blobs/v2" documentation.
blobOptions: {
seed: number | string;
randomness: number;
extraPoints: number;
size: number;
};
}
export interface CanvasCustomKeyframe extends Keyframe {
// List of point coordinates that produce a single, closed shape.
points: Point[];
}
export interface Animation {
// Renders the current state of the animation.
renderFrame: () => Path2D;
// Immediately begin animating through the given keyframes.
// Non-rendered keyframes from previous transitions are cancelled.
transition: (...keyframes: (CanvasKeyframe | CanvasCustomKeyframe)[]) => void;
// Resume a paused animation. Has no effect if already playing.
play: () => void;
// Pause a playing animation. Has no effect if already paused.
pause: () => void;
// Toggle between playing and pausing the animation.
playPause: () => void;
}
// Function that returns the current timestamp. This value will be used for all
// duration/delay values and will be used to interpolate between keyframes. It
// must produce values increasing in size.
// Default: `Date.now`.
export interface TimestampProvider {
(): number;
}
export const canvasPath: (timestampProvider?: TimestampProvider) => Animation;
export interface WiggleOptions {
// Speed of the wiggle movement. Higher is faster.
speed: number;
// Delay before the first wiggle frame.
// Default: 0
initialDelay?: number;
// Length of the transition from the current state to the wiggle blob.
// Default: 0
initialTransition?: number;
// Interpolation function.
// Default: linear
initialTimingFunction?: Keyframe["timingFunction"];
}
// Preset animation that produces natural-looking random movement.
// The wiggle animation will continue indefinitely until the next transition.
export const wigglePreset = (
animation: Animation,
blobOptions: BlobOptions,
canvasOptions: CanvasOptions,
wiggleOptions: WiggleOptions,
)