对于“前端canvas骚操作”的一些理解

canvas ,作为HTML5的「画布」,canvas强大的功能将一直伴随H5走下去。

前端使用canvas一般有两种方式:

<canvas class="canvas" width="" height=""></canvas>

然后通过JavaScript去操控一些属性比如
文字:

ctx.fillText("一段文字",左上角X坐标值,左上角Y坐标值);    //填充
//或
ctx.strokeText("一段文字",左上角X坐标值,左上角Y坐标值);   //镂空

图片:

ctx.drawImage(img对象,左上角X坐标,左上角Y坐标);

线条:

ctx.moveTo(0,0);   //移动到【画布的】(0,0)坐标处“落笔”
ctx.lineTo(100,100);   //从画布的(0,0)处“画”到(100,100)处

或图形:

ctx.arc(左上角X,左上角Y,圆心坐标,弧度,false顺时针/true逆时针);
ctx.stroke();
//
ctx.strokeRect(左上角X,左上角Y,,);

当然,上面这些都有一个“前提”:

//获取canvas元素
var canvas=document.querySelector('.canvas');
//获取“上下文”对象
var ctx=canvas.getContext("2d");

这第二种方式就是【JavaScript动态生成canvas并控制】了:

var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');

(这种方式倒是经常用在“需要转为图片再追加到页面上”的场景下)
这些基础内容就不再多说。
这里要说的是【图片和canvas的“爱恨情仇”】,各位瓜子备好:

上面说到img在canvas的使用方法:ctx.drawImage(img对象,左上角X坐标,左上角Y坐标);
img对象哪里来的呢?通常是我们自行创建的:

var img=new Image()
img.src=xxx;

对了,这里说的是img对象 !在实际使用中,你可能经常能看到下面这样的代码(你可能已经非常熟悉):

var img=new Image()
var imgt=document.querySelector('img')
img.src=imgt.getAttribute("src")

这样就行了吗?就可以操作了吗?

这里其实有一个“致命的错误”:img对象是需要【等待加载】的 —— 请想一下,在网页优化中,是不是经常用「 document.readyState == "complete"window.addEventListener('DOMContentLoaded',function(){}) 」之流来处理“游离于文档流之外的”图片加载请求?
所以我们在处理JavaScript中的图片对象时,也要这么做:

img.onload=function(){
}

然后再在其内部实现对图片的操作,尤其是canvas操作

笔者在做一个图片处理的工具时就遇到了这样的问题:因为图片合成时以一张图片为“原始图”,另一张图片做“背景”,那么我们就要让最后的图片和“第一张图片”的大小保持一致:这就需要获取到“原始图的宽高”了:

cid=document.querySelector('img[id="iim"]').offsetWidth
chd=document.querySelector('img[id="iim"]').offsetHeight

(img[id=“iim”]是其中一个js创建的图片的id,用于区分其它图片)
但这时笔者遇到了当时的一个难题:在加载图片后直接获取宽高在生成的canvas里时而能取到时而没有值!
当时笔者以为是其它原因,就加了个默认替换值;但是后来偶尔写到其它项目时发现了一点:我没有考虑图片加载 —— 如你所想:它是个异步的过程!
于是,我将上面这段代码“抽离”出主要逻辑,单独拿到onload中去:

imgt.onload=function(){
	// console.log(imgt)
	cid=document.querySelector('img[id="iim"]').offsetWidth
	chd=document.querySelector('img[id="iim"]').offsetHeight
}

这样果然就没有再错过!

canvas操作还有一个比较【蜜汁操作】的点:canvas像素级操作!

let imageData = ctx.getImageData(左上角X坐标,左上角Y坐标,,)
ctx.putImageData(imageData,左上角X坐标,左上角Y坐标)

get和put的基础是“存在canvas画布” —— 你可以通过像素级操作新的API:ctx.createImageData(宽,高) 或者仍然通过drawImage()来完成。

哦,这里说到像素级操作可并不是“无的放矢”:它里面有一个非常重要的操作 —— 图片透明度处理Alpha
我们应该知道:一个像素占4个单位——在imageData(的data)中,这四个单位分别被称之为

  • r
  • g
  • b
  • A:Alpha,透明度,0—255,0为全透明,255为不透明

这里要吐槽一句:本来写这个的博客就不多,其中一些博主还在用0-1表示透明度A。当时被坑惨了。。。

比如笔者在项目中就这样用的:

contextt.drawImage(fimg2, 0, 0, vid, vhd);
	let imageData = contextt.getImageData(0, 0, vid, vhd)
		, data = imageData.data
	//对像素集合中的单个像素进行循环,每个像素是由4个通道组成,所以是 i=i+4
	for(let i = 0; i < data.length; i+=4) {
		//let r = data[i]
      	//	, g = data[i+1]
      	//	, b = data[i+2]
		data[i+3] = 80
	}	  
contextt.putImageData(imageData, 0, 0)

如上面我们得到canvas-RGBA的值后其实还可以进行一些“特殊操作”,比如将png图的白色背景转为透明:

//在for循环内
if([r,g,b].every(v => v < 256 && v > 245)) data[i+3] = 0

哦对了,getImageData 似乎还经常玩“跨域”。

HTML5为一些元素提供了支持CORS(跨域资源共享)的属性,这些元素包括

你可能感兴趣的:(前端时间)