前言

去年在公司内部做了一次canvas的分享,或者说canvas总结会更为贴切,但由于一直都因为公事或者私事,一直没有把东西总结成文章分享给大家,实在抱歉~ 分享这篇文章的目的是为了让同学们对canvas有一个全面的认识,废话不多说,开拔!

介绍

Canvas是一个可以使用脚本(通常为Javascript,其它比如 Java Applets or JavaFX/JavaFX Script)来绘制图形,默认大小为300像素×150像素的HTML元素。

<canvas style="background: purple;"></canvas>

小试牛刀

<!-- canvas -->
<canvas id="canvas"></canvas>
<!-- javascript -->
<script>const canvas = document.getElementById('canvas')const ctx = canvas.getContext('2d')ctx.fillStyle = 'purple'ctx.fillRect(0, 0, 300, 150)</script>

经过了以上地狱般的学习,我相信同学们现在已精通canvas。 接下来,我将介绍很多案例,把自己能想到的都列举出来,并且,结合其原理,为同学们一一介绍。

应用案例

案例如下:

  • 动画
  • 游戏
  • 视频(因为生产环境还不成熟,略)
  • 截图
  • 合成图
  • 分享网页截图
  • 滤镜
  • 抠图
  • 旋转、缩放、位移、形变
  • 粒子

动画

API介绍

requestAnimationFrame

该方法告诉浏览器您希望执行动画并请求浏览器在下一次重绘之前调用指定的函数来更新动画。 该方法使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。

requestAnimationFrame 优点

1.避免掉帧 完全依赖浏览器的绘制频率,从而避免过度绘制,影响电池寿命。 2.提升性能 当Tab或隐藏的iframe里,暂停调用。

Demo

方块移动

<!-- canvas -->
<canvas id="canvas" width="600" height="600"></canvas>
<!-- javascript -->
<script>const canvas = document.getElementById('canvas')const ctx = canvas.getContext('2d')ctx.fillStyle = 'purple'const step = 1    // 每步的长度let xPosition = 0 // x坐标move()            // call movefunction move(){ctx.clearRect(0, 0, 600, 600)ctx.fillRect(xPosition, 0, 300, 150)xPosition += stepif (xPosition <= 300) {requestAnimationFrame(() => {move()})}}</script>

游戏

三要素

个人做游戏总结的三要素:

  • 对象抽象
  • requestAnimationFrame
  • 缓动函数

对象抽象:即对游戏中角色的抽象,面向对象的思维在游戏中非常地普遍。举个例子,我们来抽象一个《勇者斗恶龙》里的史莱姆:

class Slime{constructor(hp, mp, level, attack, defence) {this.hp = hpthis.mp = mpthis.level = levelthis.attack = attackthis.defence = defence}bite() {return this.attack}fire() {return this.attack * 2}
}

requestAnimationFrame:之前我们已经接触过这个API了,结合上面动画的例子,我们很容易自然的就能想到,游戏动起来的原理了。

缓动函数:我们知道,匀速运动的动画会显得非常不自然,要变得自然就得时而加速,时而减速,那样动画就会变得更加灵活,不再生硬。

Demo

有兴趣的同学可以看我以前写的小游戏。 项目地址:github.com/CodeLittleP…

截图

API介绍

drawImage(image, sx, sy [, sWidth, sHeight [, dx, dy, dWidth, dHeight]])

绘制图像方法。

toDataURL(type, encoderOptions)

方法返回一个包含图片展示的 data URI 。可以使用 type 参数其类型,默认为 PNG 格式。图片的分辨率为96dpi。 注意:

  • 该方法必须在http服务下
  • 非同源的图片需要CORS支持,图片设置crossOrigin =“”(只要crossOrigin的属性值不是use-credentials,全部都会解析为anonymous,包括空字符串,包括类似'abc'这样的字符)

canvas.style.width 和 canvas.width 的区别

把canvas元素比作画框: canvas.width则是控制画框尺寸的方式。 canvas.style.width则是控制在画框中的画尺寸的方式。

Demo

核心代码

const captureResultBox = document.getElementById('captureResultBox')
const captureRect = document.getElementById('captureRect')
const style = window.getComputedStyle(captureRect)
// 设置canvas画布大小
canvas.width = parseInt(style.width)
canvas.height = parseInt(style.height)
// 画图
const x = parseInt(style.left)
const y = parseInt(style.top)
const w = parseInt(img.width)
const h = parseInt(img.height)
ctx.drawImage(img, x, y, w, h, 0, 0, w, h)
// 将图片append到html中
const resultImg = document.createElement('img')
// toDataURL必须在http服务中
resultImg.src = canvas.toDataURL('image/png', 0.92)

合成图

原理

回看之前的例子,我们知道了drawImage可以自己画图画,也可以画图片。canvas完全就是个画板,可任由我们发挥。 合成的思路其实就是把多张图片都画在同一个画布(cavans)里。是不是一下子就知道接下来怎么做啦?

Demo

核心代码

// 设置画布大小canvas.width = bg.widthcanvas.height = bg.height// 画背景ctx.drawImage(bg, 0, 0)// 画第一个角色ctx.drawImage(character1, 100, 200,character1.width / 2,character1.height / 2)// 画第二个角色ctx.drawImage(character2, 500, 200,character2.width / 2,character2.height / 2)

如图,背景是一深夜无人后院,然后去网上搜两张背景透明的角色图片,再将两张图一次画到画布上就成了合成图啦。

分享网页截图

原理

拿比较出名的html2canvas为例,实现方式就是遍历整个dom,然后挨个拉取样式,在canvas上一个个地画出来。

Demo

滤镜

API介绍

getImageData(sx, sy, sw, sh)

返回一个ImageData对象,用来描述canvas区域隐含的像素数据,这个区域通过矩形表示,起始点为(sx, sy)、宽为sw、高为sh。 看段代码:

const img = document.createElement('img')
img.src = './filter.jpg'
img.addEventListener('load', () => {canvas.width = img.widthcanvas.height = img.heightctx.drawImage(img, 0, 0)console.log(ctx.getImageData(0, 0, canvas.width, canvas.height))
})

它会打印出如下数据:

有点迷?不慌,接下去看。

数据类型介绍

Uint8ClampedArray

8位无符号整型固定数组) 类型化数组表示一个由值固定在0-255区间的8位无符号整型组成的数组;如果你指定一个在 [0,255] 区间外的值,它将被替换为0或255;如果你指定一个非整数,那么它将被设置为最接近它的整数。(数组)内容被初始化为0。一旦(数组)被创建,你可以使用对象的方法引用数组里的元素,或使用标准的数组索引语法(即使用方括号标记)。 回看这张图:

data里其实就是像素,按每4个为一组成为一个像素。 4个一组,难道是rgba? (o゜▽゜)o☆[BINGO!] 这样的话,图片的宽x高x4(w * h * 4 )就是所有像素的总和,刚好就死data的length。

数学推导

已知:924160 = 640 x 316 x 4 可知:数组的长度为length = canvas.width x canvas.height x 4

知道了这种关系,我们不妨把这个一维数组想象成二维数组,想象它是一个平面图,如图:

一个格子代表一个像素

w = 图像宽度

h = 图像高度

这样,我们可以很容易得到点(x, y)在一维数组中对应的位置。我们想一想,点(1, 1)坐标对应的是数组下标为0,点(2, 1)对应的是数组下标4,假设图像宽度为2*2,那么点(1,2)对应下标就是index=((2 - 1)*w + (1 - 1))*4 = 8。

推导出公式:index = [(y - 1) * w + (x - 1) ] * 4

继续API介绍

createImageData(width, height)

createImageData是在canvas在取渲染上下文为2D(即canvas.getContext(‘2d'))的时候提供的接口。作用是创建一个新的、空的、特定尺寸的ImageData对象。其中所有的像素点初始都为黑色透明。并返回该ImageData对象。

putImageData

putImageData方法作为canvas 2D API 以给定的ImageData对象绘制数据进位图。如果提供了脏矩形,将只有矩形的像素会被绘制。这个方法不会影响canvas的形变矩阵。

这小节我们学了好几个新API,然后重新理了理数学知识。同学们好好消化完以后,就进Demo阶段吧。

Demo

核心代码:

最终效果:

抠图

对于纯背景抠图,其实还是比较简单的。上面我们已经说过,我们可以拿到整个canvas的每个像素点的值了。所以,只需要把纯色的色值转为透明就好了。 但这种场景不多,因为,背景很少有纯色的情况,而且即使背景纯色,不保证被扣对象的身上没有和背景同色值的情况。 所以,如果要处理复杂的情况,还是建议后端来做比较好,后端早已有了成熟的图像处理解决方案,比如opencv等。像美图的话,有专门的图像算法团队,天天研究这方面。 接下来,我将介绍下美图人像抠图的思路。

属性介绍

globalCompositeOperation

控制drawImage的绘制图层先后顺序。

思路

我们将使用souce-in这个属性。如上图所示,这个属性的作用是,两图叠加,只取叠加的部分。 为什么这样搞?不是说好了,美图是让后端算法大佬们处理吗? 因为,为了人像抠图适应更多的场景,算法大佬们只会把人物图像处理成一个蒙版图并返给前端,之后让前端自己处理。 我们看下原图:

再看下后端返给的蒙版图:

得到以上的蒙版图以后,先把黑色处理成透明; 先在canvas上draw原图; 再把globalCompositeOperation 设置为 'source-in'; 然后再draw处理后的蒙版图; 得到的就是最后的抠图啦! 这个方案是咨询前美图大佬@xd-tayde的,感谢~

Demo

处理结果:

旋转、缩放、位移、形变

对于旋转、缩放、位移、形变,canvas的上下文ctx有对应的API可以调用,也可以用martrix方式做更高级的变化。因为涉及的内容很多,如果全写这的话,篇幅太大。 所以,我这里直接推荐一篇文章给同学们学习 ——《canvas 图像旋转与翻转姿势解锁》

粒子

抽象

之前我们就知道了,我们可以获取canvas上的每个像素点。 所谓的粒子,其实算是对一个像素的抽象。它具有自己坐标,自己的色值,可以通过改变自身的属性“动”起来。 因此我们不妨将粒子作为一个对象来看待,它有坐标和色值,如:

let particle = {x: 0,y: 0,rgba: '(1, 1, 1, 1)'
}

Demo - 小试牛刀

我将把一张网易支付的logo图,用散落的粒子重新画出来。 核心代码:

// 获取像素颜色信息const originImageData = ctx.getImageData(0, 0, canvas.width, canvas.height)const originImageDataValue = originImageData.dataconst w = canvas.widthconst h = canvas.heightlet colors = []let index = 0for (let y = 1; y <= h; y++) {for (let x = 1; x <= w ; x++) {const r = originImageDataValue[index]const g = originImageDataValue[index + 1]const b = originImageDataValue[index + 2]const a = originImageDataValue[index + 3]index += 4// 将像素位置打乱,保存进返回数据中colors.push({x: x + getRandomArbitrary(-OFFSET, OFFSET),y: y + getRandomArbitrary(-OFFSET, OFFSET),color: `rgba(${r}, ${g}, ${b}, ${a})`})}

效果:

Demo - 粒子动画

三要素

  • 粒子对象化
  • 缓动函数
  • 性能

粒子对象化已经介绍过了。 缓动函数,在之前的游戏也提及过,是为了让动画更加的自然生动。 性能是一个很需要关注的问题。因为比如一张500x500的图片,那数据量就是500x500x4=1000000。动画借助了requestAnimationFrame,正常的情况下一般刷新频率在60HZ,能展现非常流畅的动画。但现在要处理这么大的数据量,浏览器抗不过来了,自然造成了降频,导致动画卡帧严重。

为了性能,粒子动画往往采用选择性的选取像素用来绘制。比如,只绘制原图x坐标为偶数,或能被4等整除的像素。比如,只绘制原图对应像素r色值为155以上的像素。

结合上面的思路,就可以做出各种强大的例子动画啦。

Demo

所有Demo项目地址

github.com/CodeLittleP…

原文出处

《canvas-应用大全》

参考文章

《打造高大上的 Canvas 粒子动画 - 腾讯 ISUX》

转载于:https://juejin.im/post/5c81e6de5188257ade0231f4

canvas-应用大全相关推荐

  1. 深入讲解VsCode各场景高级调试与使用技巧

    前端瓶子君,关注公众号 回复算法,加入前端编程面试算法每日一题群 VsCode自从诞生以来,以其各自优异的特性迅速走红.尤其是对于前端开发小伙伴来说,几乎成为必不可少的开发工具.所以,熟练掌握VsCo ...

  2. 前端交互开发微体验--总结了一些国内外炫酷的网站

    前端交互开发微体验 关于首屏灵动小效果,微交互提升页面生机: 一.关于首屏视频播放 http://designmodo.com/startup/ 感官体验:科技感,高大上,酷 综合评价:如页面请求不多 ...

  3. html android canvas兼容_快来!这里有5分钟看完马上学会的HTML基础大全

    很多人会问做运营为什么我要了解前端HTML.首先做运营最主要的是做各种活动,做活动自然要面对客户,当然要能设计页面,做设计当然要懂页面技术 HTML了,所谓技多不压身,而且在这个快节奏的时代,掌握的技 ...

  4. canvas使用技巧大全

    1.基本知识 1.1 canvas的基本知识 canvas元素的大小和绘图表面的大小 canvas绘图环境及保存和回复 基本绘制操作 事件处理 1.1.1 canvas 基本知识 canvas是htm ...

  5. 前端:JS/38/canvas状态的保存和恢复(canvas常用状态大全),canvas画布中图像的变形

    canvas状态的保存和恢复 Saving and restoring state 在了解变形之前,我先介绍两个在你开始绘制复杂图形时必不可少的方法. canvas.save() 保存画布(canva ...

  6. python设置背景图片大全_Python实例讲解 - tkinter canvas (设置背景图片及文字)

    Python实例讲解 -- tkinter canvas (设置背景图片及文字) 先来一个绘图: from Tkinter import * master = Tk() w = Canvas(mast ...

  7. 必备的Canvas接口和动画效果大全

    来源 | https://segmentfault.com/a/1190000021998875 1.概述 <canvas>元素用于生成图像.它本身就像一个画布,JavaScript 通过 ...

  8. canvas 文字颜色_canvas 中普通动效与粒子动效的实现

    (给前端大全加星标,提升前端技能) 作者:薄荷前端 https://github.com/BooheeFE/weekly/issues/26 canvas 用于在网页上绘制图像.动画,可以将其理解为画 ...

  9. app canvas渲染后图片黑色_H5 基于 canvas 实现电子签名并生成PDF文档

    (给前端大全加星标,提升前端技能) 转自:coyota666 https://juejin.cn/post/6901273585428463624 前言 电子签名通俗来说就是通过技术手段实现在电子文档 ...

最新文章

  1. IOS开发笔记17-Object-C中的继承
  2. 信息系统项目管理师-常用术语中英文对应
  3. .NET 指南:转换操作符
  4. Live Rate creation中的Territory check逻辑
  5. mega2560电脑识别不到端口后_Qt音视频开发41-人脸识别嵌入式
  6. android list 替换元素_Java学习之List集合
  7. svn up 更新与回退
  8. 《信息安全系统设计基础》第六周学习总结
  9. ubuntu16.04命令行模式和图形界面互相切换
  10. Big FAT32 Format Pro(U盘格式化工具)官方正式版V2.0 | u盘无法格式化怎么办 | 万能u盘修复工具下载 | 突破FAT文件系统4GB的限制
  11. datagrid的deleteRow使用
  12. 三维重建之结构光编码方案研究
  13. AD19画原理图和PCB注意事项和步骤(主要为PCB和总结注意事项)
  14. 容性耦合等离子体(CCP)和电感耦合等离子体(ICP)
  15. 程序员如何选择未来的职业路线
  16. Mysql 查出某个字段不包含某个值
  17. 计算机系统的组成一般不包括,计算机系统的基本组成,一般应包括哪些
  18. 5寸android智能手机,5寸刚入门 六款巨屏安卓智能手机盘点(5)
  19. 安全日记—零基础开始学安全(3)
  20. python 整合_python的资源整合

热门文章

  1. Kubernetes基本概念之Name和NameSpace
  2. c# 委托和事件(总结篇)
  3. 安装 postgresql
  4. twitter 监控登陆活动
  5. C1 FlexGrid 行Style设置问题
  6. 数据库的内连接和外连接区别?
  7. WINDOWS高级窗口的客户区域拖动技术及其应用
  8. 自我理解:const char*, char const* and char *const
  9. 字符的点阵显示(模拟户外广告显示屏)
  10. 系统试图将驱动器合并到合并驱动器上的目录_系统小技巧:“徒手”创建可启动的VHD系统...