探索HTML5 Canvas:解锁JavaScript中的动态图形绘制技术

1,336 阅读6分钟

引言

你有时候是不是觉得验证码的效果看起高大上,过于复杂的?这样的页面效果是不是需要花费很多功夫才能完成,其实通过本章的学习就会发现不过尔尔,这只是一个简单的页面布局罢了,展示一下效果:

不断地点击一样可以一直更换。

Convas

JavaScript中的Canvas功能强大且多样化,它不仅限于基础的图形绘制,还涵盖了图像处理、动画创建以及高级交互性功能。以下是Canvas的一些核心功能:

  1. 图形绘制

    • 支持绘制点、线、圆形、矩形、椭圆、弧线等多种基本图形。
    • 可以设置线条的粗细、颜色、线帽样式、虚线模式等。
    • 支持填充图形,包括纯色填充、渐变填充、图案填充等。
  2. 图像处理

    • 能够加载并绘制外部图片到Canvas上。
    • 支持图像的缩放、裁剪、旋转等操作。
    • 可以操作像素数据,实现滤镜效果,如灰度、模糊、锐化等。
  3. 文本渲染

    • 支持在Canvas上绘制文本,包括设置字体、大小、颜色和对齐方式。
    • 可以测量文本宽度,以便精确布局。
  4. 路径操作

    • 强大的路径功能,可以创建和操作复杂路径,包括贝塞尔曲线等。
    • 支持闭合路径,以及路径的填充和描边。
  5. 变换操作

    • 提供了平移、缩放、旋转和倾斜等变换功能,可以组合使用,实现复杂变换效果。
  6. 复合操作

    • 支持全局Alpha设置,即整体透明度控制。
    • 可以设置剪切路径,控制绘制区域。
  7. 动画与交互

    • 利用requestAnimationFrame可以创建流畅的动画效果。
    • 通过监听鼠标和键盘事件,实现与用户的交互,如拖拽、缩放等。
  8. 数据可视化

    • 适用于创建图表、仪表盘等数据可视化内容,结合JavaScript的数据处理能力,可以灵活展示各种数据。
  9. 导出图像

    • 可以将Canvas上的内容导出为图片(如PNG、JPEG格式),或者转换成SVG格式,便于保存或分享。
  10. 集成库支持

    • 有许多强大的JavaScript库(如Fabric.js、Konva.js、EaselJS、Paper.js等)建立在Canvas之上,提供了更高级的抽象层和便利的API,简化复杂图形和动画的创建过程。

通过这些功能,Canvas成为Web开发中实现创意视觉效果和动态用户界面的重要工具。以下我主要讲解三个实例来运用这些功能:

1. 圆形的绘画

canvas最基本的步骤:

let canvas = document.getElementById("canvas")

let ctx = canvas.getContext("2d")

  1. 获取canvas这个点击事件

  2. 获取nvas元素的2D渲染上下文,'2d'指代二维绘图上下文。

  3. 填充矩形: ctx.fillRect(0,0,100,100) 这行代码在canvas上绘制了一个填充的矩形。fillRect(x, y, width, height)方法接受四个参数,分别代表矩形的左上角坐标(x, y)以及矩形的宽度和高度。在这个例子中,矩形的左上角位于(0, 0),宽高都是100px,默认使用的是上下文的当前填充色(如果未指定,默认是黑色)。

  4. 设置描边颜色: ctx.strokeStyle = 'red' 这行代码设置了接下来要绘制图形的描边颜色为红色。strokeStyle属性控制了所有后续描边操作的颜色。

  5. 描边矩形: ctx.strokeRect(0,0,100,100) 最后这行代码在同样的位置(0, 0)绘制了一个宽度和高度都为100px的矩形的边框,颜色为之前设置的红色。strokeRect()方法只绘制边框,不填充内部。

效果是在canvas上首先绘制了一个100x100像素的填充矩形,然后在同一位置绘制了一个相同尺寸但只有边框的红色矩形,形成了一个带有红色边框的正方形。

        ctx.fillRect(0,0,100,100)
        ctx.strokeStyle = 'red' //
        ctx.strokeRect(0,0,100,100) // 描边

image.png

达到预期的效果。

再画一个圆形并且填充颜色:

 ctx.beginPath()     //多边形的描绘,开始一个路径的描述
        ctx.arc(150,75,50,0,2 * Math.PI)
        ctx.fillStyle = 'green'
        ctx.fill()

image.png

  • 开始路径: ctx.beginPath() 这行代码表示开始一个新的路径描述。在Canvas中,当你想要绘制独立的形状时(避免它们自动连接),通常会在绘制前调用此方法。这样可以确保新图形的起点不会与前一个图形的终点相连。

  • 绘制圆形: ctx.arc(150, 75, 50, 0, 2 * Math.PI) 这行代码用于绘制一个圆形。arc(x, y, radius, startAngle, endAngle) 方法用于创建弧线(用于圆形、部分圆等)。在这个例子中:

    • x 和 y 分别是圆心的坐标,即 (150, 75)
    • radius 是圆的半径,这里是 50 像素。
    • startAngle 和 endAngle 定义了圆弧的起始和结束位置,以弧度为单位。0 表示从三点钟方向开始,而 2 * Math.PI 表示完整一圈,因此会绘制一个完整的圆。
  • 设置填充颜色: ctx.fillStyle = 'green' 这行代码设置了即将填充图形的颜色为绿色。

  • 填充图形: ctx.fill() 调用 fill() 方法后,根据之前设置的填充颜色(这里是绿色),填充当前路径描述的内部区域,即刚刚定义的圆形。

到这里想一想镂空的圆形该怎么形成?

 ctx.beginPath()
        ctx.arc(150,75,50,0,2 * Math.PI)
        ctx.strokeStyle = 'blue'
        ctx.lineWidth = 6
        ctx.stroke()

image.png

只要注意下格式的更改,绘制圆形的轮廓和宽度即可。

2. 圆形倒计时:

这里主要可以分为三个部分:绘制圆环,数值的确定,以及绘画的运行。

  1. 圆环的制定:
function blueCircle(n) {
        ctx.save()
        ctx.strokeStyle = '#fff'
        ctx.lineWidth = 5
        ctx.beginPath()
        ctx.arc(centerX,centerY,100,-Math.PI / 2,-Math.PI /2 + n * rad)
        ctx.stroke()
        ctx.closePath() // 结束路径
        ctx.restore()
    }

这里注意一下有Begin的开始,就添加一个Close的结束这样整体的效果会更好。

  1. 计算数值:
function text(n) {
        ctx.save() //
        ctx.strokeStyle = '#fff'
        ctx.font = '40px Arial'
        ctx.strokeText(n.toFixed(0) + '%',centerX - 25,centerY + 10)
        // ctx.stroke()
        ctx.restore() // 跟save一样
    }

这保证接下来的样式只生效于这一段。

  1. Draw的绘画
(function draw() {
            requestAnimationFrame(draw)
            ctx.clearRect(0, 0, canvas.width, canvas.height)

            text(speed)
            blueCircle(speed)
            if (speed >= 100) {
                speed = 100
            }
            speed += 0.1
        })()

这里的定时器除了基本的两种:setTimeout()setTimeout(),还可以使用requestAnimationFrame()来执行。

基本的效果可以达成:

3. 验证码

其实验证码的写法也是类似,这里就不多叙述了,大家可以看看源码更正一下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <canvas id="canvas" width="120" height="40" onclick="draw()"></canvas>



    <script>
        let pool = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'

        function randomNum(min, max) {
            return Math.floor(Math.random() * (max - min) + min)
        }

        function randomColor(min, max) {
            const r = randomNum(min, max)
            const g = randomNum(min, max)
            const b = randomNum(min, max)
            return `rgb(${r},${g},${b})`
        }


        function draw() {
            let canvas = document.getElementById("canvas");
            let ctx = canvas.getContext("2d");
            // 填充色 随机
            ctx.fillStyle = randomColor(100, 230)
            ctx.fillRect(0, 0, canvas.width, canvas.height)
            // 随机生成字符串
            let imgCode = ''
            for (let i = 0; i < 4; i++) {
                const text = pool[randomNum(0, pool.length)]
                imgCode += text
                // 随机字体大小
                const fontSize = randomNum(18, 40)
                // 随机旋转角度
                const deg = randomNum(-30, 30)
                ctx.font = `${fontSize}px Simhei`
                ctx.textBaseline = 'top'
                ctx.fillStyle = randomColor(80, 150)
                ctx.save()  // 将当前状态封存入栈
                ctx.translate(30 * i + 15, 15)
                ctx.rotate((deg * Math.PI) / 180)
                ctx.fillText(text, -10, -15)
                ctx.restore() //

            }
            // 随机生成干扰线条
            for (let i = 0; i < 5; i++) {
                ctx.beginPath()
                ctx.moveTo(randomNum(0, canvas.width), randomNum(0, canvas.height))
                ctx.lineTo(randomNum(0, canvas.width), randomNum(0, canvas.height))
                ctx.strokeStyle = randomColor(120,230)
                ctx.closePath()
                ctx.stroke()
            }
            // 随机小点
            for(let i = 0; i < 40; i++){
                ctx.beginPath()
                ctx.arc(randomNum(0,canvas.width),randomNum(0,canvas.height),1,0,2 * Math.PI)
                ctx.fillStyle = randomColor(150,200)
                ctx.closePath()
                ctx.fill()

            }
        }

        draw()
    </script>
</body>

</html>

基本的效果就是最开始的情况。

总结:

其实Convas的编写远不止于此,任何(evey)的效果美化,只要你想并且去写,就能够跑起来,甚至居左《蒙娜丽莎》也是可以的!

image.png