看完这本攻略,Canvas新手小白也可以创建惊人特效

综合技术 2018-12-09 阅读原文

作者本人的干货,一般人我不给他看。(开玩笑的)

canvas是一个很实用的工具,难可以难到头发掉光,简单可以简单到几行代码就能给人眼前一亮的效果。这里是作者在开发canvas的道路上遇过的坑,以及如何用简易地使用canvas做一些日常任务,比如分享图片的自定义,又比如大家喜欢的X炸天的粒子特效(不知道算不算,反正很COOL就是了)。

No.1 Canvas的正确打开方式

大家都知道Canvas可以做流畅的动画,功能很强大,但是Canvas中并没有像Dom那样可以帮助我们调试的工具。不过现在mac有一款web inspector( webkit.org/downloads// )可以调试canvas,有兴趣的同学可以去试一下。

不过这里还有一种方式可以帮助大家调试Canvas。Canvas其实就是一块绘板,随便你在上面画什么。但是又不像PS那样有辅助线,因此定位很艰辛。

创建网格

  • mark api:
    • context.fillStyle
    • context.textAlign
    • context.font
    • context.textBaseline
    • context.fillRect
    • context.fillText

因此,手动给Canvas加上网格,不就可以了!这里我们可以创建一个绘制网格的方法,然后每次render的时候调用,这样就可以对图形的定位有一个直观的感受了。再也不用抓瞎。

首先我们要计算好网格的数量,将所有计算好的网线放入一个数组中。虽然我们也可以动态计算,网格的位置,但是从性能上考虑,canvas中凡是在绘图之前可以确认的位置都提前计算好,这样可以提高性能。这里我留了一点空间给坐标值,因此并不是全屏的网格。

let Grids=[]
function initGrid(cap,width,height,lineWidth){
  const colNum=Math.ceil(width/cap)-1
  const rowNum=Math.ceil(height/cap)-1
  for(let i=1;i<=colNum;i++){
    Grids.push([[cap*i-1, 0,lineWidth,colNum*cap],[i*cap,cap*i-1,colNum*cap+5,"top"]]) 
  }
  for(let i=1;i<=rowNum;i++){
    Grids.push([[ 0,cap*i-1,rowNum*cap,lineWidth],[i*cap,rowNum*cap+5,cap*i-1,"middle"]])
  }
}
initGrid(cap,canvasWidth,canvasHeight,lineWidth);
复制代码

计算并保存好信息之后,我们就要开始画线了。对于线的宽度最好是双数,然后位置偏1px,然线居中,因为.5px在有些机子上性能不好,比如四舍五入一类的,容易有偏差。

function createGrid(){
  context.fillStyle = 'green';
  context.textAlign="center"
  context.font="24px Arial"
  Grids.forEach((grid)=>{
     context.textBaseline=grid[1][3]
     context.fillRect( grid[0][0],grid[0][1], grid[0][2], grid[0][3])
     context.fillText (grid[1][0],grid[1][1], grid[1][2]);   
  })
}
复制代码

画一个几何图形

  • mark api:
    • context.beginPath();
    • context.moveTo (50, 50);
    • context.lineTo (450, 450);
    • context.closePath()

注意这里beginPath是一切新开始的意思,也就是当前图和上一个操作已经没有了任何关系。closePath就是结束的意思,一切回到最初开始的地方,就是最先开始的那个点。lineTo就是画线的意思,两个点之间画一条直线。我们可以搭配moveTo使用,moveTo就是移动到当前点,但是并不绘制任何内容。

如果我们只是绘制图形,并无其他操作,比如每段路径的颜色不一样或者是填充颜色不一样,那么moveTo和beiginPath的作用差不多,但是如果是,那么每次都需要beginPath一下,切断与上一个路径的联系,否则会以最后的设置的填充色路径色为主。

那么问题来了我直接closePath可以吗?当然不行,你可以说开始就开始,但不能说结束就结束!closePath最大的作用就是连接路径最后一个点和路径最开始的点。

橡皮擦

因为Canvas是画布,所以每次画面更新时都是擦干净,再画下一幅画,不然就会重叠。大家想想一下帧动画,就是1s中N幅画划过的动态感,变成了会动的动画。如果是jpg这种不透明的图片还可以一层层覆盖,如果是png透明的图片,一层层就会堆叠在一起。所以橡皮擦的功能时必不可少的。

  • mark api:
    • context.clearRect (x,y,width,height);

其实就是画一个矩形,矩形所在的地方图像消失。

在线玩耍地址:

See the Pencanvas No.1 by cherryvenus (@cherryvenus) onCodePen.

No.2 Canvas的实用工具

Canvas中有几个小知识点,非常的实用,而且应该是日常开发中基本上都要使用的。

Canvas的像素点

首先就是像素的问题,大家有没有遇到过Canvas模糊的问题,尤其是手机,这个现象尤为明显。那么有没有解决方案呢?答案是当然有!而且并不复杂,一个属性就可以搞定!

Canvas的尺寸其实又两个,不知道大家有没有发现。一个时Canvas的大小,一个是Canvas的样式大小。

canvas.width=cWidth
canvas.height=cHeight
canvas.style.width=cWidth
canvas.style.height=cHeight
复制代码

那么这就好解决的。我们的图片如果1:1放在手机上肯定是模的,但是我们会将图片的样式宽度减少一半以上,这样就不模糊了!Canvas也是同理,只要样式大小小于Canvas大小即可。那么小多少呢?有没有一个标准?这个时候就要借用 window.devicePixelRatio 这个参数了,告诉我们屏幕的像素比,如果没有就2,一般来说像素比是2的情况下,Canvas就不会模糊。

const dpr=window.devicePixelRatio||2
canvas.width=cWidth*dpr
canvas.height=cHeight*dpr
canvas.style.width=cWidth
canvas.style.height=cHeight
复制代码

论如何保存Canvas的图像

我们的H5在哪里的传播最多?微信啊!我们经常接到一个功能,让用户保存图片,分享到朋友圈。通常这个图片是用户自己填写内容,然后打印到屏幕上。最后合成,保存的。那么Canvas该如何帮助我们保存图片呢?

Canvas虽然不能直接保存图片,但是却可以生成Base64的文件。我们将Base64放入img标签中,用户就可以自由保存了。

代码也很简单 canvas.toDataURL('image/jpeg');

代码玩耍地址:

See the PenCanvas No.2 by cherryvenus (@cherryvenus) onCodePen.

No.3 save&restore,Canvas的回退功能

Canvas的代码一个不小心就会写好多好多,每次新建一个操作都要写好多内容。这个时候就要看看Canvas的复用操作了,一个是减少操作,还有一个就是减少机器的压力,防止计算量过多。

谈到save&restore,可能大家会有点懵,不知道这个是用来做什么的。也不知道有什么用。我们假想所有的canvas的配置,如fillSytle,strokeStyle的状态都封装在一个对象之中,然后每次save这个对象,就将这个对象push到一个Cavans状态的数组之中,之后我们可能改变了其中的一些属性,然后我们又需要之前的哪个配置,怎么办?再写一遍属性配置吗?不,这个时候我们可以用restore,一键切换至上一个状态。也就是当前的配置全部失效。所有属性值回退到之前的一个状态。我们可以一直restore到默认值,也就是Canvas状态数组空了为止。

代码玩耍地址:

See the Pen Canvas No3 save&restore by cherryvenus (@cherryvenus) onCodePen.

No.4 最常用的drawImage全方位解读

解析图:

个人觉得Canvas中最头疼的就是图片的绘制了,drawImage这个一个方法,就可以帮助我们完成拉伸,剪切,放大,缩小的功能。

drawImage的总参数有9个,但是平时我们可以简写。

第一个参数一定是image,也就是我们的图片对象。其余的几个参数,容易搞混。我们来详细地看下。

参数作用
dx,dy这个最好理解,这里是指图片开始绘制的位置,如果设置这两个参数,就是从这个(dx,dy)点开始绘制原始完整的图片
dx,dy,dwidth,dheight这里除了开始点(dx,dy),还有图片在画布上呈现的大小,这边需要注意,虽然会画完整的图片,但是会按照dwidth和dheight的尺寸来,因此就会产生图片变形的情况。
sx,sy,swidth,sheight,dx,dy,dwidth,dheight这个比较难以理解,前四个是对原始图片的操作,也就获取原始图片的区域,后四个参数就是图片需要绘制在画布上的位置和大小。也就是图片的所选区域放入画布的所选区域。

玩耍地址:

See the PenCanvas N0.4 by cherryvenus (@cherryvenus) onCodePen.

No5. X炸天的特效

  • mark api:
    • context.getImageData 获取图像信息

这个api是最amazing的方法,因为他帮助我们获取了画布的颜色信息,通过这个信息,我们可以重新创造新的图片。这个方法除了体积大,没啥毛病。因为他的data长度是按照 width(宽)*height(高)*4(rgba四个颜色信息) 组成的。

let info=context.getImageData(x,y,width,height)
//返回
data
width
height
复制代码

这里要避免遍历data( data.forEach ),来处理图片信息,因为很大,浏览器容易卡顿。最好按照定位信息,获取当前坐标的颜色信息。

至于最开始的那个特效,我是借助了matter.js这个库,才能完成的。如果是手写特效的话,不如这个库来的生动有趣。

play地址:

See the Pencanvas No5. COOL by cherryvenus (@cherryvenus) onCodePen.

稀土掘金

责编内容by:稀土掘金阅读原文】。感谢您的支持!

您可能感兴趣的

Html5游戏开发-图形与动画(一) 最近研究了一下出来了很久的HTML5,总结了一下,准备来个系列,文中也许有很多问题,欢迎大家指正。 Canvas介绍 canvas用于在网页中绘制图形的一个元素,具体内容请查看 -> HTML5 Canvas 这里说些...
Introducing Tools section and our first tool – An ... Today, we are introducing a brand new section to our site. This new section will hostweb-based Tools for accomplishing ...
canvas动画之动态绘出六边形 先上 demo: http://en.jsrun.net/W5iKp/show 这两天我一直在研究这个动画,花了大量的时间来想是如何实现的, 一开始我是想在进入 canvas 时按时间来用 lineTo 绘出六边...
canvas绘制太阳系 原文地址: http://jeffzhong.space/2017/10/26/solar/ 学习canvas有一段时间了,顺便写个小项目练手,该项目用到的知识点包括: ES6面向对象 基本的三角函数 can...
why the keyboard listener doesn’t work here is my code,i want to move the picture(pic1) with the four arrow keyboards,draw some lines as background are...