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

JavaScript全解析——canvas 入门(上)

●canvas 是 HTML5 新增的一个标签, 表示画布
●canvas 也是 h5 的画布技术, 我们通过代码的方式在画布上描绘一个图像

canvas 标签
●向进行 canvas 绘图, 首先我们先要了解到 canvas 标签
●是 html5 推出的一个标签

<html>
<head>...
</head>
<body><canvas></canvas>
</body>
</html>复制代码

○canvas 默认是一个行内块元素
○canvas 默认画布大小是 300 * 150
○canvas 默认没有边框, 背景默认为无色透明

canvas 画布大小
●我们在绘图之前, 先要确定一个画布的大小
○因为画布默认是按照比例调整
○所以我们调整宽度或者高度的时候, 调整一个, 另一个自然会按照比例自己调整
○我们也可以宽高一起调整
●调整画布大小有两种方案
○第一种 : 通过 css 样式 ( 不推荐 )

<html>
<head><style>canvas {width: 1000px;height: 500px;}</style>
</head>
<body><canvas></canvas>
</body>
</html>复制代码

○第二种 : 通过标签属性 ( 推荐 )

<html>
<head>...
</head>
<body><canvas width="1000" height="500"></canvas>
</body>
</html>复制代码

●两种方案的区别
○通过 css 样式的调整方案, 不推荐
是因为这个方案其实并没有设置了画布的大小
而是把原先 300 * 150 的画布, 将他的可视窗口变成了 1000 * 500
所以真实画布并没有放大, 只是可视程度变大了
举个例子 : 就是你把一个 300 * 150 的图片, 放大到 1000 * 500 的大小来看
所以这个方式我们及其不推荐

○通过属性的调整方案, 推荐
这个才是真正的调整画布的的大小
也就是我们会在一个 1000 * 500 的画布上进行绘制

●画布的坐标
○canvas 画布是和我们 css 的坐标系一样的
○从 canvas 的左上角为 0 0 左边, 分别向右向下延伸为正方向

canvas 初体验
●准备工作已经完成了, 我们可以开始体验一下绘制了
●其实 canvas 画布很简单, 就和我们 windows 电脑的画板工具是一样的道理

●思考 :
我们在 windows 这个画板上绘制内容的时候
我们一定是先选定一个工具 ( 画笔, 矩形, 圆形, ... )
设定好样式 ( 粗细, 颜色 )
然后开始绘制
●其实在 canvas 绘制也是一个道理
拿到一个画布工具箱
从工具箱中选定工具
设定样式
开始绘制
●初体验步骤
●index.html

<html>
<head>...
</head>
<body><canvas id="canvas" width="600" height="300"></canvas><script src="./index.js"></script>
</body>
</html>复制代码

●index.js

// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('#canvas')// 1. 获取当前这个画布的工具箱
// 语法: canvas 元素.getContext('2d')
const ctx = canvasEle.getContext('2d')// 2. 开始绘制
// 2-1. 讲画笔移动到一个指定位置开始下笔
// 语法: 工具箱.moveTo(x轴坐标, y轴坐标)
ctx.moveTo(100, 100)// 2-2. 将笔移动到一个指定位置, 画下一条轨迹
// 注意: 这里是没有显示的, 因为只是画了一个轨迹
// 语法: 工具箱.lineTo(x轴坐标, y轴坐标)
ctx.lineTo(300, 100)// 2-3. 设定本条线的样式
// 设定线的宽度
// 语法: 工具箱.lineWidth = 数字
ctx.lineWidth = 10
// 设定线的颜色
// 语法: 工具箱.strokeStyle = '颜色'
ctx.strokeStyle = '#000'// 2-4. 描边
// 把上边画下的痕迹按照设定好的样式描绘下来
// 语法: 工具箱.stroke()
ctx.stroke()复制代码

●至此我们的第一个线段就绘制完毕, 画布上就会出现一条线段
○从坐标 ( 100, 100 ) 绘制到坐标 ( 300, 100 )
○线段长度为 200px
○线段宽度为 10px
○线段颜色为 '#000' ( 黑色 )

canvas 线宽颜色问题
●刚才我们经过了初体验, 画了一个线段
●看似没有问题, 也出现了线段, 但是其实内在是有一些问题的
●我们先来观察
●这次我们再来画一个线段
○从坐标 ( 100, 100 ) 绘制到坐标 ( 300, 100 )
○线段长度为 200px
○线段宽度为 1px
○线段颜色为 '#000' ( 黑色 )

// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('#canvas')// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')// 2. 开始绘制
ctx.moveTo(100, 100)// 2-2. 将笔移动到一个指定位置, 画下一条轨迹
ctx.lineTo(300, 100)// 2-3. 设定本条线的样式
// 设定线的宽度
ctx.lineWidth = 10
// 设定线的颜色
ctx.strokeStyle = '#000'// 2-4. 描边
ctx.stroke()复制代码

●效果出现了, 没有什么问题
●只是看上去不太想 1px, 而且颜色有些浅
●不着急, 我们再来画一个线段
○从坐标 ( 100, 100 ) 绘制到坐标 ( 300, 100 )
○线段长度为 200px
○线段宽度为 2px
○线段颜色为 '#000' ( 黑色 )

●这个时候问题就出现了
○两次画出来的线段, 一次设置 1px 一次设置 2px
○感觉上 线宽度 一样
○两次画出来的线段, 两次都是设置为 '#000' 的颜色
○但是感觉上颜色不太一样

●这是因为浏览器在描述内容的时候, 最小的描述单位是 1px
●我们来模拟一下浏览器绘制的内容
○假设这是我们浏览器描述的画布中的像素点

○我们来做一个坐标的标记

○现在呢不关注线的长度和坐标, 我们就画一个宽度为 1px 的线段

○我们来剖析一下问题
因为在描绘这个线段的时候, 会把线段的最中心点放在这个像素点位上
也就是说, 在描述线宽的时候, 实际上会从 0.5px 的位置绘制到 1.5px 的位置
合计描述宽度为 1px
但是浏览器的最小描述为 1px
这里说的不是最小宽度为 1px, 是浏览器不能在非整数像素开始描述
也就是说浏览器没办法从 0.5 开始绘制, 也没有办法绘制到 1.5 停止
那么就只能是从 0 开始绘制到 2
所以线宽就会变成 2px 了
因为本身一个像素的黑色被强制拉伸到两个像素宽度, 所以颜色就会变浅
就像我们一杯墨水, 倒在一个杯子里面就是黑色
但是到在一个杯子里面的时候, 又倒进去一杯水, 颜色就会变浅
○实际描绘出来的样子

○这就变成了我们刚才看到的样子
●所以, 我们在进行 canvas 绘制内容的时候, 涉及到线段的时候
●我们一般不会把线段宽度设置成奇数, 一般都是偶数的

canvas 绘制平行线
●刚才我们绘制了线段, 接下来我们来绘制一个平行线, 也就是两个线段
●小伙伴: " 一个简单的效果, 想到就搞 "

// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('#canvas')// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')// 2. 开始绘制第一个线段
ctx.moveTo(100, 100)
ctx.lineTo(300, 100)
ctx.lineWidth = 2
ctx.strokeStyle = '#000'
ctx.stroke()// 3. 开始绘制第二个线段
ctx.moveTo(100, 100)
ctx.lineTo(300, 100)
ctx.lineWidth = 2
ctx.strokeStyle = '#000'
ctx.stroke()复制代码

●没有问题, 效果实现了

●接下来, 咱们稍微增加一下需求
○第一个线段线宽 2px, 黑色
○第二个线段线宽 10px, 红色
●这也简单啊, 稍微修改一下

// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('#canvas')// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')// 2. 开始绘制第一个线段
ctx.moveTo(100, 100)
ctx.lineTo(300, 100)
ctx.lineWidth = 2
ctx.strokeStyle = '#000'
ctx.stroke()// 3. 开始绘制第二个线段
ctx.moveTo(100, 100)
ctx.lineTo(300, 100)
ctx.lineWidth = 10
ctx.strokeStyle = 'red'
ctx.stroke()复制代码

●这是什么鬼, 为什么两个线段都变了, 不是应该只改变一个吗 ?
这是因为我们并没有告诉他这是两个不一样的线段
所以在设置线段样式的时候, 会默认按照最后一次设置的样式来绘制所有的线段
我们要想让第一个线段绘制完毕以后, 和第二个没有关系
我们需要告诉画布, 我的这个线段结束了, 后面的不要和我扯上关系

// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('#canvas')// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')// 2. 开始绘制第一个线段
ctx.moveTo(100, 100)
ctx.lineTo(300, 100)
ctx.lineWidth = 2
ctx.strokeStyle = '#000'
ctx.stroke()// 3. 结束之前的绘制内容
// 语法: 工具箱.beginPath()
ctx.beginPath()// 4.. 开始绘制第二个线段
ctx.moveTo(100, 100)
ctx.lineTo(300, 100)
ctx.lineWidth = 10
ctx.strokeStyle = 'red'
ctx.stroke()复制代码

●这样才是我们的需求

canvas 绘制三角形
●画完了线段, 咱们就来画一个简单的图形, 画一个三角形
●其实就是由三个线段组成, 用三个线段围成一个封闭图形即可

// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('#canvas')// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')// 2. 开始绘制第一个线段
// 三角形第一个点
ctx.moveTo(100, 100)
// 三角形第二个点
ctx.lineTo(200, 100)
// 三角形第三个点
ctx.lineTo(200, 200)
// 回到第一个点
ctx.lineTo(100, 100)
ctx.lineWidth = 2
ctx.strokeStyle = '#000'
// 描边
ctx.stroke()复制代码

●看似没啥问题, 一个三角形就出来了
●但是我们仔细观察一下三角形的第一个角

●因为这是两个线段, 只是画到了一个点, 不可能重叠出一个 尖儿~~
●这个时候, 我们就不能这样绘制三角形了
当我们要绘制闭合图形的时候
我们不要手动绘制最后一个路径, 而是描述出形状
通过 canvas 让他自动闭合
●首先, 我们绘制出形状, 不要闭合最终路径

// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('#canvas')// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')// 2. 开始绘制第一个线段
// 三角形第一个点
ctx.moveTo(100, 100)
// 三角形第二个点
ctx.lineTo(200, 100)
// 三角形第三个点
ctx.lineTo(200, 200)ctx.lineWidth = 2
ctx.strokeStyle = '#000'
// 描边
ctx.stroke()复制代码

●接下来, 让 canvas 来帮我们闭合这个封闭图形

// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('#canvas')// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')// 2. 开始绘制第一个线段
ctx.moveTo(100, 100)
ctx.lineTo(200, 100)
ctx.lineTo(200, 200)// 自动闭合图形
// 语法: 工具箱.closePath()
ctx.closePath()ctx.lineWidth = 2
ctx.strokeStyle = '#000'
// 描边
ctx.stroke()复制代码

●这个时候, 我们发现一个正常的三角形就出现了
●注意 : 闭合路径
closePath() 这个方法
是从当前坐标点, 直接用线段的方式连接到 modeTo() 的位置
也就是从当前坐标点直接连接到开始坐标点

相关文章:

JavaScript全解析——canvas 入门(上)

●canvas 是 HTML5 新增的一个标签, 表示画布 ●canvas 也是 h5 的画布技术, 我们通过代码的方式在画布上描绘一个图像 canvas 标签 ●向进行 canvas 绘图, 首先我们先要了解到 canvas 标签 ●是 html5 推出的一个标签 <html> <head>... </head> <body&…...

vue 插槽的用法

Vue的插槽&#xff08;Slot&#xff09;是一种可以让父组件向子组件传递内容的机制。插槽可以让开发者将组件的结构和内容分离开来&#xff0c;从而实现更好的可维护性和复用性。 在Vue中&#xff0c;插槽通过 标签实现。具体用法如下&#xff1a; 单个插槽 在子组件中使用一…...

【C++复习2】C++编译器的工作原理

如果你是一名newbird的话&#xff0c;建议观看如下视频加深你的理解&#xff0c;再看如下内容&#xff1a; https://www.bilibili.com/video/BV1N24y1B7nQ?p7 The cherno会额外告诉你如何将目标文件转换成汇编代码&#xff0c;CPU执行指令的过程以及编译器如何通过删除冗余变…...

回调函数_回顾

函数指针和回调函数 函数指针是指向函数的指针变量。 通常我们说的指针变量是指向一个整型、字符型或数组等变量&#xff0c;而函数指针是指向函数。 函数指针可以像一般函数一样&#xff0c;用于调用函数、传递参数。 直接用指针表示函数&#xff0c;而不是*指针&#xff…...

今天面了个字节跳动拿35K出来的,真是砂纸擦屁股,给我露了一手啊

今年的金三银四已经结束&#xff0c;很多小伙伴收获不错&#xff0c;拿到了心仪的 offer。 各大论坛和社区里也看见不少小伙伴慷慨地分享了常见的面试题和八股文&#xff0c;为此咱这里也统一做一次大整理和大归类&#xff0c;这也算是划重点了。 俗话说得好&#xff0c;他山之…...

8. 数据结构与算法

8. 数据结构与算法 常见面试题 说说一个算法有哪些时间复杂度?归并算法时间复杂度是多少?⭐⭐⭐ O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) 归并算法时间复杂度是O(nlogn) 说说数组时间复杂度,什么场景下使用?⭐⭐⭐⭐⭐ 从渐进趋势…...

Springboot +Flowable,任务认领和回退(一)

一.简介 有的时候&#xff0c;一个任务节点会存在多个候选人&#xff0c;例如&#xff1a;张三提交一个任务&#xff0c;这个任务即可以由李四处理&#xff0c;又可以由王五处理&#xff0c;那么针对这种多个任务候选人的情况&#xff0c;该如何处理&#xff1f; 二.绘制流程…...

机器学习 | MATLAB实现CNN-LSTM卷积长短期记忆神经网络模型答疑

机器学习 | MATLAB实现CNN-LSTM卷积长短期记忆神经网络模型答疑 目录 机器学习 | MATLAB实现CNN-LSTM卷积长短期记忆神经网络模型答疑问题汇总问题一答疑问题二答疑问题三答疑问题四答疑问题五答疑参考资料问题汇总 有几个问题咨询一下: 1.数据集划分时出现了问题如何解决? 2…...

卷首语:我决定从零开始,用C++手写自己的键值数据库

2023年5月1日&#xff0c;周一下午。 我为什么要自己写一个&#xff1f; 今天中午&#xff0c;我在CSDN上提出了一个关于如何学习C的观点——通过用C造东西来学习C&#xff0c;于是为了实践这个观点&#xff0c;我决定用C手写一个简单的属于自己的键值数据库。 我会把这个键值…...

【Vue】收集表单数据 过滤器

收集表单数据 收集表单数据&#xff1a; 若&#xff1a;<input type"text"/>,则v-model收集的是value值&#xff0c;用户输入的就是value值若&#xff1a;<input type"radio"/>,则v-model收集的是value值&#xff0c;且要给标签配置value值若…...

Linux线程:死锁

1. 死锁 &#xff08;1&#xff09;概念 死锁&#xff08;DeadLock&#xff09;指两个或两个以上的进程或线程执行时&#xff0c;由于竞争临界资源而造成阻塞的现象&#xff1b;若不干涉&#xff0c;则无法推进下去。 &#xff08;2&#xff09;死锁的原因 ① 竞争临界资源…...

thinkphp+vue+html超市零食品美食推荐系统零食购物商城网站7v281

本系统的开发使获取食品推荐系统信息能够更加方便快捷&#xff0c;同时也使食品推荐系统管理信息变的更加系统化、有序化。系统界面较友好&#xff0c;易于操作 运行环境:phpstudy/wamp/xammp等 开发语言&#xff1a;php 后端框架&#xff1a;Thinkphp5 前端框架&#xff1a;vu…...

思考外语学习的底层逻辑(以英语、法语为例)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言一、英语1.学习历程2.英语学习的心得3.理论检验&#xff08;持续更新&#xff09; 二、法语1.学习历程2.读入数据 总结 前言 提示&#xff1a;这里谈谈自己为什么要…...

命名ACL配置

命名ACL配置 【实验目的】 掌握命名ACL的配置。验证配置。 【实验拓扑】 实验拓扑如图1所示。 图1 实验拓扑 设备参数如表所示。 表1 设备参数表 设备 接口 IP地址 子网掩码 默认网关 R1 S0/3/0 192.168.1.1 255.255.255.252 N/A Fa0/0 192.168.2.1 255.255.…...

2008-2019年主要城市PITI指数

2008-2019年主要城市PITI指数 1、来源&#xff1a;附在文件内 2、时间区间&#xff1a;2008-2019年 3、具体时间分布&#xff1a;、2008、2009-2010、2011、2012、2013-2014、2014-2015、2015-2016、2016-2017、2017-2018、2018-2019、 4、范围&#xff1a;包括110个城市&a…...

拷贝构造函数和赋值重载函数详解

1.拷贝构造函数 1.1拷贝构造函数的概念 拷贝构造函数&#xff1a;只有单个形参&#xff0c;该形参是对本类类型对象的引用(一般常用const修饰)&#xff0c;在用已存在的类类型对象创建新对象时由编译器自动调用。拷贝构造函数也是特殊的成员函数&#xff0c;其特征如下&#…...

5件关于JavaScript中this参数的事

this 关键字是 JavaScript 中最令人困惑的部分之一&#xff0c;本文试图通过介绍有关它的五个重要事项来阐明其目的和用法。 1、它允许访问同一对象上的其他属性 在 JavaScript 中&#xff0c;函数可以是独立的单元&#xff0c;但它们也可以用作对象的值。考虑下一个对象。 …...

面试题30天打卡-day17

1、什么是内部类&#xff1f; 内部类的分类有哪些 &#xff1f;内部类的优点 &#xff0c;内部类有哪些应用场景&#xff1f; 内部类&#xff1a;在一个类中创建一个新的类 内部类主要分为以下4种&#xff1a;成员内部类、局部内部类、静态内部类、匿名内部类。 成员内部类 …...

对标世界一流|弹性应对“供应链不确定性常态化” ——快消与重资产行业的经验互鉴

1963年&#xff0c;气象学家洛伦兹提出的“蝴蝶效应”表示&#xff1a;“一只蝴蝶在巴西扇动翅膀&#xff0c;有可能会在美国德克萨斯州引起一场龙卷风”。本文希望通过提供快消行业的先进实践&#xff0c;帮助重资产企业从“蝴蝶扇动翅膀”之前就开始行动&#xff0c;避免“龙…...

【MPC|云储能】基于模型预测控制(MPC)的微电网调度优化的研究(matlab代码)

目录 1 主要内容 2 程序难点及问题说明 3 部分程序 4 下载链接 1 主要内容 该程序分为两部分&#xff0c;日前优化部分——该程序首先根据《电力系统云储能研究框架与基础模型》上面方法&#xff0c;根据每个居民的实际需要得到响应储能充放电功率&#xff0c;优化得到整体…...

WeChatMsg:3步实现微信聊天记录永久保存与智能分析的终极指南

WeChatMsg&#xff1a;3步实现微信聊天记录永久保存与智能分析的终极指南 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/w…...

[具身智能-636]:什么是语音识别?语音识别的技术过程?语音于语音特征提取?什么环节实现时域到频域的转化?

一、什么是语音识别语音识别 ASR&#xff1a;把人说话的语音声波&#xff08;时域音频&#xff09;&#xff0c;自动转换成文字的技术。本质&#xff1a;时域语音信号 → 机器可懂的语音特征 → 文本。不关心声音多好听&#xff0c;只关心说了什么内容。二、语音识别完整技术流…...

Ctool:开发者的“瑞士军刀“,告别工具切换的烦恼

Ctool&#xff1a;开发者的"瑞士军刀"&#xff0c;告别工具切换的烦恼 【免费下载链接】Ctool 程序开发常用工具 chrome / edge / firefox / utools / windows / linux / mac 项目地址: https://gitcode.com/gh_mirrors/ct/Ctool 深夜两点&#xff0c;屏幕前的…...

从接入到稳定运行Taotoken服务可靠性的个人观察记录

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 从接入到稳定运行&#xff1a;Taotoken服务可靠性的个人观察记录 1. 引言 作为需要频繁调用多种大模型能力的开发者&#xff0c;服…...

【仅限首批2000名开发者】:获取奇点大会AI原生CR沙箱环境访问权+5套企业级审查策略模板(含金融/车规/医疗三类合规预置包)

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;AI原生代码审查&#xff1a;2026奇点智能技术大会Code Review新范式 在2026奇点智能技术大会上&#xff0c;AI原生代码审查&#xff08;AI-Native Code Review&#xff09;正式取代传统人工规则引擎混合…...

用MK60单片机+鹰眼摄像头,从零搭建一个能画方块的板球控制系统(附完整代码)

基于MK60与鹰眼摄像头的板球控制系统实战&#xff1a;从硬件搭建到PID调参全解析 1. 项目背景与核心挑战 板球控制系统作为经典的控制理论教学案例&#xff0c;完美融合了机械设计、图像处理和自动控制三大技术领域。这个看似简单的系统——让小球在平板上按预设轨迹运动——实…...

Rimworld Mod制作入门:从零搭建你的第一个功能Mod

1. 为什么选择Rimworld Mod开发 Rimworld作为一款深度沙盒游戏&#xff0c;其魅力很大程度上来自于丰富的Mod生态。你可能已经玩过不少别人制作的Mod&#xff0c;但有没有想过自己动手创造一个&#xff1f;我刚开始接触Mod开发时也觉得很复杂&#xff0c;但实际尝试后发现&…...

为LLM注入联网能力:SuGPT-kexue项目的架构设计与工程实践

1. 项目概述与核心价值最近在开源社区里&#xff0c;一个名为“SuGPT-kexue”的项目引起了不少开发者和AI爱好者的注意。这个项目名本身就挺有意思&#xff0c;它指向了一个非常具体且实用的场景&#xff1a;如何让一个大型语言模型&#xff08;LLM&#xff09;具备科学上网的能…...

免费解锁B站4K大会员视频下载:三步完成离线观看的终极指南

免费解锁B站4K大会员视频下载&#xff1a;三步完成离线观看的终极指南 【免费下载链接】bilibili-downloader B站视频下载&#xff0c;支持下载大会员清晰度4K&#xff0c;持续更新中 项目地址: https://gitcode.com/gh_mirrors/bil/bilibili-downloader 还在为B站大会员…...

如何在5分钟内实现Figma界面全中文汉化?

如何在5分钟内实现Figma界面全中文汉化&#xff1f; 【免费下载链接】figmaCN 中文 Figma 插件&#xff0c;设计师人工翻译校验 项目地址: https://gitcode.com/gh_mirrors/fi/figmaCN 还在为Figma复杂的英文界面而头疼吗&#xff1f;作为一名中文设计师&#xff0c;面对…...