最先知道 canvas 的 globalCompositeOperation 属性,是在须要实现一个刮刮卡效果的时候,当时也就是网上找到刮刮卡的效果赶忙完成任务就完了,此次又学习一次,但愿能加深理解吧。javascript
先来看下 canvas 的 globalCompositeOperation属性,具体是干什么的。html
globalCompositeOperation 属性设置或返回如何将一个源(新的)图像绘制到目标(已有)的图像上。
源图像 = 您打算放置到画布上的绘图。
目标图像 = 您已经放置在画布上的绘图。
这个属性用来设置要在绘制新形状时应用的合成操做的类型,好比在一个蓝色的矩形上画一个红色的圆形,是红色在上显示,仍是蓝色在上显示,重叠的部分显示仍是不显示,不重叠的部分又怎么显示,等一些状况,在面对这些状况的时候,就是 globalCompositeOperation
属性起做用的时候了。
在取默认值的状况下,都是显示的,新画的图形会覆盖原来的图形。java
默认值: source-over
语法: context.globalCompositeOperation="source-in";
canvas
表格中的蓝色矩形为目标图像,红色圆形为源图像。跨域
属性值 | 描述 | 效果 |
---|---|---|
source-over | 默认。在目标图像上显示源图像。 | ![]() |
source-atop | 在目标图像顶部显示源图像。源图像位于目标图像以外的部分是不可见的。 | ![]() |
source-in | 在目标图像中显示源图像。只有目标图像内的源图像部分会显示,目标图像是透明的。 | ![]() |
source-out | 在目标图像以外显示源图像。只会显示目标图像以外源图像部分,目标图像是透明的。 | ![]() |
destination-over | 在源图像上方显示目标图像。 | ![]() |
destination-atop | 在源图像顶部显示目标图像。源图像以外的目标图像部分不会被显示。 | ![]() |
destination-in | 在源图像中显示目标图像。只有源图像内的目标图像部分会被显示,源图像是透明的。 | ![]() |
destination-out | 在源图像外显示目标图像。只有源图像外的目标图像部分会被显示,源图像是透明的。 | ![]() |
lighter | 显示源图像 + 目标图像。 | ![]() |
copy | 显示源图像。忽略目标图像。 | ![]() |
xor | 使用异或操做对源图像与目标图像进行组合。 | ![]() |
好的,下来实现一个水滴扩散的效果
效果图 数组
实现思路 函数
在一个 canvas 上先画出黑白色的图片,而后设置背景是一张彩色的图片,鼠标点击时,设置 canvas 的 globalCompositeOperation
属性值为 destination-out
,根据鼠标在 canvas 中的 坐标,用一个不规则的图形逐渐增大,来擦除掉黑白色的图片,就能够慢慢显示彩色的背景了。学习
也就是说咱们须要三张图片 url
黑白的图片spa
彩色的图片
不规则形状的图片
代码
<!doctype html> <html> <head> <meta charset="UTF-8"> <style> canvas { /* 设置鼠标的光标是一张图片, 16和22 分别表示热点的X坐标和Y坐标 */ /* https://developer.mozilla.org/zh-CN/docs/Web/CSS/cursor/url */ cursor: url('https://www.kkkk1000.com/images/mouse.png') 16 22, auto; } </style> </head> <body> <canvas id="canvas" width="400px" height="250px"></canvas> <script type="text/javascript"> var canvas = document.getElementById("canvas"); var context = canvas.getContext("2d"); // 保存图片路径的数组 var urlArr = ["https://www.kkkk1000.com/images/bg2.png", "https://www.kkkk1000.com/images/clear.png"]; // imgArr 保存加载后的图片的数组,imgArr中保存的是真实的图片 // loadImg 函数用来加载 urlArr 中全部的图片 // 并返回一个保存全部图片的数组 var imgArr = loadImg(urlArr); // flag 用来限制 点击事件,一张图片只会产生一次效果 var flag = false; function loadImg(urlArr) { var index = 0; var res = []; // 每次给 load 函数传入一个图片路径,来加载图片 load(urlArr[index]); function load(url) { // 若是 index 等于 urlArr.length, // 表示加载完 所有图片了,就结束 load函数 if (index == urlArr.length) { // 加载彻底部图片,调用 init 函数 init(); return; } var img = new Image(); img.src = url; // 无论当前图片是否加载成功,都要加载下一张图片 img.onload = next; img.onerror = function () { console.log(res[index] + "加载失败"); next(); } // next 用来加载下一张图片 function next() { // 把加载后的图片,保存到 res 中 res[index] = img; load(urlArr[++index]) } } // 最后返回保存全部真实图片的数组 return res; } function init() { // 先在canvas上画黑白的图片,而后再设置背景是彩色的图片 // 避免先显示出彩色图片,再显示出黑白的图片 context.globalCompositeOperation = "source-over"; context.drawImage(imgArr[0], 0, 0, 400, 250); canvas.style.background = 'url(https://www.kkkk1000.com/images/bg.jpg)'; canvas.style.backgroundSize = "100% 100%"; // flag 是 true 时,鼠标点击才有水滴扩散的效果 flag = true; // canvas 绑定点击事件,点击时产生水滴扩散效果 canvas.onclick = diffusion; } // width 表示 不规则形状的图片的尺寸 var width = 0; // speed 表示扩散效果的速度 var speed = 8; // diffusion 函数根据鼠标坐标,产生效果 function diffusion (e) { if (flag) { flag = false; context.globalCompositeOperation = "destination-out"; window.requestAnimationFrame(draw); // 根据鼠标坐标,画扩散效果 function draw() { // 这里不必定须要是 1800 ,但必须是一个足够大的数,能够扩散出整张背景图 if (width > 1800) { flag = true; return; } width += speed; // 获取鼠标相对于 canvas 的坐标 var x = e.layerX; var y = e.layerY; // 画不规则形状的图片,逐渐增大图片尺寸 context.drawImage(imgArr[1], x - (width / 2), y - (width / 2), width, width); window.requestAnimationFrame(draw); } } } </script> </body> </html>
咱们继续来实现一个刮刮卡的效果
效果图
刮刮卡效果实现的思路:
一个 canvas 上先画一层灰色,而后设置canvas的背景图,设置 canvas 的 globalCompositeOperation
属性值为 destination-out
,点击并移动时,根据移动点的坐标,擦除掉灰色,当擦掉一部分时,再自动擦除掉所有灰色,显示出背景来。
刮刮卡的效果和水滴扩散的效果,在开始的时候几乎是同样的,不过水滴扩散效果,用的是一张不规则形状的图片来清除黑白图片,而刮刮卡效果,是经过画线的方式,线比较粗而已,来清除上面的灰色。
主要的不一样是,刮刮卡效果最后须要自动擦除掉所有灰色,这里有两种方式。
第一种
使用 canvas 的 getImageData 方法,来获取 canvas 上的像素信息,这个方法返回的对象的 data 属性是一个一维数组,包含以 RGBA 顺序的数据,数据使用 0 至 255(包含)的整数表示,详细的能够看看 canvas 的像素操做。
用这个方法来判断有多少已经擦除掉了,也就是经过一个变量来记录有多少像素的RGBA的值是0,当变量的值超过某一个值时,就清除所有灰色。
代码在这里。
第二种
就直接看移动了多少,鼠标移动时,会有一个变量进行自增运算,当这个变量,超过必定值时,就擦除所有灰色。
代码在这里。
注意:
第一种方式使用 getImageData 存在跨域问题,不过由于这个效果中,没有在canvas上画图片,而是设置canvas的 background
为一张图片,因此这个尚未影响,可是若是canvas上画了其余图片,就可能须要处理跨域的问题了。
使用 getImageData 能获取到 canvas 上的像素信息,就能够根据刮刮卡上灰色的面积,决定擦除所有灰色的时机,更加灵活。
第二种方式,虽然不存在跨域的问题,可是,不能很好的根据刮刮卡上灰色的面积,控制最后擦除所有灰色的时机。
文章中的效果主要是使用 globalCompositeOperation
属性取值为 destination-out
,而取值为其余值的时候,一样也是能够制做出各类效果的,你们也能够发挥本身的想象力,去试试其它值,也许有新发现呢。