Tracking a moving object
The ball's hotspot has three keyframes that follow the ball's path through the video. Click the ball before it stops to catch it. If the video ends first, the scene continues to its next.
- Events will show up here.
Pause the video with Space or seek with ← →: the hotspot stays on the ball, because its position is computed from the video's time, not from an animation that runs on its own. Where the browser supports requestVideoFrameCallback, it moves exactly when each frame is shown.
Story
import type { Story } from "@ivkit/core";
import { video } from "./video";
export const tracking: Story = {
version: "0.1",
start: "chase",
scenes: {
chase: {
src: video("tracking"),
// Reached only if the viewer doesn't catch the ball.
next: "missed",
events: [
{
type: "hotspot",
id: "ball",
from: 1,
label: "The ball",
goto: "caught",
shape: "ellipse",
variant: "pulse",
// Same path as the ball in the video: it moves in straight lines
// between these points, so linear easing keeps them together.
keyframes: [
{ at: 1, area: { x: 0.08, y: 0.6, width: 0.1, height: 0.1778 } },
{ at: 4, area: { x: 0.45, y: 0.15, width: 0.1, height: 0.1778 } },
{ at: 7, area: { x: 0.8, y: 0.55, width: 0.1, height: 0.1778 } },
],
},
],
},
caught: { src: video("tracking-caught") },
missed: { src: video("tracking-missed") },
},
};Getting keyframes from your own video
Pause the video where the object starts moving, changes direction or stops, and write down the time.
Measure the object's box in pixels and divide by the video's size to get fractions:
tsconst area = (x: number, y: number, width: number, height: number) => ({ x: x / 1920, y: y / 1080, width: width / 1920, height: height / 1080, });Add a keyframe for each time. Use
ease-in-outfor motion that speeds up and slows down, andstepfor jumps.
Keyframes can also change the shape: a rect keyframe followed by an ellipse one morphs smoothly between them.
Learn more: Animation · Coordinates · hotspotGeometryAt