首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >从理论上讲,你是如何沿着路径实现动画的?

从理论上讲,你是如何沿着路径实现动画的?
EN

Stack Overflow用户
提问于 2021-09-08 20:06:34
回答 1查看 19关注 0票数 0

我正在研究CSS中的offset-path,您可以在其中执行以下操作:

代码语言:javascript
运行
复制
* {
  box-sizing: border-box;
}

:root {
  --delay: 0ms;
  --path: path("M.4 76.8C102-24.9 266.9-24.9 368.5 76.8c81.3 81.3 81.3 213.2 0 294.5-65.1 65.1-170.6 65.1-235.6 0-52.1-52.1-52.1-136.5 0-188.5 41.6-41.6 109.2-41.6 150.8 0 33.3 33.3 33.3 87.3 0 120.6-26.7 26.7-69.9 26.7-96.5 0-21.3-21.3-21.3-55.9 0-77.2 17.1-17.1 44.7-17.1 61.8 0 13.6 13.6 13.6 35.8 0 49.4-10.9 10.9-28.6 10.9-39.5 0-8.7-8.7-8.7-22.9 0-31.6 7-7 18.3-7 25.3 0");
}

body {
  margin: 0;
  padding: 2rem;
  min-height: 100vh;
  background-color: #1b1b24;
  display: flex;
  justify-content: center;
  align-items: center;
}

.wrapper {
  position: relative;
}

.obj {
  --color: salmon;
  position: absolute;
  top: 0;
  left: 0;
  offset-path: var(--path);
  animation: move 4500ms infinite ease-in-out var(--delay);
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  background-color: var(--color);
  opacity: 0;
  transform: scale(0);
}

.obj--2 {
  --delay: 1500ms;
  --color: hotpink;
}

.obj--3 {
  --delay: 3000ms;
  --color: turquoise;
}

svg {
  width: 429px;
}

@keyframes appear {
  100% {
    opacity: 1;
  }
}
@keyframes move {
  10% {
    opacity: 1;
    offset-distance: 0%;
    transform: scale(1);
  }
  30% {
    box-shadow: -0.5rem 0 0.3rem var(--color, white);
  }
  70% {
    box-shadow: -0.5rem 0 0.3rem var(--color, white);
  }
  90% {
    opacity: 1;
    offset-distance: 100%;
    transform: scale(0.2);
    box-shadow: none;
  }
  100% {
    opacity: 0;
    offset-distance: 100%;
    transform: scale(0.2);
  }
}
代码语言:javascript
运行
复制
<div class="wrapper">
  <div class="obj"></div>
  <div class="obj obj--2"></div>
  <div class="obj obj--3"></div>
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 431.7 422.6"><path d="M1.1 77.8c101.7-101.7 266.5-101.7 368.2 0 81.3 81.3 81.3 213.2 0 294.5-65.1 65.1-170.6 65.1-235.6 0-52.1-52.1-52.1-136.5 0-188.5 41.6-41.6 109.2-41.6 150.8 0 33.3 33.3 33.3 87.3 0 120.6-26.7 26.7-69.9 26.7-96.5 0-21.3-21.3-21.3-55.9 0-77.2 17.1-17.1 44.7-17.1 61.8 0 13.6 13.6 13.6 35.8 0 49.4-10.9 10.9-28.6 10.9-39.5 0-8.7-8.7-8.7-22.9 0-31.6 7-7 18.3-7 25.3 0" fill="none" stroke="#5e5e7d" stroke-width="3" stroke-miterlimit="10"/></svg>
</div>

他们有这个变量:

代码语言:javascript
运行
复制
  --path: path("M.4 76.8C102-24.9 266.9-24.9 368.5 76.8c81.3 81.3 81.3 213.2 0 294.5-65.1 65.1-170.6 65.1-235.6 0-52.1-52.1-52.1-136.5 0-188.5 41.6-41.6 109.2-41.6 150.8 0 33.3 33.3 33.3 87.3 0 120.6-26.7 26.7-69.9 26.7-96.5 0-21.3-21.3-21.3-55.9 0-77.2 17.1-17.1 44.7-17.1 61.8 0 13.6 13.6 13.6 35.8 0 49.4-10.9 10.9-28.6 10.9-39.5 0-8.7-8.7-8.7-22.9 0-31.6 7-7 18.3-7 25.3 0");

不知何故,它被转换成一些底层的数据结构,然后每一帧,你都会以某种方式沿着这条“路径”移动。它是如何工作的,你如何在高层次上实现它?

基本上,我假设您已经以某种方式将SVG d路径转换为向量(点数组?),但这似乎不正确,因为有些点不在曲线上(控制点)。然后,时钟的每一次滴答/更新,它都会移动对象的x和y位置,使其沿着路径更远。然而,我并不是在做思想上的飞跃/连接,看看如何实现这一点。有什么见解吗?

我猜问题的一部分是,path是如何在幕后实现的?如何计算沿着路径/曲线的时钟的每个滴答声的下一个位置?CSS以某种方式将所有这些都抽象了出来,我想知道它的内部工作原理。

EN

回答 1

Stack Overflow用户

发布于 2021-09-08 20:27:41

This可能会有所帮助:

代码语言:javascript
运行
复制
function setAnimationsProgress(insTime) {
  let i = 0;
  const animations = instance.animations;
  const animationsLength = animations.length;
  while (i < animationsLength) {
    const anim = animations[i];
    const animatable = anim.animatable;
    const tweens = anim.tweens;
    const tweenLength = tweens.length - 1;
    let tween = tweens[tweenLength];
    // Only check for keyframes if there is more than one tween
    if (tweenLength) tween = filterArray(tweens, t => (insTime < t.end))[0] || tween;
    const elapsed = minMax(insTime - tween.start - tween.delay, 0, tween.duration) / tween.duration;
    const eased = isNaN(elapsed) ? 1 : tween.easing(elapsed);
    const strings = tween.to.strings;
    const round = tween.round;
    const numbers = [];
    const toNumbersLength = tween.to.numbers.length;
    let progress;
    for (let n = 0; n < toNumbersLength; n++) {
      let value;
      const toNumber = tween.to.numbers[n];
      const fromNumber = tween.from.numbers[n] || 0;
      if (!tween.isPath) {
        value = fromNumber + (eased * (toNumber - fromNumber));
      } else {
        value = getPathProgress(tween.value, eased * toNumber, tween.isPathTargetInsideSVG);
      }
      if (round) {
        if (!(tween.isColor && n > 2)) {
          value = Math.round(value * round) / round;
        }
      }
      numbers.push(value);
    }
    // Manual Array.reduce for better performances
    const stringsLength = strings.length;
    if (!stringsLength) {
      progress = numbers[0];
    } else {
      progress = strings[0];
      for (let s = 0; s < stringsLength; s++) {
        const a = strings[s];
        const b = strings[s + 1];
        const n = numbers[s];
        if (!isNaN(n)) {
          if (!b) {
            progress += n + ' ';
          } else {
            progress += n + b;
          }
        }
      }
    }
    setProgressValue[anim.type](animatable.target, anim.property, progress, animatable.transforms);
    anim.currentValue = progress;
    i++;
  }
}

function getPathProgress(path, progress, isPathTargetInsideSVG) {
  function point(offset = 0) {
    const l = progress + offset >= 1 ? progress + offset : 0;
    return path.el.getPointAtLength(l);
  }
  const svg = getParentSvg(path.el, path.svg)
  const p = point();
  const p0 = point(-1);
  const p1 = point(+1);
  const scaleX = isPathTargetInsideSVG ? 1 : svg.w / svg.vW;
  const scaleY = isPathTargetInsideSVG ? 1 : svg.h / svg.vH;
  switch (path.property) {
    case 'x': return (p.x - svg.x) * scaleX;
    case 'y': return (p.y - svg.y) * scaleY;
    case 'angle': return Math.atan2(p1.y - p0.y, p1.x - p0.x) * 180 / Math.PI;
  }
}

在那里,它计算每个动画步骤的路径点。不知道该如何进一步解释。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69109073

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档