Keyframe Animations

Keyframe animations provide precise property control over time.

Supported Properties

Property Description Format
position Item position [x, y]
x, y Position components number
scale Uniform scale number
scaleX, scaleY Axis scale number
rotation Rotation in degrees number
opacity Transparency (0-1) number
fillColor Fill color color string
strokeColor Stroke color color string
fontSize Text size number

Creating Keyframes

const item = app.create({
  type: 'circle',
  position: [400, 100],
  radius: 50,
  fillColor: '#fbbf24'
});

app.modify(item, {
  animationType: 'keyframe',
  keyframes: [
    { time: 0, properties: { position: [400, 100], fillColor: '#fbbf24' }, easing: 'linear' },
    { time: 3, properties: { position: [400, 300], fillColor: '#f97316' }, easing: 'easeInOut' },
    { time: 6, properties: { position: [400, 500], fillColor: '#dc2626' }, easing: 'easeOut' }
  ]
});

Easing Functions

Easing Description
linear Constant speed
easeIn Slow start, fast end
easeOut Fast start, slow end
easeInOut Slow start and end
bounce Bounce effect at end
elastic Elastic overshoot

Color Interpolation

Colors smoothly interpolate in RGB space:

app.modify(sky, {
  animationType: 'keyframe',
  keyframes: [
    { time: 0, properties: { fillColor: '#1e3a5f' } },   // Night
    { time: 2, properties: { fillColor: '#ff7e5f' } },   // Sunrise
    { time: 4, properties: { fillColor: '#87ceeb' } },   // Day
    { time: 6, properties: { fillColor: '#ff6b6b' } },   // Sunset
    { time: 8, properties: { fillColor: '#1e3a5f' } }    // Night
  ]
});

Color Formats

All valid formats for keyframe colors:

{ fillColor: '#ff0000' }           // Hex
{ fillColor: '#ff000080' }         // Hex with alpha
{ fillColor: 'rgb(255, 0, 0)' }    // RGB
{ fillColor: 'rgba(255, 0, 0, 0.5)' }  // RGBA
{ fillColor: 'red' }               // Named colors

Keyframe Manipulation API

addKeyframe(item, time, properties)

Add a keyframe at a specific time.

Parameters:

  • item (paper.Item): Target item
  • time (number): Time in seconds
  • properties (object): Properties to animate
app.addKeyframe(circle, 2.5, { x: 500, opacity: 0.5 });

getKeyframe(item, index)

Get a keyframe by its index.

Parameters:

  • item (paper.Item): Target item
  • index (number): Keyframe index (0-based)

Returns: object - Keyframe object { time, properties, easing }

const kf = app.getKeyframe(circle, 0);
console.log(kf.time, kf.properties);

getKeyframeAtTime(item, time)

Get the keyframe at a specific time.

Parameters:

  • item (paper.Item): Target item
  • time (number): Time in seconds

Returns: object|null - Keyframe object or null

const kf = app.getKeyframeAtTime(circle, 2.5);

getKeyframes(item)

Get all keyframes for an item.

Parameters:

  • item (paper.Item): Target item

Returns: Array<object> - Array of keyframe objects

const keyframes = app.getKeyframes(circle);
keyframes.forEach(kf => {
  console.log(`t=${kf.time}:`, kf.properties);
});

removeKeyframe(item, index)

Remove a keyframe by index.

Parameters:

  • item (paper.Item): Target item
  • index (number): Keyframe index to remove
app.removeKeyframe(circle, 2);  // Remove 3rd keyframe

clearKeyframes(item)

Remove all keyframes from an item.

Parameters:

  • item (paper.Item): Target item
app.clearKeyframes(circle);

updateKeyframe(item, index, properties)

Update an existing keyframe’s properties.

Parameters:

  • item (paper.Item): Target item
  • index (number): Keyframe index
  • properties (object): New properties to merge
app.updateKeyframe(circle, 1, { x: 600, easing: 'bounce' });

moveKeyframe(item, fromIndex, toIndex)

Reorder a keyframe within the keyframe array.

Parameters:

  • item (paper.Item): Target item
  • fromIndex (number): Current index
  • toIndex (number): New index
app.moveKeyframe(circle, 0, 2);  // Move first keyframe to third position

Playback Control

// Play with duration and loop
app.playKeyframeTimeline(8, true);

// Stop
app.stopKeyframeTimeline();

// Scrub to specific time
app.setPlaybackTime(2.5);

Example: Day/Night Cycle

// Create scene elements
const sky = app.create({ type: 'rectangle', position: [400, 300], size: [800, 600] });
const sun = app.create({ type: 'circle', position: [100, 500], radius: 60, fillColor: '#fbbf24' });

// Animate sky
app.modify(sky, {
  animationType: 'keyframe',
  keyframes: [
    { time: 0, properties: { fillColor: '#0f172a' } },
    { time: 2, properties: { fillColor: '#fb923c' } },
    { time: 4, properties: { fillColor: '#38bdf8' } },
    { time: 6, properties: { fillColor: '#f97316' } },
    { time: 8, properties: { fillColor: '#0f172a' } }
  ]
});

// Animate sun arc
app.modify(sun, {
  animationType: 'keyframe',
  keyframes: [
    { time: 0, properties: { position: [100, 600], opacity: 0 } },
    { time: 2, properties: { position: [200, 100], opacity: 1 } },
    { time: 4, properties: { position: [400, 50], opacity: 1 } },
    { time: 6, properties: { position: [600, 100], opacity: 1 } },
    { time: 8, properties: { position: [700, 600], opacity: 0 } }
  ]
});

// Play
app.playKeyframeTimeline(8, true);