文章目录

  • Canvas 基础教程
    • canvas绘制矩形
    • canvas路径
    • canvas圆弧
    • 贝塞尔曲线
      • 二次贝塞尔曲线
      • 三次贝塞尔曲线
    • 绘制文字
    • canvas变换
    • 背景图片
      • 双缓存
    • canvas渐变
      • 线性渐变
      • 径向渐变
    • canvas阴影
    • canvas状态
    • canvas图像合成设置
    • canvas将画布导出为图像
      • 导出为Blob和ImageData数据的处理
  • Canvas绘制的图形的事件处理
    • **给Canvas元素绑定事件**

Canvas 基础教程

参考:canvas基础

canvas绘制矩形

canvas 标签只支持一种原生的图像绘制,那就是矩形。其他图像的绘制至少都需要生成一条路径。

//绘制图像的样式
ctx.globalAlpha = .2// 画布全局透明度设置为 0.2
ctx.fillStyle= 'red' //设置图像的填充颜色。(默认黑色)
ctx.strokeStyle= 'blue' //设置图像轮廓的颜色。(默认黑色)
ctx.lineWidth=2 //设置当前绘线的粗细,属性值必须为正数,2px
ctx.lineJoin = 'round' //设定线条与线条间结合的样式。(默认miter)round:圆角。bevel:斜角。miter:直角。ctx.fillRect(x, y, width, height) *// 填充矩形*
ctx.strokeRect(x, y, width, height) *// 边框矩形*
ctx.clearRect(x, y, width, height) //可以清除 canvas 画布上指定的区域

strokeRect 渲染时的默认边框应该是 1px, 但是 canvas 在渲染矩形边框时,边框宽度是平均分在偏移位置两侧的。导致:

// 边框会渲染在 49.5-50.5 之间,浏览器是不会让一个像素只显示一半的,只会全部显示。相当于边框会渲染在 49-51 之间,也就是 2px
ctx.strokeRect(50, 50, 100, 100)// 将偏移量多移动 0.5,边框会渲染在 200-201 和 50-51 之间,也就是1px
ctx.strokeRect(200.5, 50.5, 100, 100)

canvas路径

// 绘制路径不显示,相当于只是形成了路径列表,要调用 fill() 或 stroke() 方法才会呈现在画布中。
ctx.rect(50, 50, 100, 100)
ctx.fill()// 填充显示,会自动封闭当前路径,然后填充ctx.rect(200, 50, 100, 100)
ctx.stroke()// 路径连接显示ctx.beginPath() //新建一条路径,通常我们在绘制图像之前,都会调用该方法。
ctx.lineCap = 'round' //绘制每一条线段末端的样式属性。butt:线段末端以方形结束。(默认值);round:线段末端以圆形结束;square:线段末端以方形结束,但是增加了一个宽度和线段相同,高度是线段宽度一半的矩形区域。
ctx.moveTo(70,70)
ctx.lineTo(90,100)
ctx.lineTo(50,160)
ctx.closePath() //手动闭合路径
ctx.stroke() //调用stroke才能把上述路径绘制出来

canvas圆弧

ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise):画一个以(x, y)坐标为圆心,radius 为半径的圆弧或圆,从 startAngle 开始,到 endAngle 结束。

  1. startAngle:圆弧的起始点,x 轴方向开始计算,单位以弧度表示。
  2. endAngle:圆弧的终点,单位以弧度表示。
  3. anticlockwise:true 表示逆时针,false 表示顺时针。(默认值)

贝塞尔曲线

二次贝塞尔曲线

ctx.quadraticCurveTo(cpx, cpy, x, y)

  1. cpx:控制点的 x 轴坐标。
  2. cpy:控制点的 y 轴坐标。
  3. x:终点的 x 轴坐标。
  4. y:终点的 y 轴坐标。
三次贝塞尔曲线

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

参数只是在二次贝塞尔的基础上多增加了一个控制点 (cp2x, cp2y)。

// 将4个点连接起来
ctx.beginPath()
ctx.moveTo(20, 20)
ctx.lineTo(200, 0)
ctx.lineTo(100, 100)
ctx.lineTo(200, 100)
ctx.stroke()
// 二次贝塞尔曲线在3点之间的位置
ctx.beginPath()
ctx.strokeStyle='blue'
ctx.moveTo(20, 20)
ctx.quadraticCurveTo(200, 0, 100, 100, 20)
ctx.stroke()
// 三次贝塞尔曲线在4点之间的位置
ctx.beginPath()
ctx.strokeStyle='purple'
ctx.moveTo(20, 20)
ctx.bezierCurveTo(200, 0, 100, 100, 200, 100, 20)
ctx.stroke()

在这里插入图片描述

绘制文字

  • ctx.fillText(text, x, y, [maxWidth]):在 (x, y) 填充指定的文本 (text)。
  • ctx.strokeText(text, x, y, [maxWidth]):在 (x, y) 绘制文本边框 (text)。
// 边框文本
ctx.font = '30px sans-serif'
ctx.strokeText('天天好心情', 50, 50)
// 填充文本
ctx.textAlign='center'//文本对齐方式 'left' 'right' 'center' 'start' 'end'
ctx.fillText('天天好心情', 50, 100)
// 填充文本现在宽度
ctx.textBaseline = 'middle'//当前文本基线的属性,alphabetic:文本基线是标准的字母基线。(默认值);top:文本基线在文本块的顶部。middle: 文本基线在文本块的中间。bottom:文本基线在文本块的底部。hanging:文本基线是悬挂基线。ideographic:文本基线是表意字基线。
ctx.fillText('天天好心情', 50, 150, 30)//绘制字体宽度超出最大宽度会水平自适应。
ctx.measureText('天天好心情')//返回一个 TextMetrics 对象,包含关于文本尺寸的信息(一般都用来获取文本的宽度)

ctx.textAlign = ‘center’ 比较特殊, 它是以x作为基准,所以,如果你想让文本在整个 canvas 居中,就需要将 (fillText / strokeText) 的x值设置成 canvas 的宽度的一半。

canvas变换

canvas变换是对坐标系的一种变换

ctx.translate(x, y):对当前 canvas 画布进行平移变换。在 canvas 中 translate 是累加的。

ctx.rotate(angle):将当前 canvas 画布对照原点顺时针旋转。angle是弧度

ctx.scale(x, y):将当前 canvas 画布的 x 轴和 y 轴进行伸缩变换,负数则变化方向

背景图片

ctx.drawImage(img, sx, sy, swidth, sheight, x, y, width, height):在画布上绘制图片。

参数:

img:图像源对象(规定使用的图像、画布或视频等)。
sx:可选,开始剪切的x坐标位置。
sy:可选,开始剪切的y坐标位置。
swidth:可选,被剪切图像的宽度。
sheight:可选,被剪切图像的高度。
x:在画布上放置图像的x坐标位置。
y:在画布上放置图像的y坐标位置。
width:可选,要使用的图像的宽度(伸展或缩小图像)。
height:可选,要使用的图像的高度(伸展或缩小图像)。

const img = new Image()
img.src = './img/react.png'
//必须要等图片加载完才能操作
img.onload = () => {// 将图片绘制到画布上ctx.drawImage(img, 0, 0)
}

设置背景
ctx.createPattern(image, repetition):创建一个用于图像绘制使用的样式。

参数:

  1. image:图像源对象(规定使用的图像、画布或视频等)。
  2. repetition:重复图像的方式,值只能是 repeat | repeat-x | repeat-y | no-repeat。
  3. repetition 如果为空字符串 (’’) 或 null (但不是 undefined ),repetition将被当作 repeat。
// 创建背景样式
const pat = ctx.createPattern(img, 'repeat')
ctx.fillStyle = pat
ctx.fillRect(0, 0, 300, 100)
双缓存

画布已经清空了,但新的图片还没绘制完成,就造成了视觉上的闪烁或者空白

function drawImage(url, mainCanvas) {//1. 第一步加载图片const img = new Image();img.src = url;img.onload = () => {//2.第二步,将图片绘制到缓存画布上,缓存画布临时创建和存储起来都可以const cacheCanvas = document.createElement("canvas");//画布设置为等大cacheCanvas.width = mainCanvas.width;cacheCanvas.height = mainCanvas.height;//这里是绘制图片的逻辑,自适应或者拉伸取决于自己需要,为了方便这里就拉伸了cacheCanvas.getContext("2d").drawImage(img, 0, 0, cacheCanvas.width, cacheCanvas.height);//3.第三步,把缓存画布的内容绘制到主画布上const mainCtx = mainCanvas.getContext("2d");//先清空上一帧mainCtx.clearRect(0, 0, mainCanvas.width, mainCanvas.height);//绘制画面mainCtx.drawImage(cacheCanvas, 0, 0);}
}

canvas渐变

线性渐变

ctx.createLinearGradient(x1, y1, x2, y2):从 (x1, y1) 到 (x2, y2) 进行渐变。该方法返回一个 CanvasGradient 对象。使用 CanvasGradient 身上的 addColorStop(position, color) 设置渐变颜色。

参数:

  1. position:介于 0-1 之间的值,表示渐变中开始与结束之间的位置。
  2. color:在position位置显示的css颜色值。
// 从 (0, 0) 坐标点到 (300, 0) 坐标点进行渐变
const line = ctx.createLinearGradient(0, 0, 300, 0)
// 渐变顺序 红 --> 蓝 --> 绿
line.addColorStop(0, 'red')
line.addColorStop(.5, 'blue')
line.addColorStop(1, 'green')
// 图像填充颜色设置为渐变色
ctx.fillStyle = line
ctx.fillRect(0, 0, 300, 100)
径向渐变

ctx.createRadialGradient(x1, y1, r1, x2, y2, r2):从 (x1, y1) 为圆心,半径为 r1 的圆,向 (x2, y2) 为圆心,半径为 r2 的圆进行径向渐变。使用方法跟上述的 createLinearGradient 一样。

// 以 (200, 100) 为圆心 50 为半径,向 100 为半径的圆渐变
const grad = ctx.createRadialGradient(200, 100, 50, 200, 100, 100)
// 渐变顺序 红 --> 蓝 --> 绿
grad.addColorStop(0, 'red')
grad.addColorStop(.5, 'blue')
grad.addColorStop(1, 'green')
// 图像填充颜色设置为渐变色
ctx.fillStyle = grad
ctx.fillRect(0, 0, 400, 200)

canvas阴影

设置 canvas 图像或文字阴影需要如下属性:

ctx.shadowOffsetX:图像 x 轴延伸距离。(默认值 0)
ctx.shadowOffsetY:图像 y 轴延伸距离。(默认值 0)
ctx.shadowBlur:用来设定阴影的模糊程度,其数值并不跟像素数量挂钩,也不受变换矩阵的影响。(默认值 0)
ctx.shadowColor:必须是标准的CSS颜色值,用于设定阴影颜色效果。(默认是全透明的黑色)

canvas状态

ctx.save():将当前状态放入栈中,保持 canvas 全部状态的方法。

保存到栈中的绘制状态由下面部分组成。①当前的变换矩阵②当前的剪切区域③当前的虚线列表④绘制图像的样式(strokeStyle / fillStyle / lineWidth / lineJoin / lineCap …)。
ctx.restore():通过在绘图状态栈中弹出顶端的状态,将 canvas 恢复到最近的保存状态的方法,如果没有保存状态,此方法不做任何改变。

通常我们在绘制图像进行的操作,都会放在 save() 和 restore() 方法之间,避免当前绘制图像设置的状态,影响到后续图像的绘制效果。

canvas图像合成设置

ctx.globalCompositeOperation:设置或返回如何将一个源(新的 source)图像绘制到目标(已有的 destination)的图像上。可选值如下:

属性值 描述
source-over 源在上面,新的图像层级比较高。(默认值)
source-in 只留下源与目标的重叠部分。(源的那一部分)
source-out 只留下源超过目标的部分。
source-atop 砍掉源溢出的部分。
destination-over 目标在上面,旧的图像层级比较高。
destination-in 只留下源与目标的重叠部分。(目标的那一部分)
destination-out 只留下目标超过源的部分。
destination-atop 砍掉目标溢出的部分。
lighter 显示源图像 + 目标图像。(重叠图形的颜色是通过颜色值相加来确定的)
copy 显示源图像,忽略目标图像。
xor 那些重叠和正常绘制之外的其他地方是透明的
        let canvas = document.querySelector('#canvas');let ctx = canvas.getContext('2d');let div = document.querySelector('.text');canvas.width = div.clientWidth;canvas.height = div.clientHeight;ctx.fillStyle = '#333';ctx.fillRect(0, 0, canvas.width, canvas.height);canvas.addEventListener('mousedown', () => {canvas.addEventListener('mousemove', draw)canvas.addEventListener('mouseup', () => {canvas.removeEventListener('mousemove',draw);})})function draw(e) {ctx.beginPath();ctx.globalCompositeOperation = 'destination-out'ctx.lineCap = 'round';ctx.strokeStyle = '#fff'ctx.lineWidth = '15';ctx.lineTo(e.offsetX, e.offsetY);ctx.stroke();}

canvas将画布导出为图像

canvas.toDataURL(type, encoderOptions)。通过 canvas 身上的 toDataURL 方法,返回一个包含画布内容的 base64 格式的 data url。

参数:

  1. type:图片格式,默认为 image/png。
  2. encoderOptions:在指定图片格式为 image/jpeg 或 image/webp 的情况下,可以从 0-1 之间选择图片的质量。如果超出取值范围,将会使用默认值0.92。其他参数会被忽略。
ctx.fillStyle = 'red'
ctx.fillRect(50, 50, 100, 100)
const dataUrl = canvas.toDataURL()
// data:image/png;base64,iVBORw0KGgoAAAANSUh....
导出为Blob和ImageData数据的处理

有些场景下,你可能需要根据图片获取对应的ImageData数据或者Blob数据,传到后端做一些图像处理。这里就介绍下如何通过Canvas获得这两种数据,并且互相转换

// 获取ImageData
function getImageData(url) {return new Promise((res, rej) => {//根据url创建出img对象来const img = new Image(url);img.src = url;img.onload = () => {//创建临时的canvas元素,用于获取imageData数据const tempCanvas = document.createElement("canvas");//将canvas设为和图片等大tempCanvas.width = img.naturalWidth;tempCanvas.height = img.naturalHeight;//绘制图片,并获取imageData数据const ctx = tempCanvas.getContext("2d");ctx.drawImage(img, 0, 0);res(ctx.getImageData(0, 0, tempCanvas.width, tempCanvas.height));//tempCanvas.toBlob(res, type, quality)//获取Blob数据}img.onerror=(err)=>{rej(err);}})
}

Canvas绘制的图形的事件处理

参考:canvas事件处理

Canvas是一个整体,图形本身实际都是Canvas的一部分,不可单独获取,所以也就无法直接给某个图形增加JavaScript事件。

给Canvas元素绑定事件

【基本思路】 ①给Canvas元素绑定事件addEventListener('click',()=>{},false),②当事件发生时,检查触发事件的位置(e.offsetX,e.offsetY),③然后检查哪些图形覆盖了该位置isPointInPath。要考虑这个判断过程的效率,有些还需要重新判断事件类型(click,mouseover,mouseenter,mousemove,mouseleave),甚至要重新定义一个Canvas内部的捕获和冒泡机制。

​ 但是由于isPointInPath方法仅判断当前上下文环境中的路径,所以当Canvas里已经绘制了多个图形时,仅能以最后一个图形的上下文环境来判断事件。【解决方法】所以,当事件(click,mouseover,mouseenter,mousemove,mouseleave)发生时,先清空画布clearRect,开始重绘所有图形,每绘制一个就使用isPointInPath方法,判断事件坐标(e.offsetX,e.offsetY)是否在此次上下文环境中的图形覆盖范围内,根据冒泡机制,通过循环收集触发该事件的所有图形。

数组的最后一个成员处于Canvas最上层,而第一个成员则在最下层,我们可以视为最上层的成员是e.target,而其他成员则是冒泡过程中传递到的节点。当然这只是最简单的一种处理方法,如果真要模拟DOM处理,还要给图形设置父子级关系。

在实际运用时,如何缓存图形参数,如何进行循环重绘,以及如何处理事件冒泡,都还需要根据实际情况花一些心思去处理。另外,click是一个比较好处理的事件,相对麻烦的是mouseover、mouseout和mousemove这些事件,由于鼠标一旦进入Canvas元素,始终发生的都是mousemove事件,所以如果要给某个图形单独设置mouseover或mouseout,还需要记录鼠标移动的路线,给图形设置进出状态。由于处理的步骤变得复杂起来,必须对性能问题提高关注。

<!DOCTYPE html>
<head><meta charset="utf-8" />
</head>
<body><canvas class="my-canvas" style="position: relative;" width="500" height="500"> </canvas><script>let canvas = document.querySelector('.my-canvas');let ctx = canvas.getContext('2d');// ctx.beginPath();//重开个路径// ctx.rect(10, 10, 100, 100);// ctx.stroke();// ctx.isPointInPath(20, 20);     //true// ctx.beginPath();//重开个路径// ctx.rect(110, 110, 100, 100);// ctx.stroke();// ctx.isPointInPath(150, 150);     //true// ctx.isPointInPath(20, 20);     //false,当Canvas里已经绘制了多个图形时,仅能以最后一个图形的上下文环境来判断事件,isPointInPath方法仅能识别当前上下文环境里的图形路径,而之前绘制的路径,无法回溯判断arr = [{ x: 10, y: 10, width: 100, height: 100 },{ x: 110, y: 110, width: 100, height: 100 }];draw();canvas.addEventListener('click', function (e) {p = getEventPosition(e);let who = draw(p);console.log('who trigger', who);//获取canvas触发数组}, false);//获取触发元素的位置:function getEventPosition(ev) {var x, y;if (ev.layerX || ev.layerX == 0) {x = ev.layerX;y = ev.layerY;} else if (ev.offsetX || ev.offsetX == 0) { // Operax = ev.offsetX;y = ev.offsetY;}return { x: x, y: y };}function draw(p) {var who = [];ctx.clearRect(0, 0, ctx.width, ctx.height);arr.forEach(function (v, i) {ctx.beginPath();ctx.rect(v.x, v.y, v.width, v.height);ctx.stroke();if (p && ctx.isPointInPath(p.x, p.y)) {//如果传入了事件坐标,就用isPointInPath判断一下//如果当前环境覆盖了该坐标,就将当前环境的index值放到数组里who.push(i);}});//根据数组中的index值,可以到arr数组中找到相应的元素。return who;}</script>
</body>
</html>

Canvas基础教程相关推荐

  1. 《HTML5 CANVAS基础教程》读书笔记

    一.HTML5简介 1.HTML5新特性 1)结构元素:section,header,hgroup,footer,nav,article,aside, 2)内容元素:figure,figcaption ...

  2. canvas基础之旅

    canvas 主要使用2D rendering context  API 实现其功能和特效. canvas 一般浏览器都支持,但在ie9之前的是不支持的.(解决办法:添加IxplorerCanvas ...

  3. Android基础教程pdf

    下载地址:网盘下载 内容简介  · · · · · · <Android基础教程>内容完整丰富,具有较强的通用性,读者都能通过<Android基础教程>快速学习Android开 ...

  4. [转]html5 Canvas画图教程(1)—画图的基本常识

    今天看到一个讲Canvas的教程,很通俗移动,所以转载了下. 虽然大家都称Canvas为html5的新标签,看起来好像Canvas属于html语言的新知识,但其实Canvas画图是通过javascri ...

  5. 微信小游戏开发零基础教程(一)-CocosCreator

    微信小游戏零基础教程(一)-CocosCreator 最终效果预览 准备工作 创建工程 测试第一个场景 最终效果预览 本教程最后制作的游戏效果: 准备工作 下载 微信开发者工具 最新版->下载地 ...

  6. 【ThreeJS基础教程】0.在学习使用ThreeJS之前

    [ThreeJS基础教程]0.在学习使用ThreeJS之前 什么人适合学习ThreeJS 什么人不建议学习ThreeJS或学起来比较累 了解ThreeJS ThreeJS文档在哪 关于<Thre ...

  7. 【微信小程序-原生开发】实用教程20 - 生成海报(实战范例为生成活动海报,内含生成指定页面的小程序二维码,保存图片到手机,canvas 系列教程)

    可在系列教程的基础上继续开发,也可以单独使用 [微信小程序-原生开发]系列教程 效果预览 代码实现 点击触发生成海报 在活动详情页,指定点击某图标/按钮,触发跳转到生成海报的页面 pages\comp ...

  8. 《Python程序设计与算法基础教程(第二版)》江红 余青松 全部章节的课后习题,上机实践,课后答案,案例研究

    (还在更新中-) 这篇博客花费了我的大量时间和精力,从创作到维护:若认可本篇博客,希望给一个点赞.收藏 并且,遇到了什么问题,请在评论区留言,我会及时回复的 这本书对Python的知识点的描述很详细, ...

  9. Python基础教程,Python入门教程

    Python 是一门上手简单.功能强大.通用型的脚本编程语言.Python 类库极其丰富,这使得 Python 几乎无所不能,网站开发.软件开发.大数据分析.网络爬虫.机器学习等都不在话下. 这套 P ...

最新文章

  1. 看论文不用来回翻了,这款PDF阅读神器能自动提取前文信息,科研效率max!
  2. 使用LoadRunner-运行负载测试
  3. 现代软件工程 (备份)
  4. Python 面向对象 实例方法 属性 (上)
  5. java返回泛型_Java泛型从泛型方法返回持有者对象
  6. cam350 不能打开光绘文件_用CAM350导出GERBER文件的基本步骤
  7. FMEA-MSR步骤三:功能分析
  8. 人工智能助力三维几何自动化建模
  9. link rel=canonical概念和用法(增加页面权重,利于排名)
  10. 智汇华云 | ArSDN之分布式路由及浮动IP简介
  11. Unity序列帧动画疑难解答
  12. 学计算机的会excel,轻松学电脑开机即会--EXCEL电子表格商务办公应用(附光盘)
  13. 软件测试/测试开发丨Java or Python?测试开发工程师如何选择合适的编程语言?
  14. 汇编语言使用宏,实现两个字节压缩BCD数相加
  15. 中国医科大学《护理研究(本科)》在线作业
  16. 胺液(MDEA)净化树脂及工艺技术,那种胺液净化树脂运行比较稳定?
  17. 基于 Java8 的国产开源 IoT 企业级物联网平台
  18. 商务酒店机房改造环控项目解决方案
  19. Qt 信号槽的应用(一)
  20. java web商城项目难度_JavaWeb网上商城的反思

热门文章

  1. Laravel Scout 包在 Elasticsearch 中的使用记录
  2. 百度一面+二面+三面
  3. js: 字符(字母) 与 ASCII码 转换方法
  4. python处理微信消息导入excel_使用python读取excel中的数据,并重新写入到新的excel中...
  5. 低成本成FS68001A、FS68003无线充SOC芯片
  6. 【CodeForces300D】Painting Square
  7. linux 重定向 2 gt gt,Linux命令- echo、grep 、重定向、1gt;amp;2、2gt;amp;1的介绍
  8. 【东方幻想乡系列模拟赛 Stage 4】 河城荷取
  9. Java:判断是一年的第几天
  10. 计算机网口速率修改,win7系统修改无线网卡连接速率的操作方法