-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add post about animating a pirate treasure map path
- Loading branch information
1 parent
eac5afa
commit ffe1235
Showing
4 changed files
with
139 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
--- | ||
layout: block | ||
title: "Animating a pirate treasure map path" | ||
subtitle: “X” marks the spot. | ||
date: 2023-09-10 16:36:40 +0100 | ||
image: | ||
path: /assets/images/treasure-map.png | ||
block: | ||
path: animating-a-pirate-treasure-map-path.html | ||
--- | ||
Animating an SVG line is nothing to write home about. It has already been described in great detail [many], [multiple], [different][different] [times][times]. We make the line use dashes, configure it so that there is a single dash that spans the entire line, and then we animate the offset of that dash. Done. | ||
|
||
But what if we want to animate a line that itself is dashed? Like a dashed line in a treasure map? | ||
|
||
As it turns out, we can animate it in pretty much the same way! But instead of animating the dashed line, we mask that dashed line with an identical line, and then we animate _that_ line. So, step by step: | ||
|
||
1. Define the line `path` inside a `defs` element so that it's not drawn. | ||
|
||
2. Draw the line by defining a [`use`] element whose `xlink:href` attribute points to the `path` defined in the previous step. | ||
|
||
3. Create a mask with an identical line by using another `use` element. The mask must be white ([like in Photoshop]) and have the same `stroke` properties ([`stroke-width`], [`stroke-linecap`], [`stroke-miterlimit`], etc) as the drawn line so that it can cover it perfectly. | ||
|
||
4. Make the mask line use dashes, make it dashed with a single dash that spans its total length, and then animate the dash offset. The usual stuff. | ||
|
||
Finally, we can change the transition timing function to achieve different results. Using the [steps transition function] with the same amount of steps as the number of line dashes results in a nice effect. | ||
|
||
[many]: https://jakearchibald.com/2013/animated-line-drawing-svg/ | ||
[multiple]: https://css-tricks.com/svg-line-animation-works/ | ||
[different]: https://product.voxmedia.com/2013/11/25/5426880/polygon-feature-design-svg-animations-for-fun-and-profit | ||
[times]: https://tympanus.net/codrops/2013/12/30/svg-drawing-animation/ | ||
[`use`]: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use | ||
[like in Photoshop]: https://lab.iamvdo.me/css-svg-masks/#masking | ||
[`stroke-width`]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-width | ||
[`stroke-linecap`]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-linecap | ||
[`stroke-miterlimit`]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-miterlimit | ||
[steps transition function]: https://developer.mozilla.org/en-US/docs/Web/CSS/transition-timing-function#values |
100 changes: 100 additions & 0 deletions
100
assets/blocks/animating-a-pirate-treasure-map-path.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
<!DOCTYPE html> | ||
<meta charset="utf-8"> | ||
<style> | ||
html, body { | ||
height: 100%; | ||
margin: 0; | ||
} | ||
|
||
svg { | ||
width: 100%; | ||
height: 100%; | ||
display: block; | ||
} | ||
|
||
.controls { | ||
position: absolute; | ||
top: 1em; | ||
left: 1em; | ||
} | ||
|
||
#line { | ||
fill: none; | ||
stroke-width: 4; | ||
} | ||
|
||
#animated-line { | ||
stroke: currentColor; | ||
} | ||
|
||
#animated-line-mask { | ||
stroke: white; | ||
} | ||
</style> | ||
<body> | ||
|
||
<form class="controls"> | ||
<label><button type="button" value="continuous-line">Continuous line</button></label> | ||
<label><button type="button" value="continuous-dashes">Continuous dashes</button></label> | ||
<label><button type="button" value="dash-by-dash">Dash by dash</button></label> | ||
</form> | ||
<svg viewBox="0 0 560 98"> | ||
<defs> | ||
<path id="line" d="M108 10S-8 30 21 67c41 44 66-71 161-2 95 70 181-58 122-57-58 0-34 76 60 76 95 0 54-72 113-71s69 70 69 70" /> | ||
<mask id="animated-line-mask"> | ||
<use xlink:href="#line" /> | ||
</mask> | ||
</defs> | ||
<use xlink:href="#line" id="animated-line" mask="url(#animated-line-mask)" /> | ||
</svg> | ||
|
||
<script type="text/javascript"> | ||
"use strict"; | ||
|
||
var line = document.querySelector("#line"); | ||
var animatedLine = document.querySelector("#animated-line"); | ||
var animatedLineMask = document.querySelector("#animated-line-mask use"); | ||
var lineLength = line.getTotalLength(); | ||
var numDashes = 14; | ||
|
||
function animate(event) { | ||
var button = event.target; | ||
|
||
reset(); | ||
|
||
if (button.value === "continuous-line") { | ||
animatedLine.style.strokeDashoffset = lineLength; | ||
animatedLine.style.strokeDasharray = lineLength; | ||
setTimeout(() => { | ||
animatedLine.style["transition-duration"] = "3s"; | ||
animatedLine.style["transition-property"] = "stroke-dashoffset"; | ||
animatedLine.style.strokeDashoffset = 0; | ||
}, 10); | ||
} else { // "continuous-dashes" and "dash-by-dash" | ||
animatedLine.style.strokeDasharray = lineLength / (numDashes * 2 - 1); | ||
|
||
animatedLineMask.style.strokeDashoffset = lineLength; | ||
animatedLineMask.style.strokeDasharray = lineLength; | ||
setTimeout(() => { | ||
animatedLineMask.style["transition-duration"] = "3s"; | ||
animatedLineMask.style["transition-property"] = "stroke-dashoffset"; | ||
|
||
if (button.value === "dash-by-dash") { | ||
animatedLineMask.style["transition-timing-function"] = `steps(${numDashes}, jump-start)`; | ||
} | ||
|
||
animatedLineMask.style.strokeDashoffset = 0; | ||
}, 10); | ||
} | ||
} | ||
|
||
function reset() { | ||
animatedLine.removeAttribute('style'); | ||
animatedLineMask.removeAttribute('style'); | ||
animatedLine.style["transition-property"] = "none"; | ||
animatedLineMask.style["transition-property"] = "none"; | ||
} | ||
|
||
document.querySelector(".controls").addEventListener("click", animate); | ||
</script> | ||
</body> |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.