动画JS沿路径逐渐动画到特定点

问题描述

https://codepen.io/jesserosenfield/pen/LYNGRXV

var path = anime.path('#prog-svg path'),pathEl = document.querySelectorAll('#prog-svg path')[0],mylength = pathEl.getTotalLength(),mypt1 = pathEl.getPointAtLength(mylength * .10),mypt2 = pathEl.getPointAtLength(mylength * .25);

var motionPath = anime({
    targets: '.prog-circ',translateX: path('x'),translateY: path('y'),rotate: path('angle'),easing: 'easeInOutCirc',duration: 5000,direction: 'alternate',autoplay: false,elasticity: 200,loop: false,update: function(anim){
      console.log(path('x'));
    }
  });

motionPath.seek(1210);

motionPath.play();

代码可以实现我希望在广泛的方案中完成的工作,但是我有一个更具体的用例。

我正在将此SVG用作表单上的进度条:

enter image description here

用户完成表单的步骤#1时,我希望圆从点A到B进行动画。当用户完成表单的步骤#2时,我希望圆从B点到C到点动画。 ...等等。

虽然motionpath.seek()使我沿着路径到达正确的点,但它没有动画地在其中设置了圆–有与seek()等效的函数可以使该圆变为动画,而不仅仅是设置它?

此外,我还尝试使用getTotalLength()getPointAtLength()来尝试设置动画,如下所示:

var motionPath = anime({
    targets: '.prog-circ',translateX: [mypt1.x,mypt2.x],translateY: [mypt1.y,mypt2.y],

但是这并没有使路径上的圆圈产生动画。

任何帮助深表感谢。谢谢!

解决方法

沿着一条很长的路,我认为很难支持在点之间移动,因为您需要跟踪当前进度并将其根据缓动功能转换为实际长度。

我将您的<path/>分为3个片段,在这3个片段之间生成了动画时间轴,然后轻松地来回移动圆。

这是一个如何完成的示例:

const svg = document.getElementById('prog-svg');
const pathEl = document.querySelector('#prog-svg path');
const totalLength = pathEl.getTotalLength();

const points = [['A',10],['B',25],['C',75],['D',90]];

function splitPath() {
  const interval = 3;
  const toLen = percentage => percentage * totalLength / 100;
  const paths = [];
  for (let i = 0; i < points.length; i++) {
    const from = toLen(points[i][1]);

    for (let j = i + 1; j < points.length; j++) {
      const to = toLen(points[j][1]);
      const segments = [];
      for (let k = from; k <= to; k += interval) {
        const { x,y } = pathEl.getPointAtLength(k);
        segments.push([x,y]);
      }
      paths.push({
        segments,path: `${i}-${j}`
      });
    }
  }

  paths.forEach(subPath => {
    const subPathEl = document.createElementNS('http://www.w3.org/2000/svg','path');
    subPathEl.setAttribute('class',`st0 st0--hidden`);
    subPathEl.setAttribute('d',`M ${subPath.segments.map(([x,y]) => `${x},${y}`).join(' ')}`);
    svg.appendChild(subPathEl);
    subPath.el = subPathEl;
  });

  return paths;
}

const subPaths = splitPath();

function addPoint(name,progress) {
  const point = pathEl.getPointAtLength(totalLength * progress / 100);

  const text = document.createElementNS('http://www.w3.org/2000/svg','text');
  text.setAttribute('fill','#fff');
  text.setAttribute('font-size','1.6em');
  text.textContent = name;

  const circle = document.createElementNS('http://www.w3.org/2000/svg','circle');
  circle.setAttribute('r','30');
  circle.setAttribute('fill','#000');

  const g = document.createElementNS('http://www.w3.org/2000/svg','g');
  g.setAttribute('transform',`translate(${point.x},${point.y})`);
  g.appendChild(circle);

  g.appendChild(text);

  svg.appendChild(g);

  // center text
  const textBB = text.getBBox();
  const centerX = textBB.width / 2;
  const centerY = textBB.height / 4;
  text.setAttribute('transform',`translate(${-centerX},${centerY})`);

  return circle;
}

points.forEach(([name,progress]) => addPoint(name,progress));

const progressCircle = document.querySelector('.prog-circ');
progressCircle.style.display = 'block';

const animations = subPaths.map(subPath => {
  const animePath = anime.path(subPath.el);
  return anime({
    targets: progressCircle,easing: 'easeInOutCirc',autoplay: false,duration: 1000,translateX: animePath('x'),translateY: animePath('y'),rotate: animePath('angle'),});
});
// move circle to the first point
animations[0].reset();

let currentStep = 0;

function moveTo(step) {
  if (step < 0 || step > animations.length) return;
  const delta = step - currentStep;

  const path = delta > 0 ? `${currentStep}-${step}` : `${step}-${currentStep}`;
  const animationIndex = subPaths.findIndex(subPath => subPath.path === path);
  const animationToPlay = animations[animationIndex];

  if (delta < 0 &&  !animationToPlay.reversed) {
    animationToPlay.reverse();
  }
  if (delta > 0 &&  animationToPlay.reversed) {
    animationToPlay.reverse();
  }
  animationToPlay.reset();
  animationToPlay.play();

  currentStep = step;
  pagination.selectedIndex = step;
}

const btnPrev = document.getElementById('btn-prev');
const btnNext = document.getElementById('btn-next');
const pagination = document.getElementById('pagination');
btnPrev.addEventListener('click',() => moveTo(currentStep - 1));
btnNext.addEventListener('click',() => moveTo(currentStep + 1));
pagination.addEventListener('change',(e) => moveTo(+e.target.value));
body {
  margin: 0;
}

.st0 {
  fill: none;
  stroke: #000000;
  stroke-width: 5;
  stroke-linecap: round;
  stroke-miterlimit: 160;
  stroke-dasharray: 28;
}

.st0--hidden {
  stroke: none;
}
.prog-circ {
  display: none;
  position: absolute;
  border-radius: 100%;
  height: 30px;
  width: 30px;
  top: -15px;
  left: -15px;
  background: #ccc;
  opacity: .7;
}

.form-actions {
  margin-top: 2em;
  display: flex;
  justify-content: center;
}

#pagination,.form-actions button + button {
  margin-left: 2em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.0/anime.min.js"></script>
<svg id="prog-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1919.1 155.4">
  <g>
    <path class="st0" d="M4,84.1c0,58.8-57.1,235.1,17.9s348.1,18.9,470.2-44.6C800.6,9.7,869.6-2,953.5,6.6c0,19,4.1,38.6,14.4
      c20.7,10.9,40.7,40.6,65.6c0,40.2-29.5,64.8-69.7,64.8s-70.1-29.2-70.1-69.4c0-32.3,31.2-59.6,61.8-61.8
      c67.2-4.7,103.5-46.8,375.6,70.1c164.9,70.8,220.1-1.1,371.1-11.7c120.5-8.4,213.7,28.6,28.6"/>
  </g>
</svg>

<div class="prog-circ"></div>
<div class="form-actions">
  <button id="btn-prev">Prev</button>
  <button id="btn-next">Next</button>
  <select id="pagination">
    <option value="0">A</option>
    <option value="1">B</option>
    <option value="2">C</option>
    <option value="3">D</option>
  </select>
</div>

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...