如何为无缝动画计算精确的 stroke-dasharray 值

How to calculate exact stroke-dasharray values for seamless animation

我正在尝试 animate svg 元素的 stroke-dashoffset 属性,如下所示

const svg = document.querySelector("svg");

//const svgns = "http://www.w3.org/2000/svg"

var ln = document.querySelector('.ln');
var path = ln.getTotalLength();

ln.style.setProperty("--off", path + 'px');
svg.appendChild(ln);
.ln {
    stroke-dashoffset: var(--off);
    stroke-dasharray: 13;
    animation: effect 4s linear infinite;
}

@keyframes effect {
    100% {
        stroke-dashoffset: 0px;
    }
}
<!DOCTYPE html>
<html>

<body>
    <link rel="stylesheet" href="style.css"></link>
    <svg id="layer" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
  
  <rect class="boundary" x="100" y="100" height="100" width="130" fill="none" stroke="blue"></rect>
  <line class="ln" x1="100" y1="150" x2="230" y2="150"  stroke = "grey" stroke-width="2" />
  <script href="index.js"></script>
</svg>

</body>

</html>

此动画的一个重要部分是 stroke-dasharray 值。

我意识到根据该字段的值,动画可以感觉无缝或不无缝。

例如,使用 5 and 13 这样的值,动画会感觉无缝。对于任何其他值,动画会跳动。

另外,我发现5和13分别产生

破折号正好从左边开始,没有任何残差 stroke-dasharray =6, 10 确实

我如何计算 exact and safest stroke-dasharray 不同 path 长度的值,这将使线条从恰好从左边开始没有残留会产生无缝感觉而不是通过无休止的反复试验找到它?

我想通了。

假设 path length=120pxstroke-dasharray=10px.

stroke-dasharray=10 创建一个 10px wide dash + 10px wide gap 这样的

如果total path length divided by each (dash+gap)(本例中为20px)returns 0;这意味着所有元素(120 px/each stoke-dasharray of 10px *2[dash+gap]) = 6 sets (6 dash + 6 gaps) 可以完美地适应。如果除法 returns剩下的 dash array 值将不会导致元素的完美匹配。

有了这些知识,逻辑就可以传给js来拉串了

const svg = document.querySelector("svg");

const svgns = "http://www.w3.org/2000/svg";

var x = 100;
var y = 100;
var height = 100;
var width = 120;
var midHeight = height / 2;


let boundary = document.createElementNS(svgns, 'rect');
boundary.setAttribute("x", x);
boundary.setAttribute("y", y);
boundary.setAttribute("height", height);
boundary.setAttribute("width", width);
boundary.setAttribute("fill", "none");
boundary.setAttribute("stroke", "blue");
svg.appendChild(boundary);

let line = document.createElementNS(svgns, 'line');
line.setAttribute("class", "ln")
line.setAttribute("x1", x);
line.setAttribute("x2", x + width);
line.setAttribute("y1", x + midHeight);
line.setAttribute("y2", x + midHeight);
line.setAttribute("stroke", "grey");
line.setAttribute("stroke-width", "2");
svg.appendChild(line);



var ln = document.querySelector('.ln');
var path = ln.getTotalLength();

var halfPath = path / 2;

var possibleNumbers = [];

for (var i = 0; i < halfPath; i++) {
    (halfPath % i) == 0 && (i != 1) ? possibleNumbers.push(i) : 0
};

ln.style.setProperty("--off", path + 'px');
ln.style.setProperty("--arr", possibleNumbers[5] + 'px');
svg.appendChild(ln);
.ln {
    stroke-dashoffset: var(--off);
    stroke-dasharray: var(--arr);
    animation: effect 1s linear infinite;
}

@keyframes effect {
    100% {
        stroke-dashoffset: 0px;
    }
}
<!DOCTYPE html>
<html>

<body>
    <link rel="stylesheet" href="style.css"></link>
    <svg id="layer" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
  
   <script href="index.js"></script>
</svg>

</body>

</html>