SVG Animation: CSS, SMIL & JavaScript Techniques
SVG (Scalable Vector Graphics) provides resolution-independent vector graphics in the browser that can be animated via CSS, SMIL (native SVG animation elements), or JavaScript libraries like GSAP and anime.js.
What You’ll Learn
You’ll understand the SVG viewBox coordinate system, create grouped vector illustrations with <g>, animate with CSS keyframes, use SMIL’s <animate> and <animateTransform> elements, integrate GSAP and anime.js libraries, and build interactive SVGs that respond to user input.
Why It Matters
SVG animations are lightweight, resolution-independent, and perform better than GIF or video for UI micro-interactions, loading spinners, data visualizations, and icon animations. DodaZIP uses animated SVG icons for its compression progress indicators. Durga Antivirus Pro renders animated threat-level badges with SVG.
Real-World Use
A dashboard loading spinner animated with SVG, a logo that animates on page scroll, an interactive infographic with hover tooltips, and a weather icon that smoothly transitions from sun to rain — all powered by SVG animation.
SVG Animation Techniques Decision
flowchart LR
A[SVG Animation] --> B{Method}
B -->|Simple transitions| C[CSS Animations]
B -->|Native elements| D[SMIL]
B -->|Complex timelines| E[JS Libraries]
C --> F[`@keyframes`, `transition`]
D --> G[``, ``]
E --> H[GSAP, anime.js]
style C fill:#10b981,color:#fff
style D fill:#3b82f6,color:#fff
style E fill:#8b5cf6,color:#fff
Step 1: SVG Coordinate System and Basic Shapes
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="30" fill="#ff6633" />
<rect x="100" y="20" width="60" height="60" rx="8" fill="#3366ff" />
<path d="M 20 150 L 100 100 L 180 150 Z" fill="#33cc66" />
</svg>Expected output: An orange circle at top-left, a blue rounded rectangle at top-right, and a green triangle below. The viewBox="0 0 200 200" defines the coordinate space — everything scales proportionally.
Step 2: CSS Animations on SVG
<style>
@keyframes pulse {
0% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.3); opacity: 0.7; }
100% { transform: scale(1); opacity: 1; }
}
@keyframes dash {
to { stroke-dashoffset: 0; }
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.pulsing-circle {
animation: pulse 2s ease-in-out infinite;
transform-origin: center;
}
.drawing-path {
stroke-dasharray: 500;
stroke-dashoffset: 500;
animation: dash 3s ease forwards;
}
.spinner {
animation: spin 1s linear infinite;
transform-origin: center;
}
</style>
<svg viewBox="0 0 300 200" xmlns="http://www.w3.org/2000/svg">
<!-- Pulsing circle -->
<circle class="pulsing-circle" cx="60" cy="100" r="30" fill="#ff6633" />
<!-- Stroke-dash drawing animation -->
<path class="drawing-path" d="M 120 160 L 180 40 L 240 160"
fill="none" stroke="#3366ff" stroke-width="4" stroke-linecap="round" />
<!-- Loading spinner -->
<circle class="spinner" cx="270" cy="100" r="20"
fill="none" stroke="#33cc66" stroke-width="4"
stroke-dasharray="30 100" />
</svg>Expected output: An orange circle pulses (scales up and down). A blue zigzag line draws itself from left to right over 3 seconds. A green spinner rotates continuously — the dashed stroke creates the spinning effect.
Step 3: SMIL Native Animation
SMIL (Synchronized Multimedia Integration Language) is SVG’s native animation system — no CSS or JS needed:
<svg viewBox="0 0 400 200" xmlns="http://www.w3.org/2000/svg">
<!-- Moving circle -->
<circle cx="50" cy="100" r="20" fill="#ff6633">
<animate
attributeName="cx"
values="50; 350; 50"
dur="4s"
repeatCount="indefinite" />
<animate
attributeName="fill"
values="#ff6633; #3366ff; #33cc66; #ff6633"
dur="4s"
repeatCount="indefinite" />
</circle>
<!-- Rotating rectangle -->
<rect x="150" y="50" width="60" height="60" fill="#3366ff">
<animateTransform
attributeName="transform"
type="rotate"
from="0 180 80"
to="360 180 80"
dur="3s"
repeatCount="indefinite" />
</rect>
<!-- Bouncing ball with easing -->
<circle cx="300" cy="30" r="15" fill="#33cc66">
<animate
attributeName="cy"
values="30; 180; 30"
dur="1s"
repeatCount="indefinite"
calcMode="spline"
keySplines="0.5 0 1 0.5; 0.5 0.5 1 0"
keyTimes="0; 0.5; 1" />
</circle>
</svg>Expected output: An orange circle moves left-to-right and back, cycling through orange, blue, green. A blue rectangle rotates continuously around its center. A green ball bounces with realistic easing — slowing at the top and accelerating downward.
SMIL vs CSS Animation
| Feature | SMIL | CSS |
|---|---|---|
| Attribute animation | Any SVG attribute (cx, fill, d) | Only CSS properties |
| Motion along path | <animateMotion> | Not supported natively |
| Browser support | IE/Edge limited | All modern browsers |
| Scripting control | Difficult to pause/resume | animation-play-state |
| Performance | Native, very smooth | GPU-accelerated |
Step 4: JavaScript Animation with Anime.js
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script>
<svg viewBox="0 0 500 200" id="morph-svg">
<path id="morph-path"
d="M 50 100 Q 125 20 200 100 Q 275 180 350 100 Q 425 20 500 100"
fill="none" stroke="#ff6633" stroke-width="4" />
<circle id="morph-circle" cx="50" cy="100" r="10" fill="#ff6633" />
</svg>
<button onclick="startMorph()">Animate</button>
<script>
function startMorph() {
// Animate circle along path
anime({
targets: "#morph-circle",
cx: [50, 500],
cy: [100, 100],
easing: "easeInOutSine",
duration: 3000,
loop: true,
});
// Morph path shape
anime({
targets: "#morph-path",
d: [
{ value: "M 50 100 Q 125 20 200 100 Q 275 180 350 100 Q 425 20 500 100" },
{ value: "M 50 100 Q 125 180 200 100 Q 275 20 350 100 Q 425 180 500 100" },
],
easing: "easeInOutQuad",
duration: 2000,
direction: "alternate",
loop: true,
});
}
</script>Expected output: Click “Animate” — a circle slides along the sine wave path while the path itself morphs between two wave shapes, creating a fluid organic animation.
Step 5: Interactive SVGs
<style>
.interactive-gear { transition: transform 0.3s ease; cursor: pointer; }
.interactive-gear:hover { transform: rotate(45deg); }
.bar { transition: height 0.5s ease; }
.chart-container:hover .bar:nth-child(1) { height: 120px; }
.chart-container:hover .bar:nth-child(2) { height: 80px; }
.chart-container:hover .bar:nth-child(3) { height: 160px; }
</style>
<svg viewBox="0 0 300 200" xmlns="http://www.w3.org/2000/svg">
<!-- Interactive chart bars -->
<g class="chart-container">
<rect class="bar" x="30" y="100" width="40" height="60" fill="#ff6633" rx="4" />
<rect class="bar" x="90" y="130" width="40" height="30" fill="#3366ff" rx="4" />
<rect class="bar" x="150" y="80" width="40" height="80" fill="#33cc66" rx="4" />
</g>
<!-- Clickable gear -->
<g class="interactive-gear" onclick="alert('Gear clicked!')">
<circle cx="250" cy="100" r="25" fill="none" stroke="#888" stroke-width="6" stroke-dasharray="10 5" />
<circle cx="250" cy="100" r="10" fill="#888" />
</g>
</svg>Expected output: Three chart bars. Hovering over the container animates the bars to different heights. A gear icon rotates on hover and shows an alert when clicked.
Common Errors
1. SVG animations don’t work in CSS because of transform-origin
SVG elements default transform-origin to (0, 0) (top-left of the SVG), not the element’s center. Always set transform-origin: center explicitly. For precise control, use pixel values: transform-origin: 50px 50px.
2. SMIL animations on Firefox are choppy Older Firefox versions had SMIL performance issues. Use CSS animations or JavaScript for complex scenes. Firefox fixed most performance problems in version 85+, but CSS is still more reliable across browsers.
3. Path morphing distorts instead of smoothly transitioning
Both paths must have the same number of points and the same command structure. You can’t morph a 4-point M...L...L...L...Z rectangle into a 3-point M...L...L...Z triangle. Use a path editor (like SVGOMG) to normalize path structures.
4. stroke-dasharray animation doesn’t animate
You must set stroke-dasharray and stroke-dashoffset to the same large value (the path length). Use getTotalLength() in JavaScript to get the exact length: path.getTotalLength(). Set both properties to this value, then animate stroke-dashoffset to 0.
5. SVG with foreignObject doesn’t render
<foreignObject> allows HTML inside SVG but is inconsistently supported. Use it only for simple text. For complex HTML overlays, position an HTML <div> over the SVG using CSS absolute positioning instead.
Practice Questions
1. What is the difference between CSS and SMIL SVG animation?
CSS animates CSS properties (transform, opacity, color) using @keyframes and transitions. SMIL animates any SVG attribute (cx, d, fill) natively using <animate> elements. SMIL can animate path data and motion along paths; CSS cannot without JavaScript.
2. How does viewBox affect SVG scaling?
viewBox="0 0 width height" defines the coordinate system and aspect ratio. When the SVG element is resized via CSS, the viewBox ensures all content scales proportionally. Without viewBox, the SVG uses pixel coordinates and may clip or stretch.
3. What is stroke-dasharray and how does it enable drawing animations?
stroke-dasharray creates dashed strokes. When you set stroke-dasharray equal to the path length and stroke-dashoffset to the same value, the stroke is invisible. Animating offset to 0 reveals the path progressively — creating a drawing effect.
4. Why use GSAP or anime.js instead of CSS animations? JavaScript animation libraries provide timeline control (sequence, overlap, delay), easing beyond CSS’s built-in functions, callback hooks, and runtime-controlled reverse/pause/seek. CSS is simpler for single-element loops; JS is better for orchestrated sequences.
5. Challenge: Build an animated SVG loading spinner
Create a circular spinner with 8 dots evenly spaced. Each dot fades in and out sequentially using CSS animation-delay. The dots should animate in a rotating pattern that creates the illusion of continuous motion.
FAQ
What’s Next
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro