Canvas 的基本原理
过个年一下荒废了个把月。 最近刚接触canvas,将一些概念点简单归纳下,canvas是基于像素的图像API,与svg的最大的区别在于canvas需要重绘(canvas移除图片时需要重新绘制,而SVG可以通过编辑元素节点来编辑图片),并且基于基于像素绘制(svg顾名思义是矢量),更详细的对比mark在此:?SVG 与 HTML5 的 canvas 各有什么优点 而且我个人认为虽然canvas的API也很复杂,但是svg更复杂,囧rz。以下是我将我接触canvas过程中认为需要厘清的概念点归纳如下。
基础结构
canvas元素本身没有任何外观,它就是一块空白画板,提供给JS的一套API,最早由Safari引入,IE9之前可以使用一些类库在IE中模拟canvas,大部分的API都不在canvas元素自身定义,canvas元素自身属性与常规的HTML元素并没有多大区别, 它的绘图API都定义在一个CanvasRenderingContext2D
对象上(这里简单翻译成上下文对象),该对象通过getContext()
方法获得,代码示例:
<html>
<head>
<title>坐标系demo</title>
</head>
<body>
<canvas id = 'square' width= 200 heigth=200></canvas>
</body>
<script>
var canvas = document.getElementById('square')
var ctx = canvas.getContext('2d')//2d表示画板维度,输入3d将得到一个更为复杂的3d图形API,也称WebGL
默认坐标系与当前坐标系
图像绘制需要参考坐标系定位, canvas的默认坐标系即画布左上角原点(0,0),但是如果图像的每次绘制都参考一个固定点将缺少灵活性,于是canvas引入了“当前坐标系”的概念,所谓“当前坐标系”即指图像在此时绘制的时候所参考的坐标系,它也会作为图像状态(图像状态的概念将在后介绍)的一部分。比如rotate
旋转操作,改变当前坐标系也就是改变了rotate
的参考点,试想下如果没有当前坐标系的概念,无论是旋转,缩放,倾斜等操作不就只能参考画布左上角原点了吗(默认坐标系)?
canvas提供了translate()
和setTransform()
这两个方法分别影响当前坐标系与默认坐标系。
translate()
与setTransform()
方法
translate()
方法将坐标原点进行上下左右移动。它所影响的就是在图像在绘制的时候所参考的“当前坐标系”,举个例子?:
可以直接在demo中操作观察:
坐标系DEMO
代码:
<html>
<head>
<title>坐标系demo</title>
</head>
<body>
<canvas id = 'square' width= 200 heigth=200></canvas>
</body>
<script>
var canvas = document.getElementById('square')
var ctx = canvas.getContext('2d')ctx.beginPath()
ctx.translate(20,20) //translate影响了当前坐标系
ctx.moveTo(0,0)
ctx.lineTo(100,20)
ctx.stroke()
</script>
</html>
无任何坐标系变化的图像绘制:
translate()
方法将坐标原定移动到(20,20)后得到当前坐标系后的绘制
了解这点后setTransform()
也很容易,该方法影响的是默认坐标系,也就是说它并非将原点移来移去,而是重置当前坐标系,定义一个新的默认坐标系,什么叫影响默认坐标系,比如说前面的translate()
所移动的坐标原点(0,0)还是初始的默认坐标系,而现在setTransform()
所影响的就是这个原点(0,0)的坐标系,还是之前的demo,当加入ctx.setTransform(1,0.5,-0.5,1,30,10)
这条语句后,图像绘制将变成:
这是因为setTransform()
将默认坐标系重新定义了,于是translate()
基于新的默认坐标系来得到当前坐标系。理解了这两个概念也就掌握了canvas中坐标系的变换。
setTransform()
与transform()
方法
setTransform()
这个API略复杂, 它所接受的参数与transform()
(使用transform()
可直接得到一个变换结构,可代替rotate()
等方法,并且更为灵活)一样为6个参数,setTransform(a,b,c,d,e,f)
而坐标系变化的原理就是通过与这6个参数进行以下运算后得出的:
x' = ax + cy +e
y' = bx + dy +f
这种坐标系变换也被称为仿射变换(affine transform),关于该变换的栗子可参考这两篇博客:
?Html5 Canvas 变换矩阵与坐标变形之间的关系
路径
路径是绘制所有图形的基础,不同于SVG中path
使用属性M
,L
,A
等控制的XML文档,canvas调用上下文对象的方法来完成路径的绘制,调用beginPath()
开始一段新路径,每段路经又有子路径,正是依靠这些子路径使得图形成形。调用beginPath()
后调用MoveTo()
开始一段子路径。绘制完成后使用closePath()
闭合路径,从而形成一个闭合区域,这时候就可以使用fill()
等方法填充该区域了。每次开始一段新路径的绘制必须再次调用beginPath()
,否则新绘制的路径将作为之前路径的子路径继续绘制。
类似于lineTo()
是最简单的直线段路径方法, canvas还提供了bezierCurveTo()
和quadraticCurveTo()
这些复杂的曲线路径方法,非常复杂,所以估计一般这种操作还是先找轮子解决。
另外需要注意的是,当一条路径的两条子路径不相交的时候(比如绘制一个镂空的图形),画布将采用“非零绕数原则”判断某点是在路径内还是路径外, 这样以便于填充的时候区别哪些区域是需要填充的。
有关非零绕数原则的原理可以参考这里:mark? 非零环绕数规则和奇-偶规则
canvas的图像状态
canvas的属性与方法与我们面向对象中的属性方法并没有太大区别,只是这里涉及到了一个图像状态的概念。在canvas中,无法通过getContext()
方法获得多个上下文(context)对象,而图像属性都是基于canvas的上下文对象,也就是说无法同时拥有两个属性。形象地比喻就是图像属性就像画笔, 粗细,大小,颜色。由于同一时间只能有一个上下文对象所以只能同一时间使用一支画笔。这时候当需要其它的图像属性(另一支画笔)的时候就只能通过保存当前图像状态,然后新建一个图像状态来切换。
这时候就需要借助save()
和restore()
来切换图像状态,每次save()
都将保存当前图像状态,图像状态包括当前的图像属性,当前坐标系,裁剪区域等信息。比如以下demo以两种颜色画线:
直接在demo中修改代码观察图像状态demo
JS代码:
var canvas = document.getElementById('square')var ctx = canvas.getContext('2d')ctx.beginPath()
ctx.strokeStyle = "red"
ctx.moveTo(0,0)
ctx.lineTo(100,20)
ctx.stroke()
ctx.save()//保存当前图像状态(画笔)ctx.beginPath()
ctx.strokeStyle = "blue"
ctx.moveTo(0,0)
ctx.lineTo(100,40)
ctx.stroke()
ctx.restore()//恢复到最近保存图像状态(画笔)ctx.beginPath()
ctx.moveTo(0,0)
ctx.lineTo(100,60)
ctx.stroke()
输出如下:
这些图像属性包括:
fillStyle
font
globalAlpha
globalCompositeOperation
lineCap
lineJoin
lineWidth
miterLimit
textAlign
textBaseline
shadowBlur
shadowColor
shadowOffsetX
shadowOffsetY
strokeStyle
canvas背景
一般的纯色背景填充可以使用fillStyle
属性,但是当涉及更复杂的图片或者渐变色填充就需要CanvasPattern
和CanvasGradient
对象了,可以通过creatPattern()
方法得到CanvasPattern
,这里需要注意的是该API不仅可以代入一般的图片,也可以使用canvas元素,比如画面外一个不可见的canvas元素用于插入。
关于这两个API的细节直接参考文档:
?CanvasPattern
?CanvasGradient
像素操作
基于像素的canvas可以实现针对单个像素的操作,这也是画布底层的API,通过调用getImageData()
方法将返回一个ImageData
对象,该对象表示画布中原始的RGBA像素信息,通过调用creatImageData()
方法也可以创建一个空的ImageData
对象,最后putImageData()
方法将处理后的像素输出到画布中。
微软有篇不错的教程(使用 Canvas 将彩色照片变成黑白照片)解释像素操作,其中的操作是将彩色照片转成灰白,使用的原理是将RGB三个分量提取出来,经过计算后(关键计算语句如下)重新赋值为灰度变量。
myGray = parseInt((myRed + myGreen + myBlue) / 3);// Assign average to red, green, and blue.myImage.data[i] = myGray;myImage.data[i + 1] = myGray;myImage.data[i + 2] = myGray;
Canvas 的基本原理相关推荐
- HTML5 学习手笔二:canvas API 绘制树形图案A
上篇通过对canvas 画对角线,了解了一些canvas画图基本原理.现在可以利用HTML5 canvas API创建一个场景:带有长跑跑道的树林. 本篇大纲 用canvas API绘制树木的树冠 为 ...
- canvas插件_HTML系列之-HTML5新元素之Canvas详解
课程简介: 课程目标:通过本课程学习,掌握HTML5中图形绘制canvas的基本原理和使用,并利用canvas解决实际相关问题. 适用人群:具有一定html.css.javascript开发基础的人员 ...
- 基于Canvas的动画基本原理与数理分析
转载自https://www.jianshu.com/p/e70c9cfbdb38 什么是动画? 就像思考哲学问题无法回避思维和存在的关系一样,制作动画同样无法逃避的问题是动画的原理是什么?这里提一句 ...
- lottie 导出html,Lottie Web动效基本原理
前段时间在<Lottie Web动效在React中的构建>一文中和大家聊了如何通过lottie-web将AE导出来的JSON文件自动生成动效.在该文中,聊的主要是设计软件Figma.Ske ...
- HTML5 Canvas动画效果实现原理
在线演示 使用HTML5画布能够帮助我们快速实现简单的动画效果,基本原理如下: 每隔一定时间绘制图形并且清除图形,用来模拟出一个动画过程,可以使用context.clearRect(0, 0, x, ...
- canvas 粒子效果 - 手残实践纪录
canvas 实践 粒子效果 首先确定开发的步骤 准备基础的 html 跟 css 当背景 初始化 canvas 准备一个粒子类 Particle 编写粒子连线的函数 drawLine 编写动画函数 ...
- 【带着canvas去流浪(11)】Three.js入门学习笔记
[摘要] three.js 入门学习笔记 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 资料推荐及建议 1.官方文档 很详细,但是API部分单独 ...
- canvas绘制精细走动时钟
这里简单了利用canvas做了一个时钟,能够实现分针和时针较精确的走动,没有太多难点,重点的是要实现分针和时针走动,角度的计算,笔者在下面进行说明.先看效果图: 颜色单配的比较难看,读者谅解.请看代码 ...
- android自定义view实现原理,android随笔之自定义View基本原理
前言: 在日常的Android开发中会经常和控件打交道,有时Android提供的控件未必能满足业务的需求,这个时候就需要我们实现自定义一些控件,今天先大致了解一下自定义控件的要求和实现的基本原理. 自 ...
最新文章
- SAP MB1B + 313315做二步法货物移动报错-创建交货的数据不完全(客户)-
- C#种死锁:事务(进程 ID 112)与另一个进程被死锁在 锁
- apache2 默认端口修改
- HashMap 的性能因子
- 软件保护技术--- 常见保护技巧
- 10 Golden rules for publishing your blog
- 【信息系统项目管理师】知识框架
- struts2入门第一天----------一个简单例
- 2015 2020 r4烧录卡 区别_【2015年和2020年上半年市场资金结构有何差异?】东北证券金融工程择时周报20200802...
- python学习第二天——编写名片
- 摆脱学校WiFi的设备数限制——无线桥接
- 如何下载秦皇岛市卫星地图高清版大图
- 激励视频广告 Android,微信小程序中插入激励视频广告并获取收益(实例代码)
- 通过 ANE(Adobe Native Extension) 启动Andriod服务 推送消息(五)
- 官方老爹之痛:为什么苹果能收到推送,而安卓不行?
- 网站 被降权的四种处理方法
- 中国分省30米DEM(NASA 2020版)
- gpart 分区工具
- 衡水一中2021高考成绩查询,2021清北保送名单出炉,这些学校表现很亮眼,衡水中学未上榜?...
- 接入FaceBook登录和遇到问题排查
热门文章
- 系统dsn oracle,linux平台配置oracle odbc dsn的方法.docx
- OpenCV-Java版学习(1.在IDEA中使用OpenCV)
- android 事件冒泡,Android事件分发
- 照片换色 使用Python 或者 java
- 联想g510升级换什么cpu好_老兵不死,十年前的联想 Y450 笔记本复活记
- windows系统和linux系统可以使用相同的js代码吗_「React 手册 」在 Windows 下使用 React , 你需要注意这些问题...
- 微信小程序云开发图片上传完整代码附效果图
- 19.04.02笔记
- linux下tar.gz、tar、bz2、zip等解压缩、压缩命令小结
- ulimit -n 修改