简体   繁体   English

使用JavaScript创建SVG路径(形状变形)

[英]Creating svg paths with javascript(shape morphing)

So I have this class which is used for shape morphing: 所以我有一个用于形状变形的类:

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');
      }
    }
  });
}());

Can some one please explain this code? 有人可以解释一下此代码吗? I don't really get how the paths are created using time,how the points are placed and how could I modify it.What is range used for? 我真的不知道如何使用时间创建路径,如何放置点以及如何修改它。范围是做什么用的? Why are trigonometral functions used for the delayPointsArray? 为什么三角函数用于delayPointsArray?

Basically it's this part that I don't get: 基本上这是我不了解的部分:

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))));
          }
        }
      }

Why is time being used? 为什么要用时间? What is the purpose of this: 这样做的目的是什么:

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

If you look at how updatePath() is being called, it's like this: 如果查看如何调用updatePath() ,则如下所示:

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

So the time value passed in is the difference between the current time, and the start time of the path we are working with. 因此,传入的time值是当前时间与我们正在使用的路径的开始时间之间的差。

So what then is the line of code you are interested in, doing? 那么您感兴趣的代码行是做什么的呢?

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

I'm going to ignore delayPointsArray . 我将忽略delayPointsArray It is modifying the start time slightly based on angle. 它会根据角度稍微修改开始时间。 Without seeing the full demo, I'm not sure of the reason for that. 没有看到完整的演示,我不确定原因。

The purpose of this line of code is to calculate how far through the current path's animation we are. 这行代码的目的是计算当前路径动画的距离。 The result is in the form of a coordinate value from 0 to 100. 结果的形式为从0到100的坐标值。

It's doing a lot in that one line of code. 只需一行代码即可完成很多工作。 So let's break down the individual steps. 因此,让我们分解各个步骤。

  1. Firstly, we are clamping the elapsed time to minimum of 0. 首先,我们将经过time为最小0。

     Math.max(time, 0) 

    In other words, anything before the animation start time becomes zero. 换句话说,动画开始时间之前的所有内容都为零。

  2. Then we divide by the animation's duration. 然后我们除以动画的持续时间。

     Math.max(time, 0) / duration 

    This will result in a value from 0, representing the start of the animation, to 1, representing the end of the animation. 这将导致从0(代表动画的开始)到1(代表动画的结束)的值。 However, the value might also be greater than 1 if the elapsed time is after the end of the animation. 但是,如果经过的时间是在动画结束之后,则该值也可能大于1。 Hence the next step. 因此,下一步。

  3. Now clamp this value to a maximum of 1. 现在将此值限制为最大值1。

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

    We now have a value >= 0 and <= 1 whichdescribes where in the course of the animation, the path is supposed to be. 现在,我们有一个值> = 0和<= 1,该值描述了在动画过程中路径应位于的位置。 0 if we should be at the animations start position. 如果我们应该在动画开始位置,则为0。 1 if we should be at the animations end position. 1,如果我们应该处于动画结束位置。 And somewhere in between if the animation is in progress. 如果动画正在进行,则介于两者之间。

  4. However this value is strictly linear, corresponding with the progression of time. 但是,该值严格线性,与时间的推移相对应。 And usually linear movement is not what you want. 通常,直线运动不是您想要的。 It is unnatural. 这是不自然的。 Objects accelarate when the start moving and decelerate when the come to a stop. 物体在开始移动时会加速,在停止时会减速。 That will be what the easeInOut() function will be doing. 这将是easeInOut()函数将要执行的操作。 If you are not familiar with easing curves, take a look at the diagram below. 如果您不熟悉缓和曲线,请查看下图。

    缓入缓出时序曲线

    Source: Google: The Basics of Easing 资料来源: Google:轻松的基础

    So we pass in a linear time value from 0..1 (horizontal axis). 因此,我们传入一个从0..1(水平轴)开始的线性时间值。 It will return a modified value that takes into account acceleration and deceleration. 它将返回修改后的值,其中考虑了加速和减速。

  5. The final step is to multiply by 100, to convert to a final coordinate value (0..100). 最后一步是乘以100,以转换为最终坐标值(0..100)。

Hope this helps. 希望这可以帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM