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 watchOpen 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 โค๏ธ