使用canvas绘制dribble风格水波浪

4,048

先放一张简单的效果图


传送门

😔原理

sin函数估计大家都会,用到的公式为amplitude * sin(frequency * x) + displacement

我们可以通过改变函数的振幅和频率来达到控制曲线动画。


😈开始

定义一个Wave

```

class Wave {

  constructor(color) {
    this.t = 0;
    this.step = w / 300;
    this.color = color;
    this.speed = 0.1;
    this.a = 30;
    this.f = 20;
    this.d = 20;
  }
  caculate(x) {
    this.amplitude = this.a;
    this.frequency = this.f * (1 / w);
    this.displacement = this.d;
    // A*sin(Bx + C) + D
    return (
      this.amplitude * Math.sin(this.frequency * x + this.t) + this.displacement
    );
  }
  render() {
    this.t += this.speed;
    ctx.save();
    ctx.beginPath();
    ctx.translate(0, h / 2);
    ctx.lineWidth = 1;
    ctx.moveTo(0, 0);
    for (let i = this.step; i < w; i += this.step) {
      ctx.lineTo(i, this.caculate(i));
    }
    ctx.lineTo(w, this.caculate(w));
    ctx.lineTo(w, h);
    ctx.lineTo(0, h);
    ctx.fillStyle = this.color;
    ctx.fill();
    ctx.closePath();
    ctx.restore();
  }
}
```

这里面没有什么特别的魔法,很容易就能够画出一条波浪。

传送门

但是稍微有点感觉的人就会发现整个动画特别僵硬,就像是一张图片在做平移一样。作为一个有理想的人怎么可以忍。


😄改进

很容易发现让人觉得僵硬的原因是波浪的波峰一直是这么高,如果能让波峰的大小也随着改动的画就OK了。

添加一点小小的细节,定义一个this.k的值,然后修改

this.amplitude = this.a;

改为

this.amplitude = this.a * sin(((this.t / this.k) * this.k) / 5);

看到这里你可能会好奇这迷一样的函数怎么来的,我们的目标是让振幅也跟着变动,所以就乘以了一个sin函数来让振幅也可以高高低低变化。


🐔最后

案例简单实现


传送门


参考链接

codepen.io/waynecz/pen…

dribbble.com/shots/36529…

dribbble.com/shots/37814…