您可以将曲线拆分为两个贝塞尔段,并为中间顶点提供一个marker-mid
。有一个库可以帮助您完成拆分操作,但其实现的数学原理是最简单的德卡斯特里奥算法应用。该算法通过在常数0 ≤ t ≤ 1
下使用中间插值点作为拆分路径的控制点。
const interpolate = (p1, p2, t) => {
return {
x: p1.x * (1 - t) + p2.x * t,
y: p1.y * (1 - t) + p2.y * t,
}
}
const ser = p => `${p.x},${p.y}`;
function split(start, control, end, t) {
const c1 = interpolate(start, control, t);
const c2 = interpolate(control, end, t);
const p = interpolate(c1, c2, t);
return `M ${ser(start)} Q ${ser(c1)} ${ser(p)} ${ser(c2)} ${ser(end)}`;
}
const dots = {
start: { x: 20, y: 80 },
control: { x: 160, y: 80 },
end: { x: 180, y: 20 }
};
for (const id of ['start', 'control', 'end']) {
const dot = document.querySelector(`#${id}`);
dot.setAttribute('cx', dots[id].x);
dot.setAttribute('cy', dots[id].y);
}
document.querySelector('#visual')
.setAttribute('d', `M ${ser(dots.start)} L ${ser(dots.control)} ${ser(dots.end)}`);
const d = split(dots.start, dots.control, dots.end, 0.7);
document.querySelector('#edge').setAttribute('d', d);
svg {
width: 100vw;
height: 100vh;
stroke-width: 2px;
fill: none;
}
#visual {
stroke: grey;
stroke-dasharray: 5px;
}
#start, #end {
stroke: darkgreen;
}
#control {
fill: blue;
}
#edge {
stroke: red;
marker-mid: url(#triangular);
}
<svg viewBox="0 0 200 100">
<marker
id='triangular'
orient="auto"
refX='5'
refY='8'
markerWidth="15"
markerHeight="16"
markerUnits="userSpaceOnUse"
>
<path d='M0,0 V16 L12,8 Z' fill="black" />
</marker>
<path id="visual"/>
<circle r="6" id="start"/>
<circle r="6" id="control"/>
<circle r="6" id="end"/>
<path id="edge"/>
</svg>