© Bohua Xu

review canvas

Nov 19, 2022 · 30min

jotting

正是H5的这么一项技术再加上adobe的svg使得童年的flash逐渐退出了历史的舞台,就像我们不能一直玩洛克王国,玩4399的各种小游戏一样,我们也不可能一直使用旧的技术,web前端再以一种很快的速度更新迭代,技术一直在发展,日新月异。从vue2vue3,从react类式组件函数式组件,从初识css in js的激动不已到css in jsemotion库的主要贡献者提出因为性能的缺陷弃用css in js而采用Sass Modules等等等 ,我初识前端就感受到了它的发展之快,不断的迭代。

从21年初次学习使用canvas到现在也过去了一年多的时间了,我很喜欢canvas但是我在实际的各个项目中使用的少之又少,在写数据可视化相关的项目时,我也没有去自己使用canvas去写各种效果,因为会占用很多时间,并没有大把的数据可视化的库来的方便,导致很多canvas的特性也早就忘的差不多了,webgl我很感兴趣,three.js我认为拿这些3D的东西写出的页面很炫酷,需要很牢靠的数学支撑,我也在各种的说国内webgl岗位并不多、少等等等中,并未深入学习。这是一篇我复习canvas的一些特性的简单文章。

微信图片

21年使用canvas画的国旗

微信图片_20221120014939

go over canvas

canvas标签创建了一个固定大小的画布,公开了一个或多个渲染的上下文,我们想要绘制就要先找到上下文

canvas提供了一个方法—getContext()通过此方法我们就可以获取渲染上下文和绘画功能

上下文类型(contextType)

是一个指示使用何种上下文的 DOMString 。可能的值是:

线

moveTo(x, y)

设置初始位置,参数为初始位置x和y的坐标点

lineTo(x, y)

绘制一条从初始位置到指定位置的直线,参数为指定位置x和y的坐标点

stroke()

通过线条来绘制图形轮廓

//简单画三角形
<style>
        canvas {
            box-shadow: 0px 0px 5px #ccc;
            border-radius: 8px;
        }
    </style>
</head>
<body>
    <canvas id="canvas" width="500" height="500"></canvas>
    <script>
        var canvas = document.getElementById('canvas')
        if(canvas.getContext) {
            var ctx = canvas.getContext('2d')
            ctx.beginPath()
            ctx.moveTo(50,50)
            ctx.lineTo(200,200)
            ctx.lineTo(200,50)
            ctx.lineTo(50,50)
            ctx.fill()
            ctx.stroke()
        }
    </script>
//简单画三角形
<style>
        canvas {
            box-shadow: 0px 0px 5px #ccc;
            border-radius: 8px;
        }
    </style>
</head>
<body>
    <canvas id="canvas" width="500" height="500"></canvas>
    <script>
        var canvas = document.getElementById('canvas')
        if(canvas.getContext) {
            var ctx = canvas.getContext('2d')
            ctx.beginPath()
            ctx.moveTo(50,50)
            ctx.lineTo(200,200)
            ctx.lineTo(200,50)
            ctx.lineTo(50,50)
            ctx.fill()
            ctx.stroke()
        }
    </script>

矩形

fillRect(x, y, width, height)

x和y 是矩形的起点坐标,width和height 是矩形的宽高。

绘制一个填充的矩形

strokeRect(x, y, width, height)

绘制一个矩形的边框

clearRect(x, y, width, height)

清除指定矩形区域,让清除部分完全透明。

圆和圆弧

绘制圆弧或者圆,我们使用arc()方法。当然可以使用arcTo(),不过这个的实现并不是那么的可靠,所以我们这里不作介绍。

这里详细介绍一下 arc 方法,该方法有六个参数:x,y为绘制圆弧所在圆上的圆心坐标。radius为半径。startAngle以及endAngle参数用弧度定义了开始以及结束的弧度。这些都是以 x 轴为基准。参数anticlockwise为一个布尔值。为 true 时,是逆时针方向,否则顺时针方向。

👻备注: arc() 函数中表示角的单位是弧度,不是角度。角度与弧度的 js 表达式:

弧度=(Math.PI/180)*角度。

二次贝塞尔曲线及三次贝塞尔曲线

二次及三次贝塞尔曲线都十分有用,一般用来绘制复杂有规律的图形(贝塞尔曲线)。

quadraticCurveTo(cp1x, cp1y, x, y)
quadraticCurveTo(cp1x, cp1y, x, y)

绘制二次贝塞尔曲线,cp1x,cp1y 为一个控制点,x,y 为结束点。

bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

绘制三次贝塞尔曲线,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二,x,y为结束点。

右边的图能够很好的描述两者的关系,二次贝塞尔曲线有一个开始点(蓝色)、一个结束点(蓝色)以及一个控制点(红色),而三次贝塞尔曲线有两个控制点。

参数 x、y 在这两个方法中都是结束点坐标。cp1x,cp1y为坐标中的第一个控制点,cp2x,cp2y为坐标中的第二个控制点。

使用二次以及三次贝塞尔曲线是有一定的难度的,因为不同于像 Adobe Illustrators 这样的矢量软件,我们所绘制的曲线没有给我们提供直接的视觉反馈。这让绘制复杂的图形变得十分困难。在下面的例子中,我们会绘制一些简单有规律的图形,如果你有时间以及更多的耐心,很多复杂的图形你也可以绘制出来。

这里也可以借助一个网页版的三次贝塞尔曲线调试工具来看效果

椭圆

添加椭圆路径。

语法:ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)

  • x、y:椭圆的圆心位置
  • radiusX、radiusY:x轴和y轴的半径
  • rotation:椭圆的旋转角度,以弧度表示
  • startAngle:开始绘制点
  • endAngle:结束绘制点
  • anticlockwise:绘制的方向(默认顺时针),可选参数。

矩形

直接在画布上绘制矩形的三个额外方法,正如我们开始所见的-绘制矩形,同样,也有 rect() 方法,将一个矩形路径增加到当前路径上。

  • rect(x, y, width, height)

    绘制一个左上角坐标为(x,y),宽高为 width 以及 height 的矩形。

当该方法执行的时候,moveTo() 方法自动设置坐标参数(0,0)。也就是说,当前笔触自动重置回默认坐标。

绘制样式

线条的样式

线条的样式可以通过下面一系列属性来设置。

lineWidth

lineWidth 设置当前绘线的粗细。属性值必须为正数。默认值是 1.0。

lineCap

lineCap 设置线段端点显示的样子。可选值为:butt,round 和 square。默认是 butt。

lineJoin

lineJoin 该属性可以设置两线段连接处所显示的样子。可选值为:round, bevel 和 miter。默认是 miter。

miterLimit

miterLimit 限制当两条线相交时交接处最大长度;所谓交接处长度(斜接长度)是指线条交接处内角顶点到外角顶点的长度。

线段之间夹角比较大时,交点不会太远,但随着夹角变小,交点距离会呈指数级增大。

如果交点距离大于miterLimit值,连接效果会变成了 lineJoin = bevel 的效果。

setLineDash/getLineDash

setLineDash 可以设置当前虚线样式。

getLineDash 则是返回当前虚线设置的样式,长度为非负偶数的数组。

lineDashOffset

lineDashOffset 设置虚线样式的起始偏移量。

透明度

除了绘制实色的图形,还可以绘制有透明度的图形。通过设置 globalAlpha 属性或者使用有透明度的样式作为轮廓或填充都可以实现

等等各种样式[可见MDN文档](使用样式和颜色 - Web API 接口参考 | MDN (mozilla.org))

绘制文本

canvas 提供了两种方法来渲染文本:

同样文本的样式也可以改变

动画

绘制小球

<!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>canvas - 动画</title>
  <style>
    /* 给画布增加一个阴影和圆角的样式 */
    canvas {
      box-shadow: 0px 0px 5px #ccc;
      border-radius: 8px;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width="500" height="500">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
  <script>
    // 获取 canvas 元素
    var canvas = document.getElementById('canvas');
    // 通过判断getContext方法是否存在来判断浏览器的支持性
    if(canvas.getContext) {
      // 获取绘图上下文
      var ctx = canvas.getContext('2d');
      var ball = {
        x: 100,
        y: 100,
        radius: 25,
        color: 'blue',
        draw: function() {
          ctx.beginPath();
          ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
          ctx.closePath();
          ctx.fillStyle = this.color;
          ctx.fill();
        }
      };
      ball.draw();
    }
  </script>
</body>
</html>
<!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>canvas - 动画</title>
  <style>
    /* 给画布增加一个阴影和圆角的样式 */
    canvas {
      box-shadow: 0px 0px 5px #ccc;
      border-radius: 8px;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width="500" height="500">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
  <script>
    // 获取 canvas 元素
    var canvas = document.getElementById('canvas');
    // 通过判断getContext方法是否存在来判断浏览器的支持性
    if(canvas.getContext) {
      // 获取绘图上下文
      var ctx = canvas.getContext('2d');
      var ball = {
        x: 100,
        y: 100,
        radius: 25,
        color: 'blue',
        draw: function() {
          ctx.beginPath();
          ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
          ctx.closePath();
          ctx.fillStyle = this.color;
          ctx.fill();
        }
      };
      ball.draw();
    }
  </script>
</body>
</html>

速率

我们通过给小球添加速率矢量进行移动。这个依旧用requestAnimationFrame() 方法来实现,在每一帧里面,依旧用clear 清理掉之前帧里旧的圆形。

<!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>canvas - 裁剪</title>
  <style>
    /* 给画布增加一个阴影和圆角的样式 */
    canvas {
      box-shadow: 0px 0px 5px #ccc;
      border-radius: 8px;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width="500" height="500">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
  <script>
    // 获取 canvas 元素
    var canvas = document.getElementById('canvas');
    // 通过判断getContext方法是否存在来判断浏览器的支持性
    if(canvas.getContext) {
      // 获取绘图上下文
      var ctx = canvas.getContext('2d');
      var ball = {
        x: 100,
        y: 100,
        vx: 1,
        vy: 3,
        radius: 25,
        color: 'blue',
        draw: function() {
          ctx.beginPath();
          ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
          ctx.closePath();
          ctx.fillStyle = this.color;
          ctx.fill();
        }
      };
      function draw() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ball.draw();
        // 添加速率
        ball.x += ball.vx;
        ball.y += ball.vy;
       window.requestAnimationFrame(draw);
      }
      window.requestAnimationFrame(draw);
      ball.draw();
    }
  </script>
</body>
</html>
<!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>canvas - 裁剪</title>
  <style>
    /* 给画布增加一个阴影和圆角的样式 */
    canvas {
      box-shadow: 0px 0px 5px #ccc;
      border-radius: 8px;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width="500" height="500">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
  <script>
    // 获取 canvas 元素
    var canvas = document.getElementById('canvas');
    // 通过判断getContext方法是否存在来判断浏览器的支持性
    if(canvas.getContext) {
      // 获取绘图上下文
      var ctx = canvas.getContext('2d');
      var ball = {
        x: 100,
        y: 100,
        vx: 1,
        vy: 3,
        radius: 25,
        color: 'blue',
        draw: function() {
          ctx.beginPath();
          ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
          ctx.closePath();
          ctx.fillStyle = this.color;
          ctx.fill();
        }
      };
      function draw() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ball.draw();
        // 添加速率
        ball.x += ball.vx;
        ball.y += ball.vy;
       window.requestAnimationFrame(draw);
      }
      window.requestAnimationFrame(draw);
      ball.draw();
    }
  </script>
</body>
</html>

边界

想让小球反弹那么我们就需要添加边界

<!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>canvas - 裁剪</title>
  <style>
    /* 给画布增加一个阴影和圆角的样式 */
    canvas {
      box-shadow: 0px 0px 5px #ccc;
      border-radius: 8px;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width="500" height="500">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
  <script>
    // 获取 canvas 元素
    var canvas = document.getElementById('canvas');
    // 通过判断getContext方法是否存在来判断浏览器的支持性
    if(canvas.getContext) {
      // 获取绘图上下文
      var ctx = canvas.getContext('2d');
      var ball = {
        x: 100,
        y: 100,
        vx: 1,
        vy: 3,
        radius: 25,
        color: 'blue',
        draw: function() {
          ctx.beginPath();
          ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
          ctx.closePath();
          ctx.fillStyle = this.color;
          ctx.fill();
        }
      };
      function draw() {
        ctx.clearRect(0,0, canvas.width, canvas.height);
        ball.draw();
        // 添加速率
        ball.x += ball.vx;
        ball.y += ball.vy;
        // 添加边界
        if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
          ball.vy = -ball.vy;
        }
        if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
          ball.vx = -ball.vx;
        }
       window.requestAnimationFrame(draw);
      }
      window.requestAnimationFrame(draw);
      ball.draw();
    }
  </script>
</body>
</html>
<!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>canvas - 裁剪</title>
  <style>
    /* 给画布增加一个阴影和圆角的样式 */
    canvas {
      box-shadow: 0px 0px 5px #ccc;
      border-radius: 8px;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width="500" height="500">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
  <script>
    // 获取 canvas 元素
    var canvas = document.getElementById('canvas');
    // 通过判断getContext方法是否存在来判断浏览器的支持性
    if(canvas.getContext) {
      // 获取绘图上下文
      var ctx = canvas.getContext('2d');
      var ball = {
        x: 100,
        y: 100,
        vx: 1,
        vy: 3,
        radius: 25,
        color: 'blue',
        draw: function() {
          ctx.beginPath();
          ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
          ctx.closePath();
          ctx.fillStyle = this.color;
          ctx.fill();
        }
      };
      function draw() {
        ctx.clearRect(0,0, canvas.width, canvas.height);
        ball.draw();
        // 添加速率
        ball.x += ball.vx;
        ball.y += ball.vy;
        // 添加边界
        if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
          ball.vy = -ball.vy;
        }
        if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
          ball.vx = -ball.vx;
        }
       window.requestAnimationFrame(draw);
      }
      window.requestAnimationFrame(draw);
      ball.draw();
    }
  </script>
</body>
</html>

加速度

为了让动作更真实,我们还需要加入加速度的处理。

<!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>canvas - 裁剪</title>
  <style>
    /* 给画布增加一个阴影和圆角的样式 */
    canvas {
      box-shadow: 0px 0px 5px #ccc;
      border-radius: 8px;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width="500" height="500">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
  <script>
    // 获取 canvas 元素
    var canvas = document.getElementById('canvas');
    // 通过判断getContext方法是否存在来判断浏览器的支持性
    if(canvas.getContext) {
      // 获取绘图上下文
      var ctx = canvas.getContext('2d');
      var ball = {
        x: 100,
        y: 100,
        vx: 1,
        vy: 3,
        radius: 25,
        color: 'blue',
        draw: function() {
          ctx.beginPath();
          ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
          ctx.closePath();
          ctx.fillStyle = this.color;
          ctx.fill();
        }
      };
      function draw() {
        ctx.clearRect(0,0, canvas.width, canvas.height);
        ball.draw();
        // 添加加速度
        ball.vy *= .99;
        ball.vy += .25;
        // 添加速率
        ball.x += ball.vx;
        ball.y += ball.vy;
        // 添加边界
        if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
          ball.vy = -ball.vy;
        }
        if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
          ball.vx = -ball.vx;
        }
       window.requestAnimationFrame(draw);
      }
      window.requestAnimationFrame(draw);
      ball.draw();
    }
  </script>
</body>
</html>
<!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>canvas - 裁剪</title>
  <style>
    /* 给画布增加一个阴影和圆角的样式 */
    canvas {
      box-shadow: 0px 0px 5px #ccc;
      border-radius: 8px;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width="500" height="500">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
  <script>
    // 获取 canvas 元素
    var canvas = document.getElementById('canvas');
    // 通过判断getContext方法是否存在来判断浏览器的支持性
    if(canvas.getContext) {
      // 获取绘图上下文
      var ctx = canvas.getContext('2d');
      var ball = {
        x: 100,
        y: 100,
        vx: 1,
        vy: 3,
        radius: 25,
        color: 'blue',
        draw: function() {
          ctx.beginPath();
          ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
          ctx.closePath();
          ctx.fillStyle = this.color;
          ctx.fill();
        }
      };
      function draw() {
        ctx.clearRect(0,0, canvas.width, canvas.height);
        ball.draw();
        // 添加加速度
        ball.vy *= .99;
        ball.vy += .25;
        // 添加速率
        ball.x += ball.vx;
        ball.y += ball.vy;
        // 添加边界
        if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
          ball.vy = -ball.vy;
        }
        if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
          ball.vx = -ball.vx;
        }
       window.requestAnimationFrame(draw);
      }
      window.requestAnimationFrame(draw);
      ball.draw();
    }
  </script>
</body>
</html>

拖尾效果

加一个拖尾效果:

<!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>canvas - 裁剪</title>
  <style>
    /* 给画布增加一个阴影和圆角的样式 */
    canvas {
      box-shadow: 0px 0px 5px #ccc;
      border-radius: 8px;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width="500" height="500">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
  <script>
    // 获取 canvas 元素
    var canvas = document.getElementById('canvas');
    // 通过判断getContext方法是否存在来判断浏览器的支持性
    if(canvas.getContext) {
      // 获取绘图上下文
      var ctx = canvas.getContext('2d');
      var ball = {
        x: 100,
        y: 100,
        vx: 1,
        vy: 3,
        radius: 25,
        color: 'blue',
        draw: function() {
          ctx.beginPath();
          ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
          ctx.closePath();
          ctx.fillStyle = this.color;
          ctx.fill();
        }
      };
      function draw() {
        // ctx.clearRect(0, 0, canvas.width, canvas.height);
        // 用带透明度的矩形代替清空
        ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ball.draw();
        // 添加加速度
        ball.vy *= .995;
        ball.vy += .15;
        // 添加速率
        ball.x += ball.vx;
        ball.y += ball.vy;
        // 添加边界
        if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
          ball.vy = -ball.vy;
        }
        if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
          ball.vx = -ball.vx;
        }
       window.requestAnimationFrame(draw);
      }
      window.requestAnimationFrame(draw);
      ball.draw();
    }
  </script>
</body>
</html>
<!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>canvas - 裁剪</title>
  <style>
    /* 给画布增加一个阴影和圆角的样式 */
    canvas {
      box-shadow: 0px 0px 5px #ccc;
      border-radius: 8px;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width="500" height="500">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
  <script>
    // 获取 canvas 元素
    var canvas = document.getElementById('canvas');
    // 通过判断getContext方法是否存在来判断浏览器的支持性
    if(canvas.getContext) {
      // 获取绘图上下文
      var ctx = canvas.getContext('2d');
      var ball = {
        x: 100,
        y: 100,
        vx: 1,
        vy: 3,
        radius: 25,
        color: 'blue',
        draw: function() {
          ctx.beginPath();
          ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
          ctx.closePath();
          ctx.fillStyle = this.color;
          ctx.fill();
        }
      };
      function draw() {
        // ctx.clearRect(0, 0, canvas.width, canvas.height);
        // 用带透明度的矩形代替清空
        ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ball.draw();
        // 添加加速度
        ball.vy *= .995;
        ball.vy += .15;
        // 添加速率
        ball.x += ball.vx;
        ball.y += ball.vy;
        // 添加边界
        if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
          ball.vy = -ball.vy;
        }
        if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
          ball.vx = -ball.vx;
        }
       window.requestAnimationFrame(draw);
      }
      window.requestAnimationFrame(draw);
      ball.draw();
    }
  </script>
</body>
</html>

在taro中使用canvas

最近在写taro的项目时用到了canvas在其中画一个添加的样式,在taro中使用总体上与微信小程序一致。很简单的画了两条线。

import { View, Canvas } from "@tarojs/components";
import Taro from "@tarojs/taro";
import { useEffect, useState } from "react";
import styles from "./index.module.css";

const HFloatingButton = () => {
  const [outStartCoordinate, setOutStartCoordinate] = useState([0, 0]); // 移动前的位置
  const [outMoveCoordinate, setOutMoveCoordinate] = useState([330, 570]); // 移动后的位置
  const [constraintSize, setConstraintSize] = useState<Array<number>>([]); // 可视区的范围

  // 获取可视区的范围
  useEffect(() => {
    const res = Taro.getSystemInfoSync();
    setConstraintSize([res.windowWidth, res.windowHeight]);

    const ctx = Taro.createCanvasContext("myCanvas"); //此处开始绘画
    ctx.moveTo(7, 22.5);
    ctx.lineTo(37, 22.5);
    ctx.setLineWidth(2.5);
    ctx.moveTo(22.5, 7);
    ctx.lineTo(22.5, 37);
    ctx.setStrokeStyle("white");
    ctx.stroke();
    ctx.draw();
  }, []);

  // 判断是否超出边界
  const compareIsBeyond = (x: number, y: number) => {
    if (x >= constraintSize[0] - 45) {
      x = constraintSize[0] - 45;
    } else if (x < 45) {
      x = 0;
    } else {
      x -= 22.5;
    }

    if (y >= constraintSize[1] - 45) {
      y = constraintSize[0] - 45;
    } else if (y < 45) {
      y = 0;
    } else {
      y -= 22.5;
    }
    return [x, y];
  };

  return (
    <View
      className={styles["out-circle"]}
      style={{
        width: "45px",
        height: "45px",
        left: `${outMoveCoordinate[0]}px`,
        top: `${outMoveCoordinate[1]}px`
      }}
      onTouchStart={e => {
        const { clientX, clientY } = e.changedTouches[0];
        setOutStartCoordinate([clientX, clientY]);
      }}
      onTouchMove={e => {
        e.stopPropagation();
        let { clientX, clientY } = e.changedTouches[0];
        let res = compareIsBeyond(clientX, clientY);
        setOutMoveCoordinate(res);
      }}
    >
      <>
        {" "}
        <Canvas
          canvasId="myCanvas"
          id="myCanvas"
          style={{ width: "45px", height: "45px" }}
        ></Canvas>
      </>
    </View>
  );
};
import { View, Canvas } from "@tarojs/components";
import Taro from "@tarojs/taro";
import { useEffect, useState } from "react";
import styles from "./index.module.css";

const HFloatingButton = () => {
  const [outStartCoordinate, setOutStartCoordinate] = useState([0, 0]); // 移动前的位置
  const [outMoveCoordinate, setOutMoveCoordinate] = useState([330, 570]); // 移动后的位置
  const [constraintSize, setConstraintSize] = useState<Array<number>>([]); // 可视区的范围

  // 获取可视区的范围
  useEffect(() => {
    const res = Taro.getSystemInfoSync();
    setConstraintSize([res.windowWidth, res.windowHeight]);

    const ctx = Taro.createCanvasContext("myCanvas"); //此处开始绘画
    ctx.moveTo(7, 22.5);
    ctx.lineTo(37, 22.5);
    ctx.setLineWidth(2.5);
    ctx.moveTo(22.5, 7);
    ctx.lineTo(22.5, 37);
    ctx.setStrokeStyle("white");
    ctx.stroke();
    ctx.draw();
  }, []);

  // 判断是否超出边界
  const compareIsBeyond = (x: number, y: number) => {
    if (x >= constraintSize[0] - 45) {
      x = constraintSize[0] - 45;
    } else if (x < 45) {
      x = 0;
    } else {
      x -= 22.5;
    }

    if (y >= constraintSize[1] - 45) {
      y = constraintSize[0] - 45;
    } else if (y < 45) {
      y = 0;
    } else {
      y -= 22.5;
    }
    return [x, y];
  };

  return (
    <View
      className={styles["out-circle"]}
      style={{
        width: "45px",
        height: "45px",
        left: `${outMoveCoordinate[0]}px`,
        top: `${outMoveCoordinate[1]}px`
      }}
      onTouchStart={e => {
        const { clientX, clientY } = e.changedTouches[0];
        setOutStartCoordinate([clientX, clientY]);
      }}
      onTouchMove={e => {
        e.stopPropagation();
        let { clientX, clientY } = e.changedTouches[0];
        let res = compareIsBeyond(clientX, clientY);
        setOutMoveCoordinate(res);
      }}
    >
      <>
        {" "}
        <Canvas
          canvasId="myCanvas"
          id="myCanvas"
          style={{ width: "45px", height: "45px" }}
        ></Canvas>
      </>
    </View>
  );
};
.out-circle {
    border-radius: 50%;
    background-color: #5956E9;
    position: fixed;
}
.out-circle {
    border-radius: 50%;
    background-color: #5956E9;
    position: fixed;
}

(参考文档| Taro 文档 )

CC BY-NC-SA 4.0 2021-PRESENT © Bohua Xu