当前位置: 首页 > news >正文

10分钟Canvas从入门到实践

我相信有很多小伙伴和我一样,在面试的时候面试官总喜欢问一个问题就是:让你讲讲HTML5新增的标签,而<canvas>就是其中一个非常重要的标签,但是部分小伙伴可能一开始和我一样回答得不太完整或者回答不出来,接下来让我花10分钟带你从入门到实践讲讲一些常用的知识点。

首先一句话先总结,什么是canvas?canvas顾名思义是画布、画板的意思,所以它是一个画画的载体,也可以理解成是一个画画的容器,那要如何实现在画板上进行画画的操作呢?答案就是通过我们的JS来实现,所以一句话总结:<canvas> 标签只是图形容器,必须使用脚本来绘制图形。

我们先通过一个简单的例子来看一下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>Document</title>
</head>
<body>
    <canvas id="mycanvas" width="200" height="200" style="border:1px solid black"></canvas>

    <script>
            //获取Canvas元素对应的DOM对象
            var canvas = document.getElementById('mycanvas'); 
            //获取Canvas上的绘图的CanvasRenderingContext2D对象
            var ctx = canvas.getContext('2d');
            //设置笔触线条的宽度
            ctx.lineWidth=3; 
            //将画布的中间点(100,100)设置为原点
            ctx.translate(100,100); 
            //保存当前画布的状态,该状态包含了lineWidth=3,translate(100,100),然后其他那些属性为默认值.
            ctx.save();
            //设置线条颜色为红色
            ctx.strokeStyle='red'; 
            //坐标系统旋转90°
            ctx.rotate(Math.PI/2);  
            //画第一条直线
            //因为画笔是默认连续的,我们必须将我们每次画完的图形写在beginPath和closePath里面
            ctx.beginPath();
            ctx.moveTo(-100,0);
            ctx.lineTo(100,0);
            ctx.closePath();
            //勾勒线条
            ctx.stroke();
            //恢复之前保存的绘图状态,也就是旋转90°之前的坐标系统
            ctx.restore(); 
            //再画第二条直线
            ctx.beginPath();
            ctx.moveTo(-100,0);
            ctx.lineTo(100,0);
            ctx.closePath();
            //勾勒线条
            ctx.stroke();
    </script>
</body>
</html>

相信在这个例子中大部分小伙伴都是一看就能够理解的,有点疑问的可能是对于Canvas的save()和restore()这两个方法,save()就是保存当前画布坐标系的一切配置的方法,我们可以理解成新复制一份画布然后对它进行旋转等操作,画完后restore()就是把刚才画好的图和保存前的状态图合并,就以上面的作为例子,请看下图:
在这里插入图片描述
然后在旋转后的画布上画出从 (-100,0)到(100,0)的一条红色直线,效果就是:
在这里插入图片描述
接着restore(),就是将保存前画布的状态和这个画完后的状态进行合并,注意此时画布又回到一开始保存前的坐标系,所以合并后的效果就是:

在这里插入图片描述
接着在此时的这个坐标系再画一条从(-100,0)到(100,0)的黑色直线也就是:
在这里插入图片描述
接着po上前端大全公众号给的俩个实战例子,大家学会了之后再拿这俩个小项目练练手估计对canvas的掌握就掌握得7788了。

案例1:用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>Document</title>
</head>
<body>
    
    <canvas id="mycanvas" width="600px" height="600px"></canvas>
    <script>
        const canvas = document.getElementById('mycanvas')
        const ctx = canvas.getContext('2d')

        setInterval(() => {
            ctx.save()
            ctx.clearRect(0, 0, 600, 600)
            // 设置中心点,此时300,300变成了坐标的0,0,坐标x右边为正,坐标y下边为正
            ctx.translate(300, 300) 
            ctx.save()

            // 画大圆
            ctx.beginPath()
            // 画圆线使用arc(中心点X,中心点Y,半径,起始角度,结束角度)
            ctx.arc(0, 0, 100, 0, 2 * Math.PI)
            // 执行画线段的操作
            ctx.stroke() 
            ctx.closePath()

            // 画小圆
            ctx.beginPath()
            ctx.arc(0, 0, 5, 0, 2 * Math.PI)
            ctx.stroke()
            ctx.closePath()

            // 获取当前时,分,秒
            let time = new Date()
            let hour = time.getHours() % 12
            let min = time.getMinutes()
            let sec = time.getSeconds()

            // 时针
            ctx.rotate(2 * Math.PI / 12 * hour + 2 * Math.PI / 12 * (min / 60))
            ctx.beginPath()
            // moveTo设置画线起点
            ctx.moveTo(0, 0)
            // lineTo设置画线经过点
            ctx.lineTo(0, -40)
            // 设置线宽
            ctx.lineWidth = 10
            ctx.stroke()
            ctx.closePath()
            ctx.restore()
            ctx.save()

            // 分针
            ctx.rotate(2 * Math.PI / 60 * min + 2 * Math.PI / 60 * (sec / 60))
            ctx.beginPath()
            ctx.moveTo(0, 0)
            ctx.lineTo(0, -60)
            ctx.lineWidth = 5
            ctx.strokeStyle = 'blue'
            ctx.stroke()
            ctx.closePath()
            ctx.restore()
            ctx.save()

            //秒针
            ctx.rotate(2 * Math.PI / 60 * sec )
            ctx.beginPath()
            ctx.moveTo(0, 0)
            ctx.lineTo(0, -80)
            ctx.strokeStyle = 'red'
            ctx.stroke()
            ctx.closePath()
            ctx.restore()
            ctx.save()

            // 绘制刻度,也是跟绘制时分秒针一样,只不过刻度是死的
            ctx.lineWidth = 1
            for (let i = 0; i < 60; i++) {
                ctx.rotate(2 * Math.PI / 60)
                ctx.beginPath()
                ctx.moveTo(90, 0)
                ctx.lineTo(100, 0)
                // ctx.strokeStyle = 'red'
                ctx.stroke()
                ctx.closePath()
            }
            ctx.restore()
            ctx.save()
            ctx.lineWidth = 5
            for (let i = 0; i < 12; i++) {
                ctx.rotate(2 * Math.PI / 12)
                ctx.beginPath()
                ctx.moveTo(85, 0)
                ctx.lineTo(100, 0)
                // ctx.strokeStyle = 'red'
                ctx.stroke()
                ctx.closePath()
            }

            ctx.restore()
            ctx.restore()
        }, 1000)
    </script>
</body>
</html>

运行效果:
在这里插入图片描述
案例2:用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>Document</title>
    <style>
        .text {
            position: absolute;
            left: 130px;
            top: 35px;
            z-index: -1;
        }
    </style>
</head>
<body>

    <canvas id="canvas" width="400" height="100"></canvas>
    <div class="text">恭喜您获得100w</div>
    
    <script>
        const canvas = document.getElementById('canvas')
        const ctx = canvas.getContext('2d')

        // 填充矩形的颜色
        ctx.fillStyle = 'darkgray'
        // 填充矩形 fillRect(起始X,起始Y,终点X,终点Y)
        ctx.fillRect(0, 0, 400, 100)
        // 绘制填充文字及颜色
        ctx.fillStyle = '#fff'
        ctx.fillText('刮刮卡', 180, 50)

        //设置isDraw的目的就是确保鼠标是真的按下去了,然后才开始刮
        let isDraw = false
        canvas.onmousedown = function () {
            isDraw = true
        }
        canvas.onmousemove = function (e) {
            if (!isDraw) return
            // 计算鼠标在canvas里的位置
            //e.pageX和e.pageY是鼠标相对于左上角浏览器的位置
            //canvas.offsetLeft和canvas.offsetTop是画布canvas相对于左上角原点的位置
            //所以获取鼠标在画布里的位置就是e.pageX-canvas.offsetLeft
            const x = e.pageX - canvas.offsetLeft
            const y = e.pageY - canvas.offsetTop
            // 设置globalCompositeOperation
            ctx.globalCompositeOperation = 'destination-out'
            // 画圆
            ctx.arc(x, y, 10, 0, 2 * Math.PI)
            // 填充圆形
            ctx.fill()
        }
        canvas.onmouseup = function () {
            isDraw = false
        }
    </script>
</body>
</html>

运行效果:
在这里插入图片描述
好了,相信看到这里的小伙伴们都应该大概掌握了canvas这个标签的运用,码字不易,喜欢的并且看完觉得有帮助的麻烦点个收藏加关注,这将是对我莫大的鼓励~

相关文章:

  • e.target与e.currentTarget的区别
  • 解决:微信小程序饼状图组件层级过高覆盖在日历选择器上
  • JS中数组splice方法使用需要注意的点
  • JS中for,for...in,for...of和forEach的用法和区别
  • JSON.parse和JSON.stringify的用法
  • 一文搞懂JS中变量作用域的那些事
  • JS给函数添加属性
  • 开发中常见的一些Bug
  • 一分钟搞懂JS函数提升与变量提升的优先级
  • ES5中原型、实例对象和构造函数的那些事
  • 我的创作纪念日
  • JS之手写bind原理
  • 关于解决电脑蓝屏C:\Windows\System32\Logfiles\Str\StrTrail.txt
  • 解决:flex布局之 flex-wrap:wrap 自动换行属性,导致上下两行div中间有空行
  • uni app 自定义 头部组件(1)禁用原生
  • (三)从jvm层面了解线程的启动和停止
  • Bootstrap JS插件Alert源码分析
  • classpath对获取配置文件的影响
  • gcc介绍及安装
  • Java读取Properties文件的六种方法
  • JS实现简单的MVC模式开发小游戏
  • nodejs:开发并发布一个nodejs包
  • springMvc学习笔记(2)
  • 多线程事务回滚
  • 可能是历史上最全的CC0版权可以免费商用的图片网站
  • 配置 PM2 实现代码自动发布
  • 前端每日实战 2018 年 7 月份项目汇总(共 29 个项目)
  • 推荐一款sublime text 3 支持JSX和es201x 代码格式化的插件
  • 2017年360最后一道编程题
  • CMake 入门1/5:基于阿里云 ECS搭建体验环境
  • 直播平台建设千万不要忘记流媒体服务器的存在 ...
  • 组复制官方翻译九、Group Replication Technical Details
  • #1015 : KMP算法
  • #DBA杂记1
  • #LLM入门|Prompt#1.7_文本拓展_Expanding
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • (DenseNet)Densely Connected Convolutional Networks--Gao Huang
  • (博弈 sg入门)kiki's game -- hdu -- 2147
  • (独孤九剑)--文件系统
  • (二)linux使用docker容器运行mysql
  • (三维重建学习)已有位姿放入colmap和3D Gaussian Splatting训练
  • (一)C语言之入门:使用Visual Studio Community 2022运行hello world
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • (原创)Stanford Machine Learning (by Andrew NG) --- (week 9) Anomaly DetectionRecommender Systems...
  • (转)LINQ之路
  • *p=a是把a的值赋给p,p=a是把a的地址赋给p。
  • .gitignore
  • .NET Core 版本不支持的问题
  • .NET Core引入性能分析引导优化
  • .NET 指南:抽象化实现的基类
  • .net6 webapi log4net完整配置使用流程
  • .net与java建立WebService再互相调用
  • [ vulhub漏洞复现篇 ] JBOSS AS 4.x以下反序列化远程代码执行漏洞CVE-2017-7504
  • [ 数据结构 - C++] AVL树原理及实现
  • [BIZ] - 1.金融交易系统特点