问题
I am trying to do some growing line animation with SVG and CSS animation. Since the lines have different length respectively, I use pathLength
to assign a virtual length for them. Thus i can use only one @keyframe
for all of them.
Here is the sample code
<svg width="1000px" height="100px">
<g stroke="#FAB" stroke-width="3">
<line id="Line1" x1="20", y1="20", x2="520", y2="20" pathLength="1000"/>
<line id="Line2" x1="20", y1="50", x2="780", y2="50" pathLength="1000"/>
</g>
</svg>
<style>
line {
animation-name: line-grow;
animation-duration: 3s;
animation-iteration-count: infinite;
}
@keyframes line-grow {
from {
stroke-dasharray: 0, 1000;
}
to {
stroke-dasharray: 1000, 1000;
}
}
</style>
This trick works on Chrome and Firefox, but not in Safari. Is there any other trick that can work on all these browsers? Or is there some way that i can apply this trick on Safari?
I console.log
the pathLength
with JS which do return something in Safari.
回答1:
Solution #1:
Finally, I found a solution in css only using a css variable. The only drawback is that one has feed this css variable with the actual path length of each stroke (this can be computed only once when creating the stroke using any kind of script language). In the svg, on replaces pathLength="1000"
by style="--L:nnn"
where --L
is a css variable and nnn is the actual length of the path:
<svg width="1000px" height="100px">
<g stroke="#FAB" stroke-width="3">
<line style="--L:500;" id="Line1" x1="20" y1="20" x2="520" y2="20"/>
<line style="--L:760;" id="Line2" x1="20" y1="50" x2="780" y2="50"/>
</g>
</svg>
In the css, on uses --L
variable in the @keyframes
code:
line {
animation-name: line-grow;
animation-duration: 3s;
animation-iteration-count: infinite;
}
@keyframes line-grow {
from {
stroke-dasharray: 0 var(--L);
}
to {
stroke-dasharray: var(--L) var(--L);
}
}
Solution #2:
If one cannot or does not want to pre-compute the path length of each stroke, below is a solution with a short javascript code doing the job on-the-fly.
The css:
@keyframes line-grow {
from {
stroke-dasharray: 0 var(--L);
}
to {
stroke-dasharray: var(--L) var(--L);
}
}
The svg:
<svg width="1000px" height="100px">
<g stroke="#FAB" stroke-width="3">
<line id="Line1" x1="20" y1="20" x2="520" y2="20"/>
<line id="Line2" x1="20" y1="50" x2="780" y2="50"/>
</g>
</svg>
The javascript:
function magic()
{
var list, k, style;
list = document.querySelectorAll("line");
for (k=0; k<list.length; k++)
{
style = "--L:" + list[k].getTotalLength() + ";";
style += "animation: line-grow 3s infinite;";
list[k].setAttribute("style", style);
}
}
window.addEventListener("load", magic, false);
Solution #3:
For the record, below is a former solution similar to the Cigany solution, but using getTotalLength()
method to compute the line length (as a result, one can compute the length of any kind of path easily including those that contain Bézier curves).
Just add a script (javascript) to the end of the page that does for each line:
- compute its length using
getTotalLength()
- add a keyFrames using this length
- change its animation name accordingly
- modify its
pathLength
attribute (to keep browsers that know how to use this attribute working)
The code could be:
function setKeyframes(name, len)
{
// add a specific keyframes for each line using its length
// to the style of the page
var a, e = document.createElement("style");
a = "@keyframes " + name + " {";
a += "from {stroke-dasharray: 0, " + len + ";}";
a += "to {stroke-dasharray: " + len + ", " + len + ";}";
a += "}";
e.type = 'text/css';
if (e.styleSheet) e.styleSheet.cssText = a;
else e.appendChild(document.createTextNode(a));
document.getElementsByTagName('head')[0].appendChild(e);
}
function magic()
{
// does the job for each line
var list, k, name, len;
// adapt the following instruction
// if all the lines of the document are not concerned
list = document.querySelectorAll("line");
for (k=0; k<list.length; k++)
{
name = "line-grow-" + k;
len = list[k].getTotalLength();
setKeyframes(name, len);
list[k].style.animationName = name;
list[k].setAttribute("pathLength", len);
}
}
window.addEventListener("load", magic, false);
回答2:
I temporary use a workaround. It is not a beautiful way, but i think at least it's still a clean way. This is a reference for those who face the same problem.
I use JavaScript to dynamically calculate each length of the lines, generate some CSS msg, then insert to window.stylesheet
. Note that these should be done after window.onload
.
Here is the sample code:
window.onload = setPathAnimationAll;
var seqAnimate = ["l-vertical", "l-horizen", "l-hypo"];
function setPathAnimationAll() {
for(var i = 0; i < seqAnimate.length; ++i) {
setPathAnimationSingle(seqAnimate[i]);
}
}
function setPathAnimationSingle(id){
var target = document.getElementById(id);
var nameAnimete = genAnimateName(id);
var msgCss = genPathAnimateMsg(nameAnimete, getSvgLineLength(target));
insertCss(msgCss);
target.style.animationName = nameAnimete;
}
function getSvgLineLength(ele)
{
return Math.sqrt(Math.pow(ele.x1.animVal.value - ele.x2.animVal.value, 2) + Math.pow(ele.y1.animVal.value - ele.y2.animVal.value, 2));
}
function genAnimateName(id) {
return "dynamic-animation-" + id;
}
function genPathAnimateMsg(name, length){
return "@keyframes " + name +" { from { stroke-dasharray: 0, " + length + "; } to { stroke-dasharray: " + length + "; }}" + "\n";
}
function insertCss(msg) {
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '';
document.getElementsByTagName('head')[0].appendChild(style);
window.stylesheet = document.styleSheets[document.styleSheets.length - 1];
window.stylesheet.insertRule(msg, window.stylesheet.cssRules.length);
}
svg line {
//animation-fill-mode: forwards;
animation-iteration-count: infinite;
animation-duration: 1s;
}
<svg width="400px" height="200px" viewBox="0 0 400 200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g stroke="#FABE00" stroke-width="3">
<line x1="301" y1="0" x2="301" y2="183" id="l-vertical"></line>
<line x1="301" y1="183" x2="120" y2="183" id="l-horizen"></line>
<line x1="120" y1="183" x2="301" y2="0" id="l-hypo"></line>
</g>
</svg>
来源:https://stackoverflow.com/questions/51889547/svg-pathlength-dont-work-on-safari