OG
JS
Docs
Demos
Examples
Node and camera motion
Position, size, color, and camera tracks share one frame loop and settle on exact targets.
Run motion
ready
// Describe final node attributes and camera targets in the same interaction. // OGJS advances both tracks on one frame clock, emits lifecycle events, and settles on exact // values without application-owned interpolation or render scheduling. import { Ogjs } from '../../src/index'; const og = new Ogjs({ container: '#viz', theme: 'light' }); const motionEvents = []; og.on('animate', event => motionEvents.push(event)); og.setGraph({ nodes: [ { id: 'signal', label: 'Signal', x: -180, y: -70, size: 10, color: '#ef4444' }, { id: 'account', label: 'Account', x: -30, y: 110, size: 12, color: '#3b82f6' }, { id: 'device', label: 'Device', x: 40, y: -110, size: 12, color: '#8b5cf6' }, { id: 'merchant', label: 'Merchant', x: 190, y: 80, size: 12, color: '#f59e0b' }, { id: 'case', label: 'Case', x: 185, y: -95, size: 12, color: '#0ea5e9' }, ], edges: [ { source: 'signal', target: 'account' }, { source: 'signal', target: 'device' }, { source: 'account', target: 'merchant' }, { source: 'device', target: 'case' }, ], }); og.camera.fit(); // Capture the current public node and camera state as a reusable starting target. No screen // coordinates are involved: node motion stays in graph space and camera motion in camera state. const signal = og.getNode('signal'); const start = { node: { x: signal.x, y: signal.y, size: signal.size }, camera: { cx: og.camera.cx, cy: og.camera.cy, zoom: og.camera.zoom }, }; const target = { node: { x: 100, y: 70, size: 24 }, camera: { cx: 40, cy: 15, zoom: start.camera.zoom * 1.28 }, }; const button = document.querySelector('#run-motion'); const status = document.querySelector('#motion-status'); async function runMotion() { button.disabled = true; // Reset both tracks instantly so repeated runs start from identical state and never inherit // a partially completed previous animation. await Promise.all([ og.animate({ ids: ['signal'], x: [start.node.x], y: [start.node.y], size: [start.node.size], color: ['#ef4444ff'], }, 0), og.animateCamera(start.camera, 0), ]); motionEvents.length = 0; status.textContent = 'animating · 700ms ease-in-out'; // Start node and camera animations together and await both. OGJS owns interpolation and frame // scheduling; the application receives one clear settlement point for its controls. await Promise.all([ og.animate({ ids: ['signal'], x: [target.node.x], y: [target.node.y], size: [target.node.size], color: ['#22c55eff'], }, 700, 'easeInOut'), og.animateCamera(target.camera, 700), ]); status.textContent = 'node and camera settled'; button.disabled = false; } button.addEventListener('click', runMotion);