Canvas 学习笔记(高级篇) 变形、动画、像素操作

状态保存和恢复

save()保存画布 (canvas) 的所有状态
restore() save 和 restore 方法是用来保存和恢复 canvas 状态的,都没有参数。Canvas 的状态就是当前画面应用的所有样式和变形的一个快照。

Canvas 状态存储在栈中,每当save()方法被调用后,当前的状态就被推送到栈中保存。一个绘画状态包括:

当前应用的变形(即移动,旋转和缩放)
以及下面这些属性:strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, lineDashOffset, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation, font, textAlign, textBaseline, direction, imageSmoothingEnabled
当前的裁切路径(clipping path)

变形

translate(x, y) 其中x 是左右偏移量,y 是上下偏移量
设定后xy轴偏移量后,比如画矩形、圆等他的中心点需要加上偏移量,比如:

var ctx = canvas.getContext('2d');
ctx.strokeRect(0, 0, 40, 40); //画一个矩形
ctx.translate(20, 20); //x,y轴都平移20 下面的操作都会受到变形的影响
ctx.arc(0, 0, 20, 0, Math.PI * 2, true);//画圆,这里的圆心需要加上偏移量(0+20,0+20)即为(20,20)
ctx.stroke() //圆变成实体线条
ctx.fillStyle = '#2196f3' //填充颜色
ctx.fillRect(20,0,30,30) //画矩形 此时矩形的起始位置需要加上偏移量(20+20,0+20)即为(40,20)
image.png

rotate(angle) 旋转的角度 (angle),它是顺时针方向的,以弧度为单位的值
需要注意的是:默认以原点为中心旋转(0,0),如果需要改变它,可以使用translate方法
如:

var ctx = canvas.getContext('2d');
ctx.strokeRect(0, 0, 100, 100);//画矩形
ctx.translate(100,100) //改变中心点为(100,100)
for (let k = 1; k <= 12; k++) {
    ctx.beginPath()
    ctx.rotate(Math.PI / 6);
    ctx.arc(20, 20, 20, 0, Math.PI * 2, true);
    ctx.fillStyle = 'rgb(' + (20 * k) + ',' + (255 - 20 * k) + ',255)';
    ctx.fill()
}

image.png

scale(x, y) x 为水平缩放因子,y 为垂直缩放因子,如果比 1 小,会缩小图形,如果比 1 大会放大图形。默认值为 1,为实际大小。
transform(a, b, c, d, e, f)a:水平方向的缩放 b:竖直方向的倾斜偏 c:水平方向的倾斜偏移d:竖直方向的缩放e:水平方向的移动f:竖直方向的移动
clip()将当前正在构建的路径转换为当前的裁剪路径。
举例:使用裁剪路径,绘制头像

var ctx = canvas.getContext('2d');
ctx.translate(60,60);
var img = new Image();
img.src = './images/1310CB021GZ.jpg';
img.onload = function() {
    ctx.beginPath();
    ctx.arc(0, 0, 40, 0, Math.PI * 2, true);
    ctx.save();
    ctx.clip();//创建圆的裁剪路径
    ctx.drawImage(img,-40,-40,80,80);
    ctx.restore();
    ctx.closePath();
}
image.png

像素操作

ImageData对象中存储着 canvas 对象真实的像素数据,它包含以下几个只读属性
width:图片宽度,单位是像素 height:图片高度,单位是像素 data: Uint8ClampedArray 类型的一维数组包含着 RGBA 格式的整型数据,范围在 0 至 255 之间。

创建一个 ImageData 对象
var myImageData =ctx.createImageData(width, height);
得到场景像素数据
var myImageData = ctx.getImageData(left, top, width, height);
在场景中写入像素数据
ctx.putImageData(myImageData, dx, dy);

ctx.imageSmoothingEnabled 用来设置图片是否平滑的属性,true 表示图片平滑(默认值),false 表示图片不平滑。

比如:演示图片灰度

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var img = new Image();
img.crossOrigin = 'anonymous';
img.src = './assets/rhino.jpg';
img.onload = function() {
    ctx.drawImage(img, 0, 0);
};

var grayscale = function() {
    ctx.drawImage(img, 0, 0);
    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);//拿到所有像素点
    const data = imageData.data;
    for (var i = 0; i < data.length; i += 4) {
        var avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
        data[i]     = avg; // red
        data[i + 1] = avg; // green
        data[i + 2] = avg; // blue
    }
    //处理 像素数据 取平均值
    ctx.putImageData(imageData, 0, 0);
};

比如:颜色选择器

var img = new Image();
img.crossOrigin = 'anonymous';
img.src = './assets/rhino.jpg';
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
img.onload = function() {
  ctx.drawImage(img, 0, 0);
  img.style.display = 'none';
};
var hoveredColor = document.getElementById('hovered-color');
var selectedColor = document.getElementById('selected-color');


function pick(event, destination) {
  var x = event.layerX;
  var y = event.layerY;
  var pixel = ctx.getImageData(x, y, 1, 1);
  var data = pixel.data;

    const rgba = `rgba(${data[0]}, ${data[1]}, ${data[2]}, ${data[3] / 255})`;
    destination.style.background = rgba;
    destination.textContent = rgba;

    return rgba;
}

canvas.addEventListener('mousemove', function(event) {
    pick(event, hoveredColor);
});
canvas.addEventListener('click', function(event) {
    pick(event, selectedColor);
});

保存图片

canvas.toDataURL('image/png')默认设定。创建一个 PNG 图片。
canvas.toDataURL('image/jpeg', quality)创建一个 JPG 图片。你可以有选择地提供从 0 到 1 的品质量
canvas.toBlob(callback, type, encoderOptions)创建了一个在画布中的代表图片的 Blob 对像

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容