阅读 368

学习 canvas (七)实现圆球水波进度

这是我参与更文挑战的第8天,活动详情查看: 更文挑战

简介

为了更好的使用canvas,动手实现一些小功能是很有必要的。这里主要使用clip()函数,实现水波只在裁剪后的圆中绘制,来实现圆球的水波进度。

绘制水波曲线

  1. 水波曲线最简单的生成公式就是使用三角函数。这里使用Math.sin()来生成曲线。
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <style type="text/css">
    canvas {
      background-color: #fff;
      border: 1px solid #ccc;
      margin: 0 auto;
      display: block;
    }
  </style>
  <body>
    <canvas height="100" width="100" id="canvas"></canvas>
  </body>
  <script>
    /** @type {HTMLCanvasElement} */
    var canvas = document.getElementById('canvas')
    var ctx = canvas.getContext('2d')

    var width = canvas.width
    var height = canvas.height

    var sinX = 0 // x轴
    var offsetY = 0.3 // 高度  -- 越大高度越高

    drawWave(ctx, sinX, offsetY, 0.06, 4, '#a4def6')

    /**
     * 波浪线
     * @param ctx --canvas上下文
     * @param sinX -- 波浪线 sin坐标中 x轴的位置
     * @param offsetY -- 波浪线 在画布中的 高度比 画布垂直距离
     * @param waveW -- 波浪宽度
     * @param waveH -- 波浪深度
     * */
    function drawWave(ctx, sinX, offsetY, waveW, waveH, color) {
      var canvasW = width
      var canvasH = height
      var offsetX = 0 // 波浪线 初始x轴坐标
      ctx.beginPath()
      ctx.lineWidth = 1
      for (var x = offsetX; x < canvasW; x += 20 / canvasW) {
        // 正弦曲线公式:y = Asin(ωx+φ) + k
        var y = waveH * Math.sin((offsetX + x) * waveW + sinX) + (1 - offsetY) * canvasH
        ctx.lineTo(x, y)
      }
      ctx.stroke()

      // 填充背景 水
      ctx.lineTo(canvasW, canvasH)
      ctx.lineTo(offsetX, canvasH)
      ctx.fillStyle = color
      ctx.fill()
    }
  </script>
</html>

复制代码
  1. 使用正弦曲线公式,根据外部参数来控制曲线在画布中的位置和在坐标轴中的位置。

image.png

添加流动效果

    ...
    var offsetY = 0.3 // 高度  -- 越大高度越高

    var speed = 0.2 // x轴 移动距离

    var count = 0
    var temInterval = setInterval(() => {
      draw(count)
      count++
    }, 16)
    // 绘制 波浪
    function draw(countNum) {
      ctx.clearRect(0, 0, width, height)
      sinX = countNum * speed
      drawWave(ctx, sinX, offsetY, 0.06, 4, '#a4def6')
    }
    
    ...
复制代码
  • 这里比较简单,通过clearRect()清空画布,然后修改sinX大小,让曲线动起来。

1.gif

在圆球加入水波

    ...
    var speed = 0.2 // x轴 移动距离

    var offsetYRange = 1.1 // 高度比
    drawContainer() // 裁剪圆形区

    var count = 0
    var temInterval = setInterval(() => {
      if (count <= 100) {
        draw(count)
      } else {
        clearInterval(temInterval)
      }
      count++
    }, 50)
    // 绘制 波浪
    function draw(countNum) {
      ctx.clearRect(0, 0, width, height)

      const num = offsetYRange / 100
      offsetY = countNum * num

      sinX = countNum * speed
      drawWave(ctx, sinX, offsetY, 0.06, 4, '#a4def6')
    }
    
    ...
    
    // 裁剪圆形区
    function drawContainer() {
      var pointR = width / 2
      var lineWidth = 5
      var circleR = pointR - lineWidth

      ctx.lineWidth = lineWidth
      ctx.beginPath()
      ctx.arc(pointR, pointR, circleR, 0, 2 * Math.PI)
      ctx.strokeStyle = 'rgba(192,225,242,0.8)'
      ctx.stroke()
      ctx.clip()
    }
复制代码

1.gif

  1. 这里使用clip()函数剪切圆形区域。需注意,要先执行drawContainer()才能保证后面的绘制都在圆中。
  2. 根据offsetYRange计算百分比中的高度比,来修改水波高度。

加入文字

    ...
    // 绘制 波浪
    function draw(countNum) {
      ...
      drawWave(ctx, sinX, offsetY, 0.06, 4, '#a4def6')
      drawWave(ctx, sinX + 2, offsetY - 0.02, 0.06, 6, '#79d4f9')
      drawText(countNum)
    }

    /**
     * 百分比文字
     * */
    function drawText(countNum) {
      var size = width / 4 - 5
      ctx.font = 'bold ' + size + 'px Microsoft Yahei'
      let txt = countNum + '%'
      ctx.fillStyle = '#f6b71e'
      ctx.textAlign = 'center'
      ctx.fillText(txt, width / 2 + 2, width / 2 + 5)
    }
复制代码

1.gif

  • 前面我们只让轮询执行100次,这里直接获取对应的数字,展示在页面上就好了。需要注意文本绘制要在最后执行,保证文字在最上层。
  • 这里多绘制了一成水波,让小球更有立体感。
文章分类
前端
文章标签