【问题标题】:Creating svg paths with javascript(shape morphing)使用 javascript(形状变形)创建 svg 路径
【发布时间】:2018-04-24 04:06:49
【问题描述】:

所以我有这个用于形状变形的类:

class ShapeOverlays {
  constructor(elm) {
    this.elm = elm;
    this.path = elm.querySelectorAll('path');
    this.numPoints = 18;
    this.duration = 600;
    this.delayPointsArray = [];
    this.delayPointsMax = 300;
    this.delayPerPath = 100;
    this.timeStart = Date.now();
    this.isOpened = false;
    this.isAnimating = false;
  }
    toggle() {
    this.isAnimating = true;
    const range = 4 * Math.random() + 6;
    for (var i = 0; i < this.numPoints; i++) {
      const radian = i / (this.numPoints - 1) * Math.PI;
      this.delayPointsArray[i] = (Math.sin(-radian) + Math.sin(-radian * range) + 2) / 4 * this.delayPointsMax;
    }
    if (this.isOpened === false) {
      this.open();
    } else {
      this.close();
    }
  }
  open() {
    this.isOpened = true;
    this.elm.classList.add('is-opened');
    this.timeStart = Date.now();
    this.renderLoop();
  }
  close() {
    this.isOpened = false;
    this.elm.classList.remove('is-opened');
    this.timeStart = Date.now();
    this.renderLoop();
  }
  updatePath(time) {
    const points = [];
    for (var i = 0; i < this.numPoints + 1; i++) {
      points[i] = ease.cubicInOut(Math.min(Math.max(time - this.delayPointsArray[i], 0) / this.duration, 1)) * 100
    }

    let str = '';
    str += (this.isOpened) ? `M 0 0 V ${points[0]} ` : `M 0 ${points[0]} `;
    for (var i = 0; i < this.numPoints - 1; i++) {
      const p = (i + 1) / (this.numPoints - 1) * 100;
      const cp = p - (1 / (this.numPoints - 1) * 100) / 2;
      str += `C ${cp} ${points[i]} ${cp} ${points[i + 1]} ${p} ${points[i + 1]} `;
    }
    str += (this.isOpened) ? `V 0 H 0` : `V 100 H 0`;
    return str;
  }
  render() {
    if (this.isOpened) {
      for (var i = 0; i < this.path.length; i++) {
        this.path[i].setAttribute('d', this.updatePath(Date.now() - (this.timeStart + this.delayPerPath * i)));
      }
    } else {
      for (var i = 0; i < this.path.length; i++) {
        this.path[i].setAttribute('d', this.updatePath(Date.now() - (this.timeStart + this.delayPerPath * (this.path.length - i - 1))));
      }
    }
  }
  renderLoop() {
    this.render();
    if (Date.now() - this.timeStart < this.duration + this.delayPerPath * (this.path.length - 1) + this.delayPointsMax) {
      requestAnimationFrame(() => {
        this.renderLoop();
      });
    }
    else {
      this.isAnimating = false;
    }
  }
}

(function() {
  const elmHamburger = document.querySelector('.hamburger');
  const gNavItems = document.querySelectorAll('.global-menu__item');
  const elmOverlay = document.querySelector('.shape-overlays');
  const overlay = new ShapeOverlays(elmOverlay);

  elmHamburger.addEventListener('click', () => {
    if (overlay.isAnimating) {
      return false;
    }
    overlay.toggle();
    if (overlay.isOpened === true) {
      elmHamburger.classList.add('is-opened-navi');
      for (var i = 0; i < gNavItems.length; i++) {
        gNavItems[i].classList.add('is-opened');
      }
    } else {
      elmHamburger.classList.remove('is-opened-navi');
      for (var i = 0; i < gNavItems.length; i++) {
        gNavItems[i].classList.remove('is-opened');
      }
    }
  });
}());

有人可以解释一下这段代码吗?我真的不明白如何使用时间创建路径,如何放置点以及如何修改它。范围用于什么? delayPointsArray 为什么要用三角函数?

基本上这是我没有得到的部分:

updatePath(time) {
        const points = [];
        for (var i = 0; i < this.numPoints + 1; i++) {
          points[i] = ease.cubicInOut(Math.min(Math.max(time - this.delayPointsArray[i], 0) / this.duration, 1)) * 100
        }

        let str = '';
        str += (this.isOpened) ? `M 0 0 V ${points[0]} ` : `M 0 ${points[0]} `;
        for (var i = 0; i < this.numPoints - 1; i++) {
          const p = (i + 1) / (this.numPoints - 1) * 100;
          const cp = p - (1 / (this.numPoints - 1) * 100) / 2;
          str += `C ${cp} ${points[i]} ${cp} ${points[i + 1]} ${p} ${points[i + 1]} `;
        }
        str += (this.isOpened) ? `V 0 H 0` : `V 100 H 0`;
        return str;
      }
      render() {
        if (this.isOpened) {
          for (var i = 0; i < this.path.length; i++) {
            this.path[i].setAttribute('d', this.updatePath(Date.now() - (this.timeStart + this.delayPerPath * i)));
          }
        } else {
          for (var i = 0; i < this.path.length; i++) {
            this.path[i].setAttribute('d', this.updatePath(Date.now() - (this.timeStart + this.delayPerPath * (this.path.length - i - 1))));
          }
        }
      }

为什么要使用时间?这样做的目的是什么:

points[i] = ease.cubicInOut(Math.min(Math.max(time - this.delayPointsArray[i], 0) / this.duration, 1)) * 100

【问题讨论】:

  • 这是一个动画。你不希望它被赶出过去的时间吗?
  • 是的,我知道我明白这一点,但是时间与点位置是如何对应的。我对这一切都很陌生。你能解释得更深入一点吗?我确实看到你在这方面很有经验......
  • 是否有任何理由将此问题标记为d 编程语言?

标签: javascript svg path d easing


【解决方案1】:

如果你看看updatePath() 是如何被调用的,它是这样的:

this.updatePath(Date.now() - (this.timeStart + this.delayPerPath * i))

所以传入的time 值是当前时间与我们正在使用的路径的开始时间之间的差。

那么你感兴趣的那行代码在做什么呢?

points[i] = ease.cubicInOut(Math.min(Math.max(time - this.delayPointsArray[i], 0) / this.duration, 1)) * 100

我将忽略delayPointsArray。它正在根据角度稍微修改开始时间。没有看到完整的演示,我不确定原因。

这行代码的目的是计算我们通过当前路径的动画有多远。结果是从 0 到 100 的坐标值形式。

它在这一行代码中做了很多事情。因此,让我们分解各个步骤。

  1. 首先,我们将经过的time 限制为最小值。

    Math.max(time, 0)
    

    换句话说,动画开始时间之前的任何东西都变为零。

  2. 然后我们除以动画的持续时间。

    Math.max(time, 0) / duration
    

    这将产生一个从 0 代表动画开始到 1 代表动画结束的值。但是,如果经过的时间在动画结束之后,该值也可能大于 1。因此下一步。

  3. 现在将该值限制为最大值 1。

    Math.min( Math.max(time, 0) / duration, 1)
    

    我们现在有一个值 >= 0 和

  4. 然而,这个值是严格线性的,与时间的进展相对应。通常线性运动不是你想要的。这是不自然的。物体在开始移动时加速,在停止时减速。这就是easeInOut() 函数将要做的事情。如果您不熟悉缓动曲线,请看下图。

    来源:Google: The Basics of Easing

    所以我们从 0..1(横轴)传入一个线性时间值。它将返回一个考虑了加速和减速的修改值。

  5. 最后一步是乘以 100,转换为最终坐标值 (0..100)。

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 2020-05-16
    • 2011-02-27
    • 1970-01-01
    • 1970-01-01
    • 2019-01-08
    • 2016-12-26
    • 1970-01-01
    • 1970-01-01
    • 2012-09-11
    相关资源
    最近更新 更多