例如,我有一个复杂的svg路径,其中我知道某个Y坐标,我需要找到整个路径到这个坐标的长度。
我能想到的最有效的方法是使用二分搜索来找到给定Y坐标的直线的线段。
function binarySearchLengthByYCoord(path, yCoord) {
const pathTotalLength = +path.getTotalLength();
let low = 0;
let high = pathTotalLength;
let result = 0;
while (low <= high) {
const mid = Math.floor((low + high) / 2);
const point = path.getPointAtLength(mid);
if (point.y < yCoord) {
low = mid + 1;
result = mid;
} else {
high = mid - 1;
}
}
return pathTotalLength - result;
}
字符串
这段代码工作正常,但每次触发滚动事件时都必须调用它,因此在弱设备和手机上,有明显的性能问题。我需要获得与我已经拥有的here类似的效果,并使用更高效的代码。
scrollLineAnimation();
function scrollLineAnimation() {
const decorParent = document.querySelector("[data-line-animation]"),
lines = decorParent.querySelectorAll("svg");
if (lines.length) {
const line = lines[0],
path = line.getElementsByTagName("path")[0],
pathTotalLength = +path.getTotalLength(),
lastYCoord = path.getPointAtLength(pathTotalLength).y,
offsetHeight = decorParent.dataset.lineAnimation
? decorParent.dataset.lineAnimation
: 0.8;
line.style.strokeDasharray = pathTotalLength;
line.style.strokeDashoffset = pathTotalLength;
line.style.display = "block";
let position = 0;
let prcPostion = 0;
updateStrokeDashoffset();
window.addEventListener("scroll", updateStrokeDashoffset);
window.addEventListener("resize", updateStrokeDashoffset);
// Function for getting the Y coordinate and finding the line segment
function updateStrokeDashoffset() {
const scrollPosition = window.scrollY,
windowHeight = window.innerHeight,
svgHeight = line.getBoundingClientRect().height,
yCoord =
(scrollPosition + windowHeight * offsetHeight) *
(lastYCoord / svgHeight),
newStrokeDashoffsetValue = binarySearchLengthByYCoord(path, yCoord);
prcPostion = (newStrokeDashoffsetValue / pathTotalLength) * 100;
}
// Function for smooth animation
function setStrokeDashoffset() {
let dist = prcPostion - position;
position = position + (dist * 70) / 1000;
line.style.strokeDashoffset = Math.round(
(position * pathTotalLength) / 100
);
requestAnimationFrame(setStrokeDashoffset);
}
requestAnimationFrame(setStrokeDashoffset);
}
function binarySearchLengthByYCoord(path, yCoord) {
const pathTotalLength = +path.getTotalLength();
let low = 0;
let high = pathTotalLength;
let result = 0;
while (low <= high) {
const mid = Math.floor((low + high) / 2);
const point = path.getPointAtLength(mid);
if (point.y < yCoord) {
low = mid + 1;
result = mid;
} else {
high = mid - 1;
}
}
return pathTotalLength - result;
}
}
body {
max-width: 100%;
position: relative;
padding: 0;
margin: 0;
min-height: 6000px;
}
.decor {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
max-width: 1440px;
}
.decor svg {
position: absolute;
display: none;
left: 0;
top: 0;
width: 100%;
height: auto;
min-height: 100%;
}
<div data-line-animation="0.8" class="decor">
<svg preserveAspectRatio="none" width="1440" height="5141" viewBox="0 0 1440 5141" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M886.805 0.0743205C986.888 -1.4439 1187.91 19.063 1065.22 176.635C911.848 373.6 1455.21 527.592 1367.57 373.601C1279.93 219.61 613.874 395.088 1152.86 656.514C1584.04 865.655 736.569 920.328 258.934 921.522C91.767 929.283 -164.804 1007.13 146.247 1256.43C535.061 1568.06 1238.55 1700.59 1195.7 1890.43C1152.86 2080.27 -384.754 2905.95 146.247 2905.95C1314.52 2905.95 250.102 3383.41 709.046 3545.41C849.007 3594.81 1440.94 3511.17 1410.12 3824.66C1352.7 4408.75 -60.5479 4131.44 95.6015 4451.98C290.788 4852.65 1410.12 5002.4 1440 5140" stroke="#2BB32A" stroke-width="1.5" stroke-linecap="square" />
</svg>
</div>
你能告诉我一个更有效的方法来找到一个给定的Y坐标的svg路径的长度吗?
1条答案
按热度按时间slwdgvem1#
getPointAtLength()
非常昂贵,因此您应该尝试减少采样点计算量。一种方法是先创建一个查找对象,稍后可以使用它将y位置转换为路径长度。
在上面的例子中,我们在总路径长度的每一个百分比处检索点,并保存相对于路径高度的y值。
这个过程不会返回一个连续的相对y值列表(例如0-100)。
当一个范围丢失时,我们根据开始和结束路径长度插值这些值。
显然这只会返回近似值。特别是有自相交环的路径段不能精确计算,因为我们在y位置有多个长度。
100d 1xx 1c 1d 1x的字符串
但是我们可以通过增加样本点的数量来提高精度,例如增加到200。
用于测试see codepen
示例2:滚动动画
x
的一个字符串