A feature-rich, Flash MX-style timeline control built with TypeScript and LESS. This control provides a professional animation timeline interface for web applications, with support for layers, keyframes, tweens, and playback control.
- Layer Management: Create, delete, rename, and organize layers in hierarchical folders
- Keyframe System: Insert content keyframes, blank keyframes, and standard frames
- Tween Support: Create and manage motion tweens between keyframes
- Playback Engine: Play, pause, stop, and scrub through animations at customizable frame rates
- Drag & Drop: Reorder layers and move keyframes across the timeline
- Context Menus: Right-click context menus for layers and frames (with touch support)
- Selection System: Single, multi-select (CTRL), and range select (Shift) for keyframes
- Responsive Design: Adapts to container size changes with configurable panel widths
- Synchronized Scrolling: Layer panel, ruler, and grid scroll together seamlessly
- Visual Feedback: Hover states, selection highlights, and drag indicators
- Folder Navigation: Expand/collapse folders with visual indicators
- Mobile Support: Touch-friendly three-dot menu for context actions on mobile devices
- ARIA Support: Full semantic structure with role attributes and labels
- Keyboard Navigation: Navigate layers with arrow keys, Enter, Space, and Delete
- Focus Indicators: Visible focus outlines on all interactive elements
- Screen Reader Support: Comprehensive aria-labels for all UI elements
- Logical Tab Order: Proper focus flow through the interface
- Event System: 19+ events for integrating with your application
- Data Persistence: Export/import timeline data as JSON
- Performance Optimized: Debounced scroll handlers and efficient rendering
- Event Logger: Built-in debugging tool for tracking all timeline events
- Undo/Redo Ready: Architecture designed for command pattern implementation
npm install
# Production build npm run build # Development build npm run build:dev # Watch mode npm run watch # Development server with hot reload npm run debug
<!DOCTYPE html> <html> <head> <title>Timeline Demo</title> </head> <body> <div id="timeline-container"></div> <script src="dist/JsTimeLine.bundle.js"></script> <script> // Create timeline instance const timeline = new JsTimeLine.JsTimeLine('timeline-container'); // Timeline is now ready to use! </script> </body> </html>
const timeline = new JsTimeLine.JsTimeLine('container-id');
// Enable/disable playhead movement on frame click timeline.setMovePlayheadOnFrameClick(true); // default: true // Update panel sizes timeline.updateLayerPanelWidth(300); // default: 250px timeline.updateRulerHeight(50); // default: 40px
// Export timeline data const jsonData = timeline.exportData(); // Import timeline data timeline.importData(jsonData); // Get timeline context const context = timeline.getContext();
const context = timeline.getContext(); const playback = context.Core.playbackEngine; // Control playback playback.play(); playback.pause(); playback.stop(); playback.goToFrame(25);
const layerManager = context.Core.layerManager; // Add layers layerManager.addLayer('New Layer'); layerManager.addFolder('My Folder'); // Delete layer layerManager.deleteObject(layerId); // Rename layer layerManager.renameObject(layerId, 'New Name'); // Toggle visibility/lock layerManager.toggleVisibility(layerId); layerManager.toggleLock(layerId);
const keyframeManager = context.Core.keyframeManager; // Insert keyframes keyframeManager.insertKeyframe(layerId, frameNumber); keyframeManager.insertBlankKeyframe(layerId, frameNumber); // Delete keyframes keyframeManager.deleteFrames(layerId, startFrame, endFrame); // Move keyframes keyframeManager.moveKeyframes([frameId1, frameId2], targetLayerId, frameOffset); // Copy/paste keyframes keyframeManager.copyKeyframes([frameId1, frameId2]); keyframeManager.pasteKeyframes(targetLayerId, targetFrame);
const tweenManager = context.Core.tweenManager; // Create motion tween tweenManager.createMotionTween(layerId, startFrame, endFrame); // Remove tween tweenManager.removeTween(layerId, tweenIndex); // Update tween properties tweenManager.updateTween(layerId, tweenIndex, { type: 'ease-in' });
const eventManager = context.Core.eventManager; // Listen to events eventManager.on('onObjectAdd', (data) => { console.log('Layer added:', data); }); eventManager.on('onKeyframeAdd', (data) => { console.log('Keyframe added:', data); }); eventManager.on('onFrameEnter', (data) => { console.log('Current frame:', data.currentFrame); }); // Enable event logging (for debugging) const eventLogger = JsTimeLine.attachEventLogger(context); eventLogger.enable();
| Shortcut | Action |
|---|---|
| F5 | Insert Frame |
| Shift+F5 | Delete Frame |
| F6 | Insert Keyframe (content) |
| F7 | Insert Blank Keyframe |
| Enter | Toggle Play/Pause |
| , (comma) | Previous Frame |
| . (period) | Next Frame |
| Ctrl+C | Copy selected keyframes |
| Ctrl+V | Paste keyframes |
| Delete | Delete selected frames |
| Arrow Keys | Navigate layers (when layer panel focused) |
| Ctrl+Click | Toggle selection |
| Shift+Click | Range selection |
onObjectAdd- Layer or folder addedonBeforeObjectDelete- Before layer deletion (cancellable)onObjectDelete- Layer or folder deletedonObjectRename- Layer renamedonObjectReparent- Layer moved to different parentonObjectReorder- Layer order changedonObjectVisibilityChange- Layer visibility toggledonObjectLockChange- Layer lock state toggledonLayerSelect- Layer selected
onKeyframeAdd- Keyframe addedonBeforeKeyframeDelete- Before keyframe deletion (cancellable)onKeyframeDelete- Keyframe deletedonKeyframeMove- Keyframe movedonKeyframeSelect- Keyframe selected
onTweenAdd- Motion tween createdonTweenRemove- Motion tween removedonTweenUpdate- Tween properties updated
onPlaybackStart- Playback startedonPlaybackPause- Playback pausedonTimeSeek- Playhead moved manuallyonFrameEnter- Entered new frame during playback
v2/
โโโ src/
โ โโโ core/ # Core managers (Layer, Keyframe, Tween, etc.)
โ โโโ ui/ # UI components (LayerPanel, TimelineGrid, etc.)
โ โโโ utils/ # Utilities (Performance, EventLogger)
โ โโโ data/ # Data models and interfaces
โ โโโ styles/ # LESS stylesheets
โ โโโ JsTimeLine.ts # Main entry point
โโโ dist/ # Compiled output
โโโ doc/ # Documentation
โโโ index.html # Test page
โโโ package.json
The project follows a Context Architecture Pattern:
- No Prop Drilling: All components access shared state via
IJsTimeLineContext - Event-Driven: Components communicate through
EventManager - Separation of Concerns: UI components are separate from business logic
- Type Safety: Full TypeScript with strict mode enabled
- Style Isolation: All styles in LESS files (no inline styles)
- JsTimeLine: Main control class
- LayerPanel: Layer hierarchy and management
- TimelineGrid: Frame visualization and interaction
- TimeRuler: Frame numbers and playhead dragging
- PlaybackEngine: Animation playback control
- LayerManager: Layer CRUD operations
- KeyframeManager: Keyframe operations
- TweenManager: Tween creation and management
- SelectionManager: Frame selection state
- StateManager: Persistent state storage
- EventManager: Event pub/sub system
- Node.js 16+
- npm or yarn
# Install dependencies npm install # Start development server npm run debug # Build for production npm run build # Watch mode for development npm run watch
Open index.html in a browser after building. The test page includes:
- Interactive timeline with sample data
- Event logger with real-time event tracking
- Configuration toggles
- Export/import functionality
- Chrome 90+
- Firefox 88+
- Safari 14+
- Edge 90+
Timeline data uses a hierarchical JSON structure:
{
"version": "1.0.0",
"settings": {
"totalFrames": 100,
"frameRate": 24,
"frameWidth": 15,
"rowHeight": 30,
"layerPanelWidth": 250,
"rulerHeight": 40,
"movePlayheadOnFrameClick": true
},
"layers": [
{
"id": "layer-1",
"name": "Background",
"type": "layer",
"visible": true,
"locked": false,
"keyframes": [
{ "frame": 1, "isEmpty": false },
{ "frame": 10, "isEmpty": false }
],
"tweens": [
{ "startFrame": 1, "endFrame": 10, "type": "linear" }
]
},
{
"id": "folder-1",
"name": "Character",
"type": "folder",
"children": [
{
"id": "layer-2",
"name": "Head",
"type": "layer",
"keyframes": [],
"tweens": []
}
]
}
]
}Contributions are welcome! Please ensure:
- All TypeScript code is strictly typed (no
any) - All styles are in LESS files (no inline styles)
- Follow the Context architecture pattern
- Add JSDoc comments for public APIs
- Test changes with the included test page
MIT License - feel free to use in personal and commercial projects.
- Core timeline rendering
- Layer management (CRUD, drag & drop)
- Keyframe operations
- Motion tweens
- Playback engine
- Context menus
- Selection system
- Event system
- Data persistence
- Responsive design
- Accessibility (WCAG 2.1 AA)
- Undo/Redo system
- Onion skinning
- Frame markers and labels
- Multiple tween types (ease-in, ease-out, etc.)
- Layer effects and filters
- Timeline zoom controls
- Multi-track audio visualization
- Plugin system for extensions
For issues, questions, or feature requests, please use the GitHub issue tracker.
Built with TypeScript, LESS, and โค๏ธ