Every instance of <use xlink:href...> animates? - animation

I'm using the "use xlink:ref" tag to render a polyline that's defined in the "defs" tag and animate it. I want to also use xlink:ref to render the same polyline, but without the animation, i.e., as just a static object. But all three instances of the "use xlink:ref" that I render animate, even though there's no animation associated with two of them!? How can I have only one of the three instances of it animate? I don't understand why SVG thinks I want the other 2 instances to animate. Thanks for any help!
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="600" height="600" viewBox="0 0 600 600">
<defs>
<marker id="mCircle" markerWidth="160" markerHeight="160" refX="80" refY="80" markerUnits="userSpaceOnUse">
<circle cx="80" cy="80" r="70" style="fill: red; stroke: black; stroke-width: 5px; opacity:0.2"/>
</marker>
<marker id="mMid" markerWidth="100" markerHeight="100" refX="50" refY="50" markerUnits="userSpaceOnUse" >
<circle cx="50" cy="50" r="6" style="fill: black; stroke: none; stroke-width:4px; opacity:1"/>
</marker>
<path d="M 0,0 60,0 120,0 180,0 240,0 300,0 360,0" id="linered" style="marker-start: url(#mCircle); marker-end: url(#mCircle); marker-mid: url(#mMid); stroke-width: 12; fill:none; stroke-linejoin:round; stroke-linecap:round; stroke-opacity: 0.6"/>
</defs>
<use xlink:href="#linered" transform="translate(120, 280)" style="stroke-dasharray:222; stroke-dashoffset:-70px; stroke:orange;"/>
<animate
xlink:href="#linered"
attributename="d"
dur="2s"
begin="1s;"
values= "M 0 0, 60 0, 120 0, 180 0, 240 0, 300 0, 360 0;
M 180 0, 60 40, 120 -30, 180 60, 240 220, 300 60, 180 0;
M 180 0, 60 -33, 120 60, 180 -90, 240 70, 300 0, 180 0;
M 180 0, 60 40, 120 -30, 180 60, 240 220, 300 60, 180 0;
M 0 0, 60 0, 120 0, 180 0, 240 0, 300 0, 360 0;"
keySplines= "0 0.8 0.3 1; 0 0.8 0.3 1; 0 0.8 0.3 1; 0 0.8 0.3 1;"
keyTimes= "0; 0.33; 0.5; 0.66; 1"
calcMode="spline"
repeatcount="4"
/>
<g transform="translate(300 280)">
<use id="static1" xlink:href="#linered" x="-180" y="0" transform="rotate(60)" style="stroke-dasharray:222; stroke-dashoffset:-70px; stroke:blue;"/>
<use id="static2" xlink:href="#linered" x="-180" y="0" transform="rotate(120)" style="stroke-dasharray:222; stroke-dashoffset:-70px; stroke:white;"/>
</g>
</svg>

First, there are a few errors in your code concerning upper case letters in attributes. While Chrome seems to be a bit more forgiving (as defined in SVG v2), other browsers still adhere to SVG v1.1, which is fully XML-compliant in this regard. So you should always use case-sensitive markerUnits, attributeName and repeatCount attributes.
Your main problem is what you are animating:
<animate xlink:href="#linered" attributeName="d" ... />
Your animation references the <path id="linered" /> element, which is the template from which each <use> is derived. Each of them copies the whole template, including its animation.
Please note that your <animate> tag is a sibling to the <use> tag it is positioned next to. Both are self-closing tags, and there is no connection between those two exept their placement in the source code next to each other - but that is incitental.
The connection between an element and its animation can only be established in two ways.
an <animate> element is the child of the animated element (do not use the xlink:href attribute for this syntax):
<path ...>
<animate ... />
</path>
an <animate> element, positioned anywhere in the same svg fragment, references the animated element:
<path id="target"... />
<!-- other content -->
<animate xlink:href="#target" ... />
If you want to animate one <use> element, but not the others, you need to animate that element. For example you could animate a transform attribute set on the <use> element. But the d attribute you want to animate is part of the referenced <path> element.
The <use> element is no stand-in for what it is referencing, but conceptually a container that hides a copy of its reference inside (in what is called a Shadow DOM). This copy cannot be referenced with a url, or a CSS selector. It cannot be animated individually.
The only solution is to write down the element to be animated seperately (note the id):
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="600" height="600" viewBox="0 0 600 600">
<defs>
<marker id="mCircle" markerWidth="160" markerHeight="160" refX="80" refY="80" markerUnits="userSpaceOnUse">
<circle cx="80" cy="80" r="70" style="fill: red; stroke: black; stroke-width: 5px; opacity:0.2"/>
</marker>
<marker id="mMid" markerWidth="100" markerHeight="100" refX="50" refY="50" markerUnits="userSpaceOnUse" >
<circle cx="50" cy="50" r="6" style="fill: black; stroke: none; stroke-width:4px; opacity:1"/>
</marker>
<path id="linered" d="M 0,0 60,0 120,0 180,0 240,0 300,0 360,0" style="marker-start: url(#mCircle); marker-end: url(#mCircle); marker-mid: url(#mMid); stroke-width: 12; fill:none; stroke-linejoin:round; stroke-linecap:round; stroke-opacity: 0.6"/>
</defs>
<path id="linered-animated" transform="translate(120, 280)" d="M 0,0 60,0 120,0 180,0 240,0 300,0 360,0" style="marker-start: url(#mCircle); marker-end: url(#mCircle); marker-mid: url(#mMid); stroke-width: 12; fill:none; stroke-linejoin:round; stroke-linecap:round; stroke-opacity: 0.6;stroke-dasharray:222; stroke-dashoffset:-70px; stroke:orange;"/>
<animate
xlink:href="#linered-animated"
attributeName="d"
dur="2s"
begin="1s;"
values= "M 0 0, 60 0, 120 0, 180 0, 240 0, 300 0, 360 0;
M 180 0, 60 40, 120 -30, 180 60, 240 220, 300 60, 180 0;
M 180 0, 60 -33, 120 60, 180 -90, 240 70, 300 0, 180 0;
M 180 0, 60 40, 120 -30, 180 60, 240 220, 300 60, 180 0;
M 0 0, 60 0, 120 0, 180 0, 240 0, 300 0, 360 0;"
keySplines="0 0.8 0.3 1; 0 0.8 0.3 1; 0 0.8 0.3 1; 0 0.8 0.3 1;"
keyTimes= "0; 0.33; 0.5; 0.66; 1"
calcMode="spline"
repeatCount="4"
/>
<g transform="translate(300 280)">
<use id="static1" xlink:href="#linered" x="-180" y="0" transform="rotate(60)" style="stroke-dasharray:222; stroke-dashoffset:-70px; stroke:blue;"/>
<use id="static2" xlink:href="#linered" x="-180" y="0" transform="rotate(120)" style="stroke-dasharray:222; stroke-dashoffset:-70px; stroke:white;"/>
</g>
</svg>
As a footnote only, it seems that the xlink:href attribute in an <animate> element has a bug in Firefox where the animation is never applied to a referencing <use> element.

Related

Creating an SVG animated dashed line with a transparent background

I have been playing around with this for hours and trying to think up a solution.
In this... https://jsfiddle.net/WebFlair/nc0zyjdq/78/
<div class="bg">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="400.000000pt" height="400.000000pt" viewBox="0 0 400.000000 400.000000"
preserveAspectRatio="xMidYMid meet">
<path class="path" fill="none" stroke="#b0225e" stroke-linejoin="round" stroke-width="5" stroke-miterlimit="10" d="M23.742,10.709
c-2.305,23.611-8.81,46.563-9.021,70.829c-0.252,28.966,22.237,43.666,47.06,55.482c23.642,11.255,42.368,15.766,68.461,16.631
c19.993,0.663,40.08,2.97,59.853-1.723c23.301-5.531,45.542-17.598,66.978-27.933c19.248-9.281,38.831-21.86,41.946-45.201
c5.539-41.51-54.993-47.073-81.885-42.17C159.05,47.212,89.37,104.633,77.387,164.629c-5.896,29.522-4.312,60.884,12.703,86.354
c19.17,28.697,49.512,49.927,78.596,67.591"/>
<path class="dashed" fill="none" stroke="white" stroke-width="7" stroke-linejoin="round" stroke-miterlimit="16" d="M23.742,10.709
c-2.305,23.611-8.81,46.563-9.021,70.829c-0.252,28.966,22.237,43.666,47.06,55.482c23.642,11.255,42.368,15.766,68.461,16.631
c19.993,0.663,40.08,2.97,59.853-1.723c23.301-5.531,45.542-17.598,66.978-27.933c19.248-9.281,38.831-21.86,41.946-45.201
c5.539-41.51-54.993-47.073-81.885-42.17C159.05,47.212,89.37,104.633,77.387,164.629c-5.896,29.522-4.312,60.884,12.703,86.354
c19.17,28.697,49.512,49.927,78.596,67.591"/>
</svg>
</div>
<style>
.bg {background:#eee;}
.dashed{
stroke-dasharray: 14;
}
.path {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: dash 5s linear alternate forwards;
}
#keyframes dash {
from {
stroke-dashoffset: 1000;
}
to {
stroke-dashoffset: 0;
}
}
</style>
You will see a grey background and an animated white and pink dashed line.
Now I know this wont work in my example, but what I am trying to do is make the white part transparent so that it can be used on any background, keep the animation and the pink dashed line.
Can anyone think of a way to make that happen? I have gone over everything I can think of and always need the white part in order to keep the animation.
but what I am trying to do is make the white part transparent so that
it can be used on any background, keep the animation and the pink
dashed line.
The idea is as follows: use three layers
The first bottom layer will have a white path
The middle layer will have exactly the same pink path
There will be a masking path on the top layer, which will gradually
reveal the pink path
This uses the property of the mask when "stroke: white" shows the section of the pink path that is under it
And since the length of the masking path changes from zero to the maximum value (stroke-dashoffset decreases from the maximum 917 to zero), the growth of the pink path becomes visible
#keyframes dash {
from {
stroke-dashoffset: 917;
}
to {
stroke-dashoffset: 0;
}
Clarification the maximum path length is 917px
1. The first bottom layer will have a white path
.bg {background:#eee;}
#dashed {
stroke-dasharray: 14;
fill:none;
stroke:white;
stroke-width:7;
stroke-linejoin:round;
}
<div class="bg">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="400.000000pt" height="400.000000pt" viewBox="0 0 400.000000 400.000000"
preserveAspectRatio="xMidYMid meet">
<!-- Bottom layer dashed white line -->
<path id="dashed" d="M23.742,10.709
c-2.305,23.611-8.81,46.563-9.021,70.829c-0.252,28.966,22.237,43.666,47.06,55.482c23.642,11.255,42.368,15.766,68.461,16.631
c19.993,0.663,40.08,2.97,59.853-1.723c23.301-5.531,45.542-17.598,66.978-27.933c19.248-9.281,38.831-21.86,41.946-45.201
c5.539-41.51-54.993-47.073-81.885-42.17C159.05,47.212,89.37,104.633,77.387,164.629c-5.896,29.522-4.312,60.884,12.703,86.354
c19.17,28.697,49.512,49.927,78.596,67.591"/>
</svg>
</div>
<script>
console.log(dashed.getTotalLength());
</script>
2. The middle layer will have exactly the same pink path
<style>
.bg {background:#eee;}
#dashed {
stroke-dasharray: 14;
fill:none;
stroke:white;
stroke-width:7;
stroke-linejoin:round;
}
#pink {
stroke-dasharray: 14;
fill:none;
stroke:#b0225e;
stroke-width:7;
stroke-linejoin:round;
mask:url(#msk);
}
<div class="bg">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="400.000000pt" height="400.000000pt" viewBox="0 0 400.000000 400.000000"
preserveAspectRatio="xMidYMid meet">
<!-- Bottom layer dashed white line -->
<path id="dashed" d="M23.742,10.709
c-2.305,23.611-8.81,46.563-9.021,70.829c-0.252,28.966,22.237,43.666,47.06,55.482c23.642,11.255,42.368,15.766,68.461,16.631
c19.993,0.663,40.08,2.97,59.853-1.723c23.301-5.531,45.542-17.598,66.978-27.933c19.248-9.281,38.831-21.86,41.946-45.201
c5.539-41.51-54.993-47.073-81.885-42.17C159.05,47.212,89.37,104.633,77.387,164.629c-5.896,29.522-4.312,60.884,12.703,86.354
c19.17,28.697,49.512,49.927,78.596,67.591"/>
<!-- Middle layer dashed pink line -->
<path id="pink" d="M23.742,10.709
c-2.305,23.611-8.81,46.563-9.021,70.829c-0.252,28.966,22.237,43.666,47.06,55.482c23.642,11.255,42.368,15.766,68.461,16.631
c19.993,0.663,40.08,2.97,59.853-1.723c23.301-5.531,45.542-17.598,66.978-27.933c19.248-9.281,38.831-21.86,41.946-45.201
c5.539-41.51-54.993-47.073-81.885-42.17C159.05,47.212,89.37,104.633,77.387,164.629c-5.896,29.522-4.312,60.884,12.703,86.354
c19.17,28.697,49.512,49.927,78.596,67.591"/>
</svg>
</div>
3. There will be a masking path on the top layer, which will gradually reveal the pink path
.bg {background:#eee;}
#dashed {
stroke-dasharray: 14;
fill:none;
stroke:white;
stroke-width:7;
stroke-linejoin:round;
}
#pink {
stroke-dasharray: 14;
fill:none;
stroke:#b0225e;
stroke-width:7;
stroke-linejoin:round;
mask:url(#msk);
}
#maskLine {
fill:none;
stroke:white;
stroke-width:7;
stroke-dasharray: 917;
stroke-dashoffset: 917;
animation: dash 5s linear alternate forwards;
}
#keyframes dash {
from {
stroke-dashoffset: 917;
}
to {
stroke-dashoffset: 0;
}
}
<div class="bg">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="400.000000pt" height="400.000000pt" viewBox="0 0 400.000000 400.000000"
preserveAspectRatio="xMidYMid meet">
<defs>
<mask id="msk">
<!-- A mask layer that reveals a dashed pink line -->
<path id="maskLine" d="M23.742,10.709
c-2.305,23.611-8.81,46.563-9.021,70.829c-0.252,28.966,22.237,43.666,47.06,55.482c23.642,11.255,42.368,15.766,68.461,16.631
c19.993,0.663,40.08,2.97,59.853-1.723c23.301-5.531,45.542-17.598,66.978-27.933c19.248-9.281,38.831-21.86,41.946-45.201
c5.539-41.51-54.993-47.073-81.885-42.17C159.05,47.212,89.37,104.633,77.387,164.629c-5.896,29.522-4.312,60.884,12.703,86.354
c19.17,28.697,49.512,49.927,78.596,67.591"/>
</mask>
</defs>
<!-- Bottom layer dashed white line -->
<path id="dashed" d="M23.742,10.709
c-2.305,23.611-8.81,46.563-9.021,70.829c-0.252,28.966,22.237,43.666,47.06,55.482c23.642,11.255,42.368,15.766,68.461,16.631
c19.993,0.663,40.08,2.97,59.853-1.723c23.301-5.531,45.542-17.598,66.978-27.933c19.248-9.281,38.831-21.86,41.946-45.201
c5.539-41.51-54.993-47.073-81.885-42.17C159.05,47.212,89.37,104.633,77.387,164.629c-5.896,29.522-4.312,60.884,12.703,86.354
c19.17,28.697,49.512,49.927,78.596,67.591"/>
<!-- Middle layer dashed pink line -->
<path id="pink" d="M23.742,10.709
c-2.305,23.611-8.81,46.563-9.021,70.829c-0.252,28.966,22.237,43.666,47.06,55.482c23.642,11.255,42.368,15.766,68.461,16.631
c19.993,0.663,40.08,2.97,59.853-1.723c23.301-5.531,45.542-17.598,66.978-27.933c19.248-9.281,38.831-21.86,41.946-45.201
c5.539-41.51-54.993-47.073-81.885-42.17C159.05,47.212,89.37,104.633,77.387,164.629c-5.896,29.522-4.312,60.884,12.703,86.354
c19.17,28.697,49.512,49.927,78.596,67.591"/>
</svg>
</div>
UPDATE
Filling, clearing the path on repeated clicks on the button "Play"
var dash = true;
function play() {
var path = document.getElementById('maskLine');
path.style.strokeDashoffset = (dash) ? '0' : '917.00';
dash = !dash;
}
.bg {background:#eee;}
#dashed {
stroke-dasharray: 14;
fill:none;
stroke:white;
stroke-width:7;
stroke-linejoin:round;
}
#pink {
stroke-dasharray: 14;
fill:none;
stroke:#b0225e;
stroke-width:7;
stroke-linejoin:round;
mask:url(#msk);
}
#maskLine {
fill:none;
stroke:white;
stroke-width:7;
stroke-dasharray: 917;
stroke-dashoffset: 917;
transition: stroke-dashoffset 5s linear;
}
<button onclick="play();">Play</button>
<div class="bg">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="400.000000pt" height="400.000000pt" viewBox="0 0 400.000000 400.000000"
preserveAspectRatio="xMidYMid meet">
<defs>
<mask id="msk">
<!-- A mask layer that reveals a dashed pink line -->
<path id="maskLine" d="M23.742,10.709
c-2.305,23.611-8.81,46.563-9.021,70.829c-0.252,28.966,22.237,43.666,47.06,55.482c23.642,11.255,42.368,15.766,68.461,16.631
c19.993,0.663,40.08,2.97,59.853-1.723c23.301-5.531,45.542-17.598,66.978-27.933c19.248-9.281,38.831-21.86,41.946-45.201
c5.539-41.51-54.993-47.073-81.885-42.17C159.05,47.212,89.37,104.633,77.387,164.629c-5.896,29.522-4.312,60.884,12.703,86.354
c19.17,28.697,49.512,49.927,78.596,67.591">
</path>
</path>
</mask>
</defs>
<!-- Bottom layer dashed white line -->
<path id="dashed" d="M23.742,10.709
c-2.305,23.611-8.81,46.563-9.021,70.829c-0.252,28.966,22.237,43.666,47.06,55.482c23.642,11.255,42.368,15.766,68.461,16.631
c19.993,0.663,40.08,2.97,59.853-1.723c23.301-5.531,45.542-17.598,66.978-27.933c19.248-9.281,38.831-21.86,41.946-45.201
c5.539-41.51-54.993-47.073-81.885-42.17C159.05,47.212,89.37,104.633,77.387,164.629c-5.896,29.522-4.312,60.884,12.703,86.354
c19.17,28.697,49.512,49.927,78.596,67.591"/>
<!-- Middle layer dashed pink line -->
<path id="pink" d="M23.742,10.709
c-2.305,23.611-8.81,46.563-9.021,70.829c-0.252,28.966,22.237,43.666,47.06,55.482c23.642,11.255,42.368,15.766,68.461,16.631
c19.993,0.663,40.08,2.97,59.853-1.723c23.301-5.531,45.542-17.598,66.978-27.933c19.248-9.281,38.831-21.86,41.946-45.201
c5.539-41.51-54.993-47.073-81.885-42.17C159.05,47.212,89.37,104.633,77.387,164.629c-5.896,29.522-4.312,60.884,12.703,86.354
c19.17,28.697,49.512,49.927,78.596,67.591"/>
</svg>
</div>
You can do it using a mask:
.bg {
background: #eee;
}
.dashed {
stroke-dasharray: 14;
}
.path {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: dash 5s linear alternate forwards;
}
#keyframes dash {
from {
stroke-dashoffset: 1000;
}
to {
stroke-dashoffset: 0;
}
}
<div class="bg">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="400.000000pt" height="400.000000pt" viewBox="0 0 400.000000 400.000000" preserveAspectRatio="xMidYMid meet">
<defs>
<path id="dashed" class="dashed" fill="none" stroke="white" stroke-width="7" stroke-linejoin="round" stroke-dasharray="14" stroke-miterlimit="16" d="M23.742,10.709
c-2.305,23.611-8.81,46.563-9.021,70.829c-0.252,28.966,22.237,43.666,47.06,55.482c23.642,11.255,42.368,15.766,68.461,16.631
c19.993,0.663,40.08,2.97,59.853-1.723c23.301-5.531,45.542-17.598,66.978-27.933c19.248-9.281,38.831-21.86,41.946-45.201
c5.539-41.51-54.993-47.073-81.885-42.17C159.05,47.212,89.37,104.633,77.387,164.629c-5.896,29.522-4.312,60.884,12.703,86.354
c19.17,28.697,49.512,49.927,78.596,67.591"/>
<mask id="mask">
<use xlink:href="#dashed"/>
</mask>
</defs>
<path class="path" fill="none" stroke="#b0225e" stroke-linejoin="round" stroke-width="5" stroke-miterlimit="10" mask="url(#mask)" d="M23.742,10.709
c-2.305,23.611-8.81,46.563-9.021,70.829c-0.252,28.966,22.237,43.666,47.06,55.482c23.642,11.255,42.368,15.766,68.461,16.631
c19.993,0.663,40.08,2.97,59.853-1.723c23.301-5.531,45.542-17.598,66.978-27.933c19.248-9.281,38.831-21.86,41.946-45.201
c5.539-41.51-54.993-47.073-81.885-42.17C159.05,47.212,89.37,104.633,77.387,164.629c-5.896,29.522-4.312,60.884,12.703,86.354
c19.17,28.697,49.512,49.927,78.596,67.591"/>
</svg>
</div>
You can also eliminate the duplicate path declaration by reusing the path defined in defs, but this would require additional changes, so, in order to not obfuscate things, I decided to not put that in my answer.

How to make svg symbols dropshadow

I tried to append filter to a path using svg.symbols but it is not appeared.
This is my code
let namngu = d3.select("body")
.append("svg")
.attr("width", "5000")
.attr("height", "5000");
let namnguvcl = namngu.append("defs")
let filter = namnguvcl.append("filters")
.attr("id", "drop-shadow-line");
filter.append("feGaussianBlur")
.attr("in","SourceAlpha")
.attr("stdDeviation",4)
let namnhuconbo = filter.append("feMerge")
namnhuconbo.append("feMergeNode")
namnhuconbo.append("feMergeNode")
.attr("in","SourceGraphic")
let namngunhucho = namnguvcl.append("symbol")
.attr("id","may")
.attr("preserveAspectRatio", "none")
.attr("viewBox", "0 0 17275 8599")
namngunhucho.append("svg:path")
.attr("d", "M 5097.99967 2512.5 q 67 -30 134 0")
.attr("style", "fill: transparent; stroke: blue")
namngu.append("use")
.style("filter", "url(#drop-shadow-line)")
.attr("xlink:href","#may")
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
https://jsfiddle.net/pn9ta08w/
This is what I am trying to replicate https://jsfiddle.net/fonwrspj/3/
How can I fix this, Thanks
Errors in the resulting SVG code:
In your code you have <filters> instead of <filter>
The symbol has a viewBox but the use element has no width and height
The svg element is huge and has no viewBox attribute
The viewBox attribute of the symbol is enormous compares with the bounding box of the shape.
After correcting all this the filter gets applied.
svg{border:solid}
<svg viewBox="0 0 150 75">
<defs>
<filter id="drop-shadow-line" y="-1" height="20">
<feGaussianBlur in="SourceAlpha" stdDeviation="4"></feGaussianBlur>
<feMerge>
<feMergeNode></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<symbol id="may" preserveAspectRatio="none" viewBox="5090 2490 150 50">
<path d="M 5097.99967 2512.5 q 67 -30 134 0" style="fill:transparent; stroke: blue"></path>
</symbol>
</defs>
<use xlink:href="#may" width="150" height="150" style="filter: url('#drop-shadow-line')" />
</svg>

How to implement a continuous motion illusion in SVG?

Imagine a progress-bar like this one, which creates a sensation of motion towards the left:
Note: the thin bright-green line on top of the animation is a compression artifact.
I am looking for a way to implement something similar, but in an arbitrary SVG path, such as this one:
I am trying to understand what is really going on there, e.g.:
Is it a gradient with many stops, and the stops keep moving?
Are these many adjacent, skewed rectangles moving in unison?
Is it one long sequence of skewed adjacent rectangles, with a "sliding window" moving along it?
How can such animations be conceptualized? And what would be the best practice to implement it using SVG primitives?
I am using a path twice:#a and #b. Both #a and #b have stroke-dasharray: 1 but #b id offset stroke-dashoffset: 1;
I'm animating the stroke-dashoffset for both #a and #b.
use {
stroke-dasharray: 1;
}
#a {
stroke: green;
animation: dasha 5s linear infinite;
}
#b {
stroke: DarkSeaGreen;
stroke-dashoffset: 1;
animation: dashb 5s linear infinite;
}
#keyframes dasha {
to {
stroke-dashoffset: -54.66;
}
}
#keyframes dashb {
to {
stroke-dashoffset: -53.66;
}
}
<svg viewBox='0 0 24 24' width="200"><title>gesture</title>
<defs><path id="thePath" fill="none" d='M4.59 6.89c.7-.71 1.4-1.35 1.71-1.22.5.2 0 1.03-.3 1.52-.25.42-2.86 3.89-2.86 6.31 0 1.28.48 2.34 1.34 2.98.75.56 1.74.73 2.64.46 1.07-.31 1.95-1.4 3.06-2.77 1.21-1.49 2.83-3.44 4.08-3.44 1.63 0 1.65 1.01 1.76 1.79-3.78.64-5.38 3.67-5.38 5.37 0 1.7 1.44 3.09 3.21 3.09 1.63 0 4.29-1.33 4.69-6.1h2.46'></path>
</defs>
<use id="a" xlink:href="#thePath" />
<use id="b" xlink:href="#thePath" />
</svg>
UPDATE
If you use css variables you can use only one animation:
use {
stroke-dasharray: 1;
}
#a {
--offset:0;
stroke: green;
stroke-dashoffset: 53.66;
animation: dash 5s linear infinite;
}
#b {
--offset:1;
stroke: DarkSeaGreen;
stroke-dashoffset: 54.66;
animation: dash 5s linear infinite;
}
#keyframes dash {
to {
stroke-dashoffset: var(--offset)
}
}
<svg viewBox='0 0 24 24' width="200"><title>gesture</title>
<defs><path id="thePath" fill="none" d='M4.59 6.89c.7-.71 1.4-1.35 1.71-1.22.5.2 0 1.03-.3 1.52-.25.42-2.86 3.89-2.86 6.31 0 1.28.48 2.34 1.34 2.98.75.56 1.74.73 2.64.46 1.07-.31 1.95-1.4 3.06-2.77 1.21-1.49 2.83-3.44 4.08-3.44 1.63 0 1.65 1.01 1.76 1.79-3.78.64-5.38 3.67-5.38 5.37 0 1.7 1.44 3.09 3.21 3.09 1.63 0 4.29-1.33 4.69-6.1h2.46'></path>
</defs>
<use id="a" xlink:href="#thePath" />
<use id="b" xlink:href="#thePath" />
</svg>
Well one way of doing it is with an animated pattern. Something like this:
<svg width="800px" height="600px">
<defs>
<pattern id="skewrect" x="0%" y="0%" width="20%" height="100%" patternTransform="skewX(30)" viewBox="-7 160 60 60">
<animate attributeName="x" from="20%" to="0%" dur="2s" repeatCount="indefinite"/>
<polygon points="0,0 0,600 20,600 20,0" fill="green"/>
<polygon points="20,40 20,600 40,600 40,20" fill="grey"/>
</pattern>
</defs>
<path d="M10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80" stroke="url(#skewrect)" fill="none" stroke-width="10"/>
</svg>
You can also do it with a gradient, or a filter.

SVG shows not correctly in Firefox browser

I have next svg object which shown well in Chrome and AI, but it shows not correctly in Firefox. In my svg i have 2 textPath tags, i try to set italic font-style for both of them however only 1 textPath effected. How can i fix it if i still want 2 textPath elements be in 1 svg object?
<svg width="229" height="95" viewBox="0 0 98.59523272091116 43.1456805813697" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="none"><g id="0.13161635632674862"><text fill="#FF0000" stroke="#FFFF00" data-stroke="FFFF00" stroke-width="0px" stroke-linecap="round" stroke-linejoin="round" x="" y="2.1527343388845233" text-anchor="start" font-size="24px" font-family="Carter One" data-textcurve="1" data-itemzoom="1 1" data-textspacing="0" style="font-style: italic; font-weight: normal; text-decoration: none;font-family: 'Carter One';" itemzoom="0.4305468677769046 0.45416505875126006"><textPath xlink:href="#textPath-item-4" style="stroke: rgb(255, 255, 0); stroke-width: 3.31429px;" data-stroke="rgb(255, 255, 0)"><tspan dy="0">Create</tspan></textPath><textPath xlink:href="#textPath-item-4"><tspan dy="0" style="stroke-width: 0px;">Create</tspan></textPath></text></g><defs><path id="textPath-item-4" d="M 4.5 28.240596987479876 A 5443.099053742821 5443.099053742821 0 0 1 99.4987942273035 28.240596987479876"></path><style>#font-face {
font-family: 'Carter One';
src: local('Carter One'), local('CarterOne'), url(http://fonts.gstatic.com/s/carterone/v9/VjW2qt1pkqVtO22ObxgEBfk_vArhqVIZ0nv9q090hN8.woff2) format('woff2');
}</style></defs></svg>
The problem seems to be that the font you selected, Carter One, has no italics variant. So the browser needs to use a fallback (which, as far as I know, amounts to skewing the glyphs). Firefox seems to fail with the simultanuous task of "inventing" an italics font, painting a stroke and writing it on a text path.
It is not a problem to use multiple textPath elements inside one text. Only when using both a non-zero stroke-width and font-style:italics a font with a known italics style needs to be referenced. For example Lato has one:
<svg width="229" height="95" viewBox="0 0 98.59523272091116 43.1456805813697" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="none">
<g id="0.13161635632674862">
<text fill="#FF0000" stroke="#FFFF00" data-stroke="FFFF00" stroke-width="0px" stroke-linecap="round" stroke-linejoin="round" x="" y="2.1527343388845233" text-anchor="start" font-size="24px" font-family="Carter One" data-textcurve="1" data-itemzoom="1 1" data-textspacing="0" style="font-style: italic; font-weight: normal; text-decoration: none;font-family: 'Ubuntu';" itemzoom="0.4305468677769046 0.45416505875126006">
<textPath xlink:href="#textPath-item-4" style="stroke: rgb(255, 255, 0); stroke-width: 3.31429px;" data-stroke="rgb(255, 255, 0)"><tspan dy="0">Create</tspan></textPath>
<textPath xlink:href="#textPath-item-4"><tspan dy="0" style="stroke-width: 0px;">Create</tspan></textPath>
</text>
</g>
<defs>
<path id="textPath-item-4" d="M 4.5 28.240596987479876 A 5443.099053742821 5443.099053742821 0 0 1 99.4987942273035 28.240596987479876"></path>
<style>
#font-face {
font-family: 'Ubuntu';
font-style: italic;
font-weight: 700;
src: local('Ubuntu Bold Italic'), local('Ubuntu-BoldItalic'), url(https://fonts.gstatic.com/s/ubuntu/v11/4iCp6KVjbNBYlgoKejZPslyPN4E.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
</style>
</defs>
</svg>
As an aside, while I don't know exactly which browsers support it, at least most modern browsers seem to know the paint-order CSS property, which removes the need to paint the text twice. paint-order:stroke paints the stroke below the fill:
<svg width="229" height="95" viewBox="0 0 98.59523272091116 43.1456805813697" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="none">
<g id="0.13161635632674862">
<text fill="#FF0000" stroke="#FFFF00" data-stroke="FFFF00" stroke-width="0px" stroke-linecap="round" stroke-linejoin="round" x="" y="2.1527343388845233" text-anchor="start" font-size="24px" font-family="Carter One" data-textcurve="1" data-itemzoom="1 1" data-textspacing="0" style="font-style: italic; font-weight: normal; text-decoration: none;font-family: 'Ubuntu';" itemzoom="0.4305468677769046 0.45416505875126006">
<textPath xlink:href="#textPath-item-4" style="stroke: rgb(255, 255, 0); stroke-width: 3.31429px;paint-order:stroke" data-stroke="rgb(255, 255, 0)"><tspan dy="0">Create</tspan></textPath>
</g>
<defs>
<path id="textPath-item-4" d="M 4.5 28.240596987479876 A 5443.099053742821 5443.099053742821 0 0 1 99.4987942273035 28.240596987479876"></path>
<style>
#font-face {
font-family: 'Ubuntu';
font-style: italic;
font-weight: 700;
src: local('Ubuntu Bold Italic'), local('Ubuntu-BoldItalic'), url(https://fonts.gstatic.com/s/ubuntu/v11/4iCp6KVjbNBYlgoKejZPslyPN4E.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
</style>
</defs>
</svg>

SVG animation in Processing not drawing at correct location

I'm trying to make a simple, quick and dirty demo interface in Processing, with the widget animations being made up of SVG frames that I loop through. I'm just learning and I don't really have any experience in this area, so bear with me.
The problem is that the x,y coordinates I have entered are not where the SVG is drawing.
Here is a screenshot comparing expected draw location vs actual draw location
I've checked my original SVGs (created in Adobe Animate CC and exported frame by frame), I played with shapeMode(), I tried to translate with path coordinates from the viewBox (it didn't change anything), and I've spent way too much time searching online. Here is the code:
ArrayList<Button> buttons;
//creating background grid(50x50)
int nbOfHorizontalLines = 20;
int nbOfVerticalLines = 30;
void setup() {
size(1500, 1000);
buttons = new ArrayList<Button>();
// button constructor: x, y, width, height, text
buttons.add(new Button (100, 100, 100, 100, "100,100 (top left corner of button should be drawn here)"));
}
void draw() {
background(200);
frameRate(20);
float distanceBetweenHorizontalLines = (float)height/nbOfHorizontalLines;
float distanceBetweenVerticalLines = (float)width/nbOfVerticalLines;
for(int i = 0; i < nbOfHorizontalLines; i++)
{
stroke(#56A9FA);
line(0, i *distanceBetweenHorizontalLines, width, i*distanceBetweenHorizontalLines);
}
for(int i = 0; i < nbOfVerticalLines; i++)
{
stroke(#56A9FA);
line(i*distanceBetweenVerticalLines, 0, i*distanceBetweenVerticalLines, height);
}
// draw buttons
for (Button b : buttons) {
b.drawButton();
}
textSize(15);
text("~210,140", 210, 128);
}
Here is the class for the Button (edited for the sake of this post so that it only draws one frame, instead of looping through all my SVG files) :
class Button {
float x, y;
//width and height
float w, h;
//cycles through the animation files
ArrayList<PShape> restFrames;
//var to load current frame
PShape frame;
int frameIndex;
//assign text string from parameter to t
String t;
public Button(float newX, float newY, float newWidth, float newHeight, String newText) {
x = newX;
y = newY;
w = newWidth ;
h = newHeight;
frameIndex = 0;
t = newText;
restFrames = new ArrayList<PShape>();
restFrames.add(loadShape("cleanBreathingNoBG_1.svg"));
}
void drawButton() {
shapeMode(CORNER);
PShape frame = restFrames.get(frameIndex);
shape(frame, x, y, w, h);
textSize(18);
fill(0);
textAlign(LEFT, TOP);
text(t, x, y);
}
}
And finally, the code for the SVG itself:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="none" x="0px" y="0px" width="386px" height="363px" viewBox="0 0 386 363">
<defs>
<path id="Layer0_0_1_STROKES" stroke="#000000" stroke-width="5.2" stroke-linejoin="round" stroke-linecap="round" fill="none" d="
M 454.95 160.95
Q 478.0671875 309.616015625 464.95 476.9"/>
<path id="Layer0_1_1_STROKES" stroke="#000000" stroke-width="5.2" stroke-linejoin="round" stroke-linecap="round" fill="none" d="
M 464.95 476.9
Q 607.547265625 499.9689453125 769.9 479.9"/>
<path id="Layer0_2_1_STROKES" stroke="#000000" stroke-width="5.2" stroke-linejoin="round" stroke-linecap="round" fill="none" d="
M 770.9 481.9
Q 761.9529296875 312.828515625 769.9 158.95"/>
<path id="Layer0_3_1_STROKES" stroke="#000000" stroke-width="5.2" stroke-linejoin="round" stroke-linecap="round" fill="none" d="
M 454.95 157.95
Q 614.925 174.6916015625 769.9 156.95"/>
<path id="Layer0_4_1_STROKES" stroke="#000000" stroke-width="5.2" stroke-linejoin="round" stroke-linecap="round" fill="none" d="
M 463.95 475.9
L 442.95 502.9"/>
<path id="Layer0_5_1_STROKES" stroke="#000000" stroke-width="5.2" stroke-linejoin="round" stroke-linecap="round" fill="none" d="
M 769.9 478.9
L 783.9 503.9"/>
<path id="Layer0_6_1_STROKES" stroke="#000000" stroke-width="5.2" stroke-linejoin="round" stroke-linecap="round" fill="none" d="
M 771.9 158.95
L 782.9 183.95"/>
<path id="Layer0_7_1_STROKES" stroke="#000000" stroke-width="5.2" stroke-linejoin="round" stroke-linecap="round" fill="none" d="
M 785.9 505.9
Q 781.89296875 341.61796875 784.9 187.95"/>
<path id="Layer0_8_1_STROKES" stroke="#000000" stroke-width="5.2" stroke-linejoin="round" stroke-linecap="round" fill="none" d="
M 446.95 504.9
Q 616.425 506.6560546875 780.9 505.9"/>
<path id="Layer0_9_1_STROKES" stroke="#000000" stroke-width="5.2" stroke-linejoin="round" stroke-linecap="round" fill="none" d="
M 452.95 159.95
L 439.95 182.95"/>
<path id="Layer0_10_1_STROKES" stroke="#000000" stroke-width="5.2" stroke-linejoin="round" stroke-linecap="round" fill="none" d="
M 442.95 501.9
Q 449.8580078125 337.425 439.95 186.95"/>
</defs>
<g transform="matrix( 1, 0, 0, 1, -421,-144.75) ">
<use xlink:href="#Layer0_0_1_STROKES"/>
</g>
<g transform="matrix( 1, 0, 0, 1, -421,-144.75) ">
<use xlink:href="#Layer0_1_1_STROKES"/>
</g>
<g transform="matrix( 1, 0, 0, 1, -421,-144.75) ">
<use xlink:href="#Layer0_2_1_STROKES"/>
</g>
<g transform="matrix( 1, 0, 0, 1, -421,-144.75) ">
<use xlink:href="#Layer0_3_1_STROKES"/>
</g>
<g transform="matrix( 1, 0, 0, 1, -421,-144.75) ">
<use xlink:href="#Layer0_4_1_STROKES"/>
</g>
<g transform="matrix( 1, 0, 0, 1, -421,-144.75) ">
<use xlink:href="#Layer0_5_1_STROKES"/>
</g>
<g transform="matrix( 1, 0, 0, 1, -421,-144.75) ">
<use xlink:href="#Layer0_6_1_STROKES"/>
</g>
<g transform="matrix( 1, 0, 0, 1, -421.7,-148.75) ">
<use xlink:href="#Layer0_7_1_STROKES"/>
</g>
<g transform="matrix( 1, 0, 0, 1, -421,-144.75) ">
<use xlink:href="#Layer0_8_1_STROKES"/>
</g>
<g transform="matrix( 1, 0, 0, 1, -421,-144.75) ">
<use xlink:href="#Layer0_9_1_STROKES"/>
</g>
<g transform="matrix( 1, 0, 0, 1, -421,-144.75) ">
<use xlink:href="#Layer0_10_1_STROKES"/>
</g>
</svg>
If additional information is needed, please let me know if I've missed anything! I appreciate the help, as I'm a bit of a beginner and no one around me knows much about this either.
The offset is coming from inside your svg file.
I tested this by loading your svg file alongside another svg file (which I got from going to File > Examples > Basics > Shape > LoadDisplaySVG and then going to Sketch > Show Sketch Folder and then copying bot1.svg from the example's data directory into my data directory. Then I run this code:
PShape yourShape;
PShape myShape;
void setup(){
size(500, 500);
yourShape = loadShape("image.svg");
myShape = loadShape("bot1.svg");
}
void draw(){
background(200);
ellipse(mouseX, mouseY, 10, 10);
shape(yourShape, mouseX, mouseY, 100, 100);
shape(myShape, mouseX, mouseY, 100, 100);
}
The image.svg file is your svg file, and this is what I see:
This shows that "my" svg file draws with mouseX, mouseY as its upper-left corner, but yours has the offset.
Now, I'm not sure why your svg contains the offset, but this at least shows you that it's not the Processing code, and provides you with a simpler example that you can play with. If I were you I would go through your svg file with a fine-toothed comb. The offset might be obvious to somebody else, but I'm not very familiar with svg files. Good luck.

Resources