Как я могу заставить элемент SVG двигаться по траектории?
Я хочу переместить прямоугольник по траектории. Поэтому я переместил <rect> так, чтобы его позиция была в начале траектории. Но когда я запускаю анимацию прямоугольника, перемещение интерпретируется относительно path. Таким образом, прямоугольник перемещается со смещением относительно траектории движения.
Вот пример:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="600px"
height="400px">
<defs />
<path id="path" d="M 100 100 L 100 15 L 500 15 L 500 150" fill="none" stroke="#000000" stroke-width="3"
stroke-miterlimit="10" pointer-events="none" />
<g transform="translate(50,75)">
<rect height="50" width="100" fill="#FDD34D" rx="10" stroke-width="1px" stroke="#000000">
</rect>
<text x="15" y="28">Click me !</text>
<animateMotion id="move" dur="4.6s" begin="click">
<mpath xlink:href="#path" />
</animateMotion>
</g>
</svg>
Свободный перевод вопроса How can I make a SVG element to move on the path? от участника @René Link.
Ответы (2 шт):
Основная идея состоит в том, чтобы текст и прямоугольник были центрированы вокруг точки x = 0, y = 0.
<svg viewBox="-100 -130 700 400">
<defs />
<path id="path" d="M 0 0 L 0 -90 L 500 -90 L 500 150" fill="none" stroke="#ff0000" stroke-width="3"
stroke-miterlimit="10" pointer-events="none" />
<g>
<rect height="50" width="100" x="-50" y="-25" fill="#FDD34D" rx="10" stroke-width="1px" stroke="#000000"/>
<text text-anchor="middle" dominant-baseline="middle">Click me !</text>
<animateMotion id="move" dur="4.6s" begin="click">
<mpath xlink:href="#path" />
</animateMotion>
</g>
</svg>
С помощью JS:
- находится центр прямоугольника; (атрибуты x,y могут быть любыми)
- выставляется позиция текста относительно прямоугольника; (изначальная позиция почти любая)
- размещается
<g>в начальную позицию пути.
Начальная позиция пути, а также 1000 точек, распределенных по пути, находятся с помощью функции path.getPointAtLength(distance). Одинаковые отрезки находятся с помощью деления path.getTotalLength().
Группа (<g>) перемещается по точкам благодаря изменению transform:translate() каждые timeInterval.
const svg = document.querySelector('svg');
const path = svg.querySelector('path');
const group = svg.querySelector('g');
const rect = svg.querySelector('rect');
const text = svg.querySelector('text');
const box = rect.getBBox();
const gcx = box.x + box.width/2;
const gcy = box.y + box.height/2;
const textBox = text.getBBox();
text.setAttribute('x', box.x + (box.width - textBox.width)/2);
text.setAttribute('y', box.y + (box.height + textBox.height*0.5)/2);
const startPoint = path.getPointAtLength(0);
group.setAttribute('transform', `translate(${startPoint.x - gcx}, ${startPoint.y - gcy})`);
group.onclick = startGroup;
function startGroup(){
const steps = 1000;
const timeInterval = 5;
const points = [];
for(let i=0; i <= steps; i++){
points.push(path.getPointAtLength((path.getTotalLength()/steps)*i));
}
points.push(path.getPointAtLength(0));
points.forEach((p,i) => {
setTimeout(() => {moveGroup(p)}, timeInterval*i);
})
}
function moveGroup(point){
group.setAttribute('transform', `translate(${point.x - gcx}, ${point.y - gcy})`);
}
<svg viewBox="0 -20 800 200">
<path id="path" d="M 100 100 L 100 15 L 500 15 L 500 150" fill="none" stroke="#000000" stroke-width="3"/>
<g>
<rect height="50" width="100" fill="#FDD34D" rx="10" stroke-width="1px" stroke="#000000"></rect>
<text>Click me !</text>
</g>
</svg>