A powerful animation addon for Slidev presentations using Konva.js. Create stunning step-by-step animations, interactive diagrams, and dynamic visual content synchronized with your slide navigation.
animation-showcase.mp4
- Step-by-Step Animations: Synchronized with Slidev's click navigation system
- Generator-Based Syntax: Clean, readable animation code using generator functions
- Rich Easing Library: Bounce, elastic, back, and more built-in easing functions
- Block & Connection System: Create dynamic flowcharts and diagrams
- Property Animations: Animate position, scale, rotation, opacity, color, and more
- Simultaneous Animations: Execute multiple animations in parallel with
step() - Smart Performance: Automatic animation skipping for rapid navigation
- TypeScript Support: Fully typed with comprehensive type definitions
- Animation Engine: Custom-built animation system with Konva.js integration
- Graphics: Konva 2D canvas library with Vue-Konva
- Framework: Vue 3 with Composition API and TypeScript
- Testing: Vitest with comprehensive test coverage
- Package Manager: Bun for fast dependency management
- Code Quality: Biome for linting and formatting
Install the addon in your Slidev project:
# Using npm
npm install git+https://github.com/DCC-BS/slidev-addon-animations.git
# Using bun
bun add git+https://github.com/DCC-BS/slidev-addon-animations.git Add the addon to your slides.md frontmatter:
---
theme: default
addons:
- slidev-addon-animations
---// vite.config.ts
import { defineConfig } from "vite";
export default defineConfig({
optimizeDeps: {
include: ["konva", "vue-konva"],
},
});<script setup lang="ts">
import {
animate,
EasingPresets,
moveTo,
scaleTo,
step,
} from "slidev-addon-animations";
import { ref } from "vue";
const circle = ref({ x: 100, y: 100, scaleX: 1, scaleY: 1, radius: 30, fill: "red" });
function* myAnimation() {
// Step 1: Move and scale simultaneously
yield step(
moveTo(circle, 200, 150, { duration: 1000 }),
scaleTo(circle, 1.5, { easing: EasingPresets.bounceOut }),
);
// Step 2: Change color
yield animate(circle, { fill: "blue" }, { duration: 500 });
}
</script>
<template>
<Animator :generator="myAnimation">
</Animator>
<v-stage :width="400" :height="300">
<v-layer>
<v-circle :config="circle" />
</v-layer>
</v-stage>
</template>basic-animation.mp4
<script setup lang="ts">
import type { BlockConfig, ConnectionOptions } from "slidev-addon-animations";
import { computed, ref } from "vue";
const blockA = ref<BlockConfig>({
x: 50,
y: 100,
width: 120,
height: 60,
text: "A",
});
const blockB = ref<BlockConfig>({
x: 250,
y: 100,
width: 120,
height: 60,
text: "B",
});
const blockC = ref<BlockConfig>({
x: 250,
y: 200,
width: 120,
height: 60,
text: "C",
});
const connectionAB = computed<ConnectionOptions>(() => ({
fromShape: blockA.value,
toShape: blockB.value,
fromAnchor: "right",
toAnchor: "left",
connectionType: "straight",
lineType: "arrow",
}));
const connectionAC = computed<ConnectionOptions>(() => ({
fromShape: blockA.value,
toShape: blockC.value,
fromAnchor: "bottom",
toAnchor: "left",
connectionType: "orthogonal",
lineType: "arrow",
}));
const connectionBC = computed<ConnectionOptions>(() => ({
fromShape: blockB.value,
toShape: blockC.value,
fromAnchor: "right",
toAnchor: "right",
connectionType: "curved",
lineType: "double-arrow",
}));
</script>
<template>
<v-stage :width="600" :height="300">
<v-layer>
<Block :config="blockA" />
<Block :config="blockB" />
<Block :config="blockC" />
<Connection :config="connectionAB" />
<Connection :config="connectionAC" />
<Connection :config="connectionBC" />
</v-layer>
</v-stage>
</template>The animation system works seamlessly with Vue's reactivity. When you animate reactive references, any computed values depending on them automatically update, making dynamic connections possible:
<script setup lang="ts">
import { animate, type BlockConfig, type ConnectionOptions } from "slidev-addon-animations";
import { computed, ref } from "vue";
// create a reactive reference for the y-coordinate of block B
const blockBY = ref(100);
// block A will be a static block with fixed coordinates so we can use a simple object
const blockA = {
x: 50,
y: 100,
width: 120,
height: 60,
text: "Block",
} as BlockConfig;
// create a computed reference for block B that uses the reactive reference
const blockB = computed<BlockConfig>(() => ({
x: 250,
y: blockBY.value,
width: 120,
height: 60,
text: "Block",
}));
// create a computed reference for the connection options between block A and block B
const connection = computed<ConnectionOptions>(() => ({
fromShape: blockA,
toShape: blockB.value,
type: "curved",
fromAnchor: "right",
toAnchor: "left",
connectionType: "orthogonal",
lineType: "double-arrow",
}));
function* myAnimation() {
yield animate(blockBY, 200, { duration: 1000 });
}
</script>
<template>
<Animator :generator="myAnimation" />
<v-stage :width="400" :height="300">
<v-layer>
<Block :config="blockA" />
<Block :config="blockB" />
<Connection :config="connection" />
</v-layer>
</v-stage>
</template>animated-connections.mp4
Key Benefits of Computed Values:
- Automatic Updates: When
blockBYis animated, the computedblockBautomatically recalculates - Dynamic Connections: The
connectioncomputed property updates in real-time as block positions change - Reactive Chains: Changes propagate through the entire reactive dependency graph
- Performance: Vue's reactivity system ensures only necessary updates are triggered
This approach enables smooth, synchronized animations where connections automatically adjust as blocks move, creating fluid and professional-looking animated diagrams.
Make sure to install dependencies:
# Using bun (recommended)
bun install
# Using npm
npm installPreview the example slides with hot reload:
# Using bun (recommended)
bun run dev
# Using npm
npm run devThis will start Slidev with the example.md presentation showcasing all features.
Build presentation for production:
# Using bun
bun run build
# Using npm
npm run buildExport as PDF:
# Using bun
bun run export
# Using npm
npm run exportGenerate screenshot preview:
# Using bun
bun run screenshot
# Using npm
npm run screenshotRun comprehensive test suite:
# Run all tests
bun test # or: npm test
# Run tests with UI
bun test:ui # or: npm run test:ui
# Run tests in watch mode
bun test:watch # or: npm run test:watch
# Generate coverage report
bun test:coverage # or: npm run test:coverage
# Run specific test examples
bun test:examples # or: npm run test:examplesCheck code quality with Biome:
# Lint and format
bun run lint # or: npm run lint
# Check for issues
bun run check # or: npm run checkMain composable for creating animations with fine-grained control.
const { currentStep, totalSteps, isAnimating, animateToStep } = useKonvaAnimation(
animationTargets,
{
skipThreshold: 300,
defaultDuration: 1000,
defaultEasing: EasingPresets.easeInOut
}
)High-level composable with generator-based animation syntax.
const { createAnimationFromGenerator, animate, moveTo, scaleTo } = useGeneratorAnimation({
skipThreshold: 300,
defaultDuration: 1000,
defaultEasing: 'easeInOut'
})-
animate(target, properties, options)- Animate any properties on objectstarget: unknown | Ref<unknown>- The target object or reactive reference to animateproperties: Record<string, unknown>- Object containing properties to animate and their target valuesoptions?: AnimationProps- Optional animation configuration
-
animate(target, value, options)- Animate ref primitive values directlytarget: Ref<unknown>- The reactive reference containing a primitive valuevalue: unknown- The target value to animate tooptions?: AnimationProps- Optional animation configuration
-
animateValue(target, value, options)- Explicit helper for animating ref valuestarget: Ref<unknown>- The reactive reference to animatevalue: unknown- The target value to animate tooptions?: AnimationProps- Optional animation configuration
-
moveTo(target, x, y, options)- Move to positiontarget: Ref<ShapeConfig> | ShapeConfig- The shape object or reactive referencex: number- Target x coordinatey: number- Target y coordinateoptions?: AnimationProps- Optional animation configuration
-
scaleTo(target, scale, options)- Scale uniformly or per-axistarget: Ref<ShapeConfig> | ShapeConfig- The shape object or reactive referencescale: number | { x: number; y: number }- Uniform scale factor or separate x/y scalesoptions?: AnimationProps- Optional animation configuration
-
rotateTo(target, rotation, options)- Rotate to angletarget: Ref<ShapeConfig> | ShapeConfig- The shape object or reactive referencerotation: number- Target rotation angle in radiansoptions?: AnimationProps- Optional animation configuration
-
resizeTo(target, width, height, options)- Resize to dimensionstarget: Ref<ShapeConfig> | ShapeConfig- The shape object or reactive referencewidth: number- Target widthheight: number- Target heightoptions?: AnimationProps- Optional animation configuration
-
fadeTo(target, opacity, options)- Fade to opacitytarget: Ref<ShapeConfig> | ShapeConfig- The shape object or reactive referenceopacity: number- Target opacity (0-1)options?: AnimationProps- Optional animation configuration
-
show(target, options)- Fade in to full opacitytarget: Ref<ShapeConfig> | ShapeConfig- The shape object or reactive referenceoptions?: AnimationProps- Optional animation configuration
-
hide(target, options)- Fade out to transparenttarget: Ref<ShapeConfig> | ShapeConfig- The shape object or reactive referenceoptions?: AnimationProps- Optional animation configuration
step(...animations)- Group animations to run simultaneously...animations: AnimationInstruction[]- Variable number of animation instructions- Returns:
AnimationInstruction[]- Array of animation instructions
interface AnimationProps {
duration?: number; // Animation duration in milliseconds (default: 1000)
delay?: number; // Delay before animation starts in milliseconds (default: 0)
easing?: EasingFunction | EasingPreset; // Easing function or preset name
}Manages the execution of generator-based animations synchronized with Slidev's navigation.
Props:
generator?: () => AnimationGeneratorFunction- Function that returns the animation generatorskipThreshold?: number- Time threshold in ms for skipping animations (default: 300)defaultDuration?: number- Default animation duration in ms (default: 1000)defaultEasing?: string- Default easing preset name (default: "easeInOut")
Usage:
<Animator
:generator="myAnimationGenerator"
:skipThreshold="200"
:defaultDuration="800"
defaultEasing="bounceOut"
/>Renders rectangular blocks with customizable appearance and text content.
Props (BlockConfig):
x?: number- Horizontal positiony?: number- Vertical positionwidth?: number- Block widthheight?: number- Block heighttext?: string- Text content to displayscaleX?: number- Horizontal scale factor (default: 1)scaleY?: number- Vertical scale factor (default: 1)opacity?: number- Opacity level 0-1 (default: 1)rotation?: number- Rotation angle in radians (default: 0)offsetX?: number- Horizontal offset for transformationsoffsetY?: number- Vertical offset for transformationsoffset?: { x: number; y: number }- Alternative offset specificationrectConfig?: Partial<RectConfig>- Additional Konva rectangle propertiestextConfig?: Partial<TextConfig>- Additional Konva text properties
Usage:
<Block :config="{
x: 100, y: 50, width: 120, height: 60,
text: 'My Block', opacity: 0.8,
rectConfig: { fill: 'lightblue', stroke: 'navy' },
textConfig: { fontSize: 18, fill: 'darkblue' }
}" />Creates dynamic lines, arrows, and curves connecting shapes with automatic anchor point calculation.
Props (ConnectionOptions):
fromShape: Shape- Source shape objecttoShape: Shape- Target shape objectfromAnchor: AnchorPoint- Connection point on source ("left" | "right" | "top" | "bottom" | "center")toAnchor: AnchorPoint- Connection point on target ("left" | "right" | "top" | "bottom" | "center")connectionType: ConnectionType- Line style ("straight" | "curved" | "orthogonal")lineType: LineType- Line ending ("line" | "arrow" | "double-arrow")config?: ConnectionConfig- Optional styling configuration
ConnectionConfig Properties:
stroke?: string- Line color (default: "black")strokeWidth?: number- Line thickness (default: 2)dash?: number[]- Dash pattern for dashed linesfill?: string- Fill color for arrowspointerLength?: number- Arrow head lengthpointerWidth?: number- Arrow head widthtension?: number- Curve tension for curved connectionscornerRadius?: number- Corner radius for orthogonal connectionsopacity?: number- Connection opacity 0-1
Usage:
<Connection :config="{
fromShape: blockA, toShape: blockB,
fromAnchor: 'right', toAnchor: 'left',
connectionType: 'curved', lineType: 'arrow',
config: { stroke: 'blue', strokeWidth: 3, tension: 0.5 }
}" />Available easing presets: linear, easeIn, easeOut, easeInOut, bounceIn, bounceOut, bounceInOut, elasticIn, elasticOut, elasticInOut, backIn, backOut, backInOut, strongIn, strongOut, strongInOut
The slidev-addon-animations project follows a modular, TypeScript-first architecture designed for maintainability, testing, and extensibility.
slidev-addon-animations/
βββ π components/ # Vue components for UI elements
β βββ Animator.vue # Generator-based animation controller
β βββ Block.vue # Rectangular block component
β βββ Connection.vue # Dynamic connection lines
β βββ Graphic.vue # Konva stage wrapper
β
βββ π composables/ # Reusable animation logic
β βββ useKonvaAnimation.ts # Core animation system with step control
β βββ useGeneratorAnimation.ts # High-level generator-based animations
β
βββ π types/ # TypeScript type definitions
β βββ animation.ts # Core animation system types
β βββ block.ts # Block component types
β βββ componentProps.ts # Vue component prop interfaces
β βββ generatorAnimation.ts # Generator animation types
β βββ graphic.ts # Graphic component types
β βββ lerpSystem.ts # Interpolation system types
β βββ shapeConnector.ts # Shape connection types
β βββ index.ts # Unified type exports
β
βββ π utils/ # Animation engine utilities
β βββ animationEngine.ts # Core animation processing & timing
β βββ animationHelpers.ts # Helper functions for creating animations
β βββ constants.ts # Shared constants and configurations
β βββ lerpSystem.ts # Value interpolation system
β βββ shapeConnector.ts # Connection geometry calculations
β
βββ π setup/ # Slidev integration
β βββ main.ts # Slidev plugin setup and registration
β
βββ π tests/ # Comprehensive test suite
β βββ animationEngine.test.ts # Core animation engine tests
β βββ animationHelpers.test.ts # Helper function tests
β βββ examples.test.ts # Integration example tests
β βββ generatorBug.test.ts # Generator-specific bug tests
β βββ helpers.ts # Test utility functions
β βββ integration.test.ts # End-to-end integration tests
β βββ lerpSystem.test.ts # Interpolation system tests
β βββ refPrimitiveAnimation.test.ts # Ref primitive animation tests
β βββ setup.ts # Test environment setup
β βββ stepDelays.test.ts # Step delay timing tests
β βββ types.d.ts # Test-specific type definitions
β
βββ π .github/ # GitHub workflows and automation
β βββ workflows/ # CI/CD pipeline definitions
β
βββ π index.ts # Main entry point and exports
βββ π package.json # Package configuration and dependencies
βββ π example.md # Live example presentation
βββ π vitest.config.ts # Test configuration
βββ π biome.json # Code quality and formatting rules
βββ π README.md # This documentation
- Components: Pure UI presentation layer
- Composables: Reusable business logic
- Utils: Core algorithms and calculations
- Types: Comprehensive type safety
- Vue 3 Composition API throughout
- Reactive references for real-time updates
- Computed properties for derived state
- Watch effects for side effects
- Comprehensive unit test coverage
- Integration tests for complex workflows
- Performance benchmarks for animations
- Example-based testing for user scenarios
- Clean public API through
index.ts - Tree-shakable imports
- TypeScript-first design
- Consistent naming conventions
// Clean, readable animation sequences
function* myAnimation() {
yield moveTo(shape, 100, 200);
yield scaleTo(shape, 1.5);
yield step(
fadeTo(shapeA, 0),
fadeTo(shapeB, 1)
);
}// Connections automatically update when shapes move
const connection = computed(() => ({
fromShape: blockA.value,
toShape: blockB.value,
// ... connection config
}));// Full TypeScript support for all animation properties
const target: AnimationTarget = {
target: shapeRef.value,
steps: [...],
initialState: { x: 0, y: 0 }
};- Animation Skipping: Rapid navigation detection
- Shallow Reactivity: Optimized ref usage
- Batched Updates: Microtask scheduling
- Memory Management: Proper cleanup and disposal
const customEasing = (t, b, c, d) => {
// Custom easing implementation
return c * t / d + b
}
yield animate(target, { x: 100 }, {
duration: 1000,
easing: customEasing
})function* complexAnimation() {
// Parallel animations with different timings
yield step(
moveTo(obj1, 200, 100, { duration: 1000 }),
scaleTo(obj2, 1.5, { duration: 800, delay: 200 }),
fadeTo(obj3, 0.5, { duration: 600, delay: 400 })
)
// Sequential with staggered delays
yield step(
...elements.map((el, i) =>
animate(el, { opacity: 1 }, {
duration: 500,
delay: i * 100
})
)
)
}const connectionOpacity = ref(0);
const connection = computed(() => ({
fromShape: blockA.value,
toShape: blockB.value,
connectionType: 'curved',
config: {
stroke: 'blue',
strokeWidth: 3,
opacity: connectionOpacity.value
}
}))
// Animate connection appearance
yield animate(connectionOpacity, 1, { duration: 600 })- Fork and clone the repository
- Install dependencies:
bun installornpm install - Start development:
bun run devornpm run dev - Run tests:
bun testornpm test - Check code quality:
bun run checkornpm run check - Submit pull request with your improvements
- Write tests for new features
- Follow TypeScript best practices
- Use Biome for code formatting
- Document new APIs and components
- Update example.md to showcase new features
MIT Β© Data Competence Center Basel-Stadt
Datenwissenschaften und KI
Developed with β€οΈ by Data Alchemy Team

