Hi everyone I can program, and I saw a few weeks ago, that SVG animations are pretty useful!
Now my question:
I want to animate text like this website: https://weaintplastic.com/ I do not want the other things, just the Animation! I tried a lot of things, Exported via Illustrator a svg but it did not worked out. I do not want to use, any tool from github!
My assumption is:
I have to animate any letter in a Word (like the website), such that I have to know the paths from the svg! But this makes problem! How can I get a animation like this? Lets assume I want the word for ABOUT ME for example, how can I achieve CSS and JS which does the job for any svg if I have the path, or do I have to do for every Letter an own CSS and JS?
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 315 45" style="enable-background:new 0 0 315 45;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;}
</style>
<g>
<path d="M78.55,26.83l-2,5.75h-2.57l6.53-18.3h2.99l6.56,18.3h-2.65l-2.05-5.75H78.55z M84.85,24.98l-1.88-5.27
c-0.43-1.19-0.71-2.28-1-3.34h-0.06c-0.29,1.09-0.6,2.2-0.97,3.31l-1.88,5.29H84.85z"/>
<path d="M92.61,32.58c0.06-0.9,0.11-2.23,0.11-3.39V13.31h2.48v8.25h0.06c0.88-1.47,2.48-2.42,4.71-2.42c3.42,0,5.85,2.71,5.82,6.7
c0,4.7-3.11,7.03-6.19,7.03c-2,0-3.59-0.73-4.62-2.47h-0.09l-0.11,2.17H92.61z M95.2,27.32c0,0.3,0.06,0.6,0.11,0.87
c0.48,1.66,1.94,2.8,3.76,2.8c2.62,0,4.19-2.04,4.19-5.05c0-2.63-1.43-4.89-4.11-4.89c-1.71,0-3.31,1.11-3.82,2.93
c-0.06,0.27-0.14,0.6-0.14,0.98V27.32z"/>
<path d="M121.44,25.9c0,4.86-3.54,6.98-6.87,6.98c-3.74,0-6.62-2.61-6.62-6.76c0-4.4,3.02-6.98,6.84-6.98
C118.75,19.15,121.44,21.89,121.44,25.9z M110.48,26.04c0,2.88,1.74,5.05,4.19,5.05c2.4,0,4.19-2.14,4.19-5.1
c0-2.23-1.17-5.05-4.14-5.05S110.48,23.54,110.48,26.04z"/>
<path d="M136.15,29c0,1.36,0.03,2.55,0.11,3.58h-2.22l-0.14-2.14h-0.06c-0.66,1.06-2.11,2.44-4.56,2.44
c-2.17,0-4.76-1.14-4.76-5.75v-7.68h2.51v7.27c0,2.5,0.8,4.18,3.08,4.18c1.68,0,2.85-1.11,3.31-2.17c0.14-0.35,0.23-0.79,0.23-1.22
v-8.06h2.51V29z"/>
<path d="M143.33,15.67v3.77h3.59v1.82h-3.59v7.09c0,1.63,0.48,2.55,1.88,2.55c0.66,0,1.14-0.08,1.45-0.16l0.11,1.79
c-0.48,0.19-1.25,0.33-2.22,0.33c-1.17,0-2.11-0.35-2.71-1c-0.71-0.71-0.97-1.87-0.97-3.42v-7.17h-2.14v-1.82h2.14v-3.15
L143.33,15.67z"/>
<path d="M172.08,24.55c-0.14-2.55-0.31-5.62-0.29-7.9h-0.09c-0.66,2.14-1.45,4.42-2.42,6.95l-3.39,8.88h-1.88l-3.11-8.71
c-0.91-2.58-1.68-4.94-2.22-7.11h-0.06c-0.06,2.28-0.2,5.35-0.37,8.09l-0.51,7.84h-2.37l1.34-18.3h3.17l3.28,8.85
c0.8,2.25,1.46,4.26,1.94,6.16h0.09c0.48-1.85,1.17-3.86,2.02-6.16l3.42-8.85h3.17l1.2,18.3h-2.42L172.08,24.55z"/>
<path d="M188.76,24h-7.47v6.6h8.33v1.98H178.8v-18.3h10.38v1.98h-7.9v5.78h7.47V24z"/>
</g>
<g>
<rect x="54.72" y="8.99" class="st0" width="69.62" height="7.96"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>
I tried figma and svgator, and so on, but this is not useful, since I want to DO the ANIMATION WHEN I SCROLL on the section. (Once like the website).
I am sorry if my question is not Perfect, I tried my best to be precise as possible, and searched first on the Forum before I asked.
The technique has to be a combination with SVGGeometryElement.getTotalLength, stroke-dasharray, stroke-dashoffset, and the Intersection Observer API.
The example you provided has a lot more complex SVG elements than your own example. If you inspect the SVG you'll see that each letter consists of 1 or more <polygon> elements that are being animated. Being that your own SVG is a bit simpler I've built the example below on it.
First thing that is important is that the animation in the example animates the stroke property of each polygon. They do that by calculating the entire length of each path and setting both the stroke-dasharray and stroke-dashoffset properties with that total calculated path length. This will push the stroke out of the svg to be invisible, setting up the animation. (Each path has a different length, so setting the properties manually is a tedious job, let JS do it).
Then use the Intersection Observer API to determine when certain elements come into view. In this case I've chosen to watch for whole sections to come into view. Whenever a section enters the viewport, it will add an animate class to the section. In CSS I've defined that the class will trigger an animation that will change the stroke-dashoffset property. This will cause the stroke to animate back into the SVG.
const paths = document.querySelectorAll('svg path');
const sections = document.querySelectorAll('section');
paths.forEach((path, index) => {
const length = path.getTotalLength();
path.style.strokeDashoffset = `${length}px`;
path.style.strokeDasharray = `${length}px`;
path.style.animationDelay = `${index * 250}ms`;
});
const onInterSection = (entries, observer) => {
for (const { isIntersecting, target } of entries) {
if (isIntersecting) {
target.classList.add('animate');
observer.unobserve(target);
}
}
};
const observer = new IntersectionObserver(onInterSection);
for (const section of sections) {
observer.observe(section);
}
.st0 {
fill: none;
}
section {
display: grid;
place-items: center;
height: 75vh;
}
section:first-of-type {
background-color: #f7f7f7;
}
section:nth-of-type(2) {
background-color: #d7d7d7;
}
section:nth-of-type(2) {
background-color: #e7e7e7;
}
path {
stroke: black;
stroke-width: 1px;
fill: none;
}
section.animate path {
animation: letter 2s 1s forwards ease-out;
}
#keyframes letter {
to {
stroke-dashoffset: 0;
}
}
<section>
<span>Scroll down to animation</span>
</section>
<section>
<span>A bit further</span>
</section>
<section>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 315 45" style="enable-background:new 0 0 315 45;" xml:space="preserve">
<path d="M78.55,26.83l-2,5.75h-2.57l6.53-18.3h2.99l6.56,18.3h-2.65l-2.05-5.75H78.55z M84.85,24.98l-1.88-5.27
c-0.43-1.19-0.71-2.28-1-3.34h-0.06c-0.29,1.09-0.6,2.2-0.97,3.31l-1.88,5.29H84.85z"/>
<path d="M92.61,32.58c0.06-0.9,0.11-2.23,0.11-3.39V13.31h2.48v8.25h0.06c0.88-1.47,2.48-2.42,4.71-2.42c3.42,0,5.85,2.71,5.82,6.7
c0,4.7-3.11,7.03-6.19,7.03c-2,0-3.59-0.73-4.62-2.47h-0.09l-0.11,2.17H92.61z M95.2,27.32c0,0.3,0.06,0.6,0.11,0.87
c0.48,1.66,1.94,2.8,3.76,2.8c2.62,0,4.19-2.04,4.19-5.05c0-2.63-1.43-4.89-4.11-4.89c-1.71,0-3.31,1.11-3.82,2.93
c-0.06,0.27-0.14,0.6-0.14,0.98V27.32z"/>
<path d="M121.44,25.9c0,4.86-3.54,6.98-6.87,6.98c-3.74,0-6.62-2.61-6.62-6.76c0-4.4,3.02-6.98,6.84-6.98
C118.75,19.15,121.44,21.89,121.44,25.9z M110.48,26.04c0,2.88,1.74,5.05,4.19,5.05c2.4,0,4.19-2.14,4.19-5.1
c0-2.23-1.17-5.05-4.14-5.05S110.48,23.54,110.48,26.04z"/>
<path d="M136.15,29c0,1.36,0.03,2.55,0.11,3.58h-2.22l-0.14-2.14h-0.06c-0.66,1.06-2.11,2.44-4.56,2.44
c-2.17,0-4.76-1.14-4.76-5.75v-7.68h2.51v7.27c0,2.5,0.8,4.18,3.08,4.18c1.68,0,2.85-1.11,3.31-2.17c0.14-0.35,0.23-0.79,0.23-1.22
v-8.06h2.51V29z"/>
<path d="M143.33,15.67v3.77h3.59v1.82h-3.59v7.09c0,1.63,0.48,2.55,1.88,2.55c0.66,0,1.14-0.08,1.45-0.16l0.11,1.79
c-0.48,0.19-1.25,0.33-2.22,0.33c-1.17,0-2.11-0.35-2.71-1c-0.71-0.71-0.97-1.87-0.97-3.42v-7.17h-2.14v-1.82h2.14v-3.15
L143.33,15.67z"/>
<path d="M172.08,24.55c-0.14-2.55-0.31-5.62-0.29-7.9h-0.09c-0.66,2.14-1.45,4.42-2.42,6.95l-3.39,8.88h-1.88l-3.11-8.71
c-0.91-2.58-1.68-4.94-2.22-7.11h-0.06c-0.06,2.28-0.2,5.35-0.37,8.09l-0.51,7.84h-2.37l1.34-18.3h3.17l3.28,8.85
c0.8,2.25,1.46,4.26,1.94,6.16h0.09c0.48-1.85,1.17-3.86,2.02-6.16l3.42-8.85h3.17l1.2,18.3h-2.42L172.08,24.55z"/>
<path d="M188.76,24h-7.47v6.6h8.33v1.98H178.8v-18.3h10.38v1.98h-7.9v5.78h7.47V24z"/>
<rect x="54.72" y="8.99" class="st0" width="69.62" height="7.96"/>
</svg>
</section>
For a game I'm working on,
I would like to be able to draw a SVG rectangle; using a percentage value (50% would draw half the rectangle stroke).
I need to do this in Javascript since I'll update the value quite often.
<svg id="rectangle-timer" style="width:100%;height:100%;">
<rect width="100%" height="100%"/>
</svg>
I saw quite nice JS libraries like drawSVG or Vivus, but it seems that they work with paths, not with basic shapes like rectangles.
Can anyone help ?
Thanks.
The reason most libraries will use path elements is because of their inheritance from the SVGGeometryElement prototype, which gives you handy functions for computing the path length. So if we swap out this rectangle for a path like this:
<path d="M 0 0 L 1 0 L 1 1 L 0 1 z" />
We get exactly the same output, but its much more controllable. After that, we can just adjust the strokeDasharray value in the style to extend and remove some stroke. For this property we just need two values: initial dash size and initial empty space. So when our progress is 0, we want the first value to be 0 and the second to be the path length , and as we approach 1 we want the second value to 0 and the first one to increase to the path length.
function update( amount ){
const total = rect.getTotalLength();
const filled = total * amount;
const none = total - filled;
rect.style.strokeDasharray = `${filled} ${none}`;
}
const rect = document.getElementById( 'rectangle' );
const input = document.getElementById( 'input' );
input.addEventListener( 'mousemove', event => update( input.value ));
update( input.value );
<svg width="200px" height="200px" viewBox="0 0 200 200">
<path d="M 20 20 L 180 20 L 180 180 L 20 180 z" id="rectangle" fill="none" stroke="black" stroke-width="10" />
</svg>
<input id="input" type="range" min="0" max="1" step=".01" />
If you insist on using a rect, you could get a rectangle's path length by taking its width and height twice, which would look something like this:
function update( amount ){
const total = rect.width.baseVal.value * 2 + rect.height.baseVal.value * 2;
const filled = total * amount;
const none = total - filled;
rect.style.strokeDasharray = `${filled} ${none}`;
}
const rect = document.getElementById( 'rectangle' );
const input = document.getElementById( 'input' );
input.addEventListener( 'mousemove', event => update( input.value ));
update( input.value );
<svg width="200px" height="200px" viewBox="0 0 200 200">
<rect x="20" y="20" width="160" height="160" id="rectangle" fill="none" stroke="black" stroke-width="10" />
</svg>
<input id="input" type="range" min="0" max="1" step=".01" />
In the long run, however, this would mean less versatility, so I would suggest switching to path.
This is my solution: The SVG has preserveAspectRatio ="none" style="width:100%;height:100vh;" The total length of the path is 2*window.innerWidth + 2*window.innerHeight; Both stroke-dasharray and stroke-dashoffset are igual to the total length of the path.
I'm using an input type="range" to animate the stroke-dashoffset.
In order to preserve the stroke width and avoid stretching I'm using vector-effect="non-scaling-stroke"
I hope this is what you need.
function timer(){
let totalLength = 2*window.innerWidth + 2*window.innerHeight;
thePath.setAttributeNS(null, "style", `stroke-dashoffset:${totalLength * (1-range.value)}`)
}
timer()
range.addEventListener("input",timer);
setTimeout(function() {
timer()
addEventListener('resize', timer, false);
}, 15);
*{margin:0; padding:0;}
#thePath {
stroke-dasharray: calc(2 * 100vw + 2* 100vh);
stroke-dashoffset: calc(2 * 100vw + 2* 100vh);
}
#rectangle-timer{background:#dfdfdf}
[type="range"] {
position: absolute;
display: block;
width: 200px;
height: 20px;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
<svg id="rectangle-timer" viewBox="0 0 100 100" preserveAspectRatio ="none" style="width:100%;height:100vh;">
<path id="thePath" d="M0,0L100,0 100,100 0,100 0,0" fill="none" stroke="skyBlue" stroke-width="25" vector-effect="non-scaling-stroke" />
</svg>
<input type="range" id="range" value=".5" min="0" max="1" step=".01" />
I'm trying to display a bunch of weather stations on a Leaflet map with SVG icons. Right now I'm just trying to get a handle on the drawing part.
The icons are tear shaped and should rotate around the "circle" part of the icon depending on the direction of the wind.
I'm having a hard time figuring out how to setup the transform and transform-origin so that it the icon "stays in place" and rotates around the "circle" in the path.
In the example below the number should stay in the middle of the circle.
var svg = d3.select('#icon svg');
// this is really done dynamically based on wind direction
var d = 0;
var path = svg.select('path');
// The animated rotation is just to make the example easy to verify.
function rotate(){
d = (d + 15 < 360) ? d + 15 : 0;
path.style('transform', 'rotate('+d+'deg)');
window.setTimeout(rotate, 60);
};
rotate();
svg {
overflow: visible;
background-color: #ffedfd
}
.station-icon path {
/** what am I supposed to use here? **/
transform-origin: center 40%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="icon" class="leaflet-marker-icon station-icon">
<svg width="26" height="26">
<g transform="translate(0,-6)">
<path class="st0" d="M26,19c0-2.2-0.6-4.4-1.6-6.2C22.2,8.8,13,0,13,0S3.8,8.7,1.6,12.8c-1,1.8-1.6,4-1.6,6.2c0,7.2,5.8,13,13,13
S26,26.2,26,19z"/>
</g>
<g>
<text x="13" y="13" font-family="sans-serif" font-size="20px" fill="white" text-anchor="middle" alignment-baseline="central">6</text>
</g>
</svg>
</div>
An alternative without using transform-origin is setting the center of the rotation (here, using magic numbers):
path.attr('transform', 'rotate('+d+' 13 19)');
var svg = d3.select('#icon svg');
// this is really done dynamically based on wind direction
var d = 0;
var path = svg.select('path');
// The animated rotation is just to make the example easy to verify.
function rotate(){
d = (d + 15 < 360) ? d + 15 : 0;
path.attr('transform', 'rotate('+d+' 13 19)');
window.setTimeout(rotate, 60);
};
rotate();
svg {
overflow: visible;
background-color: #ffedfd
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="icon" class="leaflet-marker-icon station-icon">
<svg width="26" height="26">
<g transform="translate(0,-6)">
<path class="st0" d="M26,19c0-2.2-0.6-4.4-1.6-6.2C22.2,8.8,13,0,13,0S3.8,8.7,1.6,12.8c-1,1.8-1.6,4-1.6,6.2c0,7.2,5.8,13,13,13
S26,26.2,26,19z"/>
</g>
<g>
<text x="13" y="13" font-family="sans-serif" font-size="20px" fill="white" text-anchor="middle" alignment-baseline="central">6</text>
</g>
</svg>
</div>
Does setting the origin to 60% achieve what you want?
var svg = d3.select('#icon svg');
// this is really done dynamically based on wind direction
var d = 0;
var path = svg.select('path');
function rotate(){
d = (d + 15 < 360) ? d + 15 : 0;
path.style('transform', 'rotate('+d+'deg)');
window.setTimeout(rotate, 60);
};
rotate();
svg {
overflow: visible;
background-color: #ffedfd
}
.station-icon path {
/** what am I supposed to use here? **/
transform-origin: center 60%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="icon" class="leaflet-marker-icon station-icon">
<svg width="26" height="26">
<g transform="translate(0,-6)">
<path class="st0" d="M26,19c0-2.2-0.6-4.4-1.6-6.2C22.2,8.8,13,0,13,0S3.8,8.7,1.6,12.8c-1,1.8-1.6,4-1.6,6.2c0,7.2,5.8,13,13,13
S26,26.2,26,19z"/>
</g>
<g>
<text x="13" y="13" font-family="sans-serif" font-size="20px" fill="white" text-anchor="middle" alignment-baseline="central">6</text>
</g>
</svg>
</div>