三、Canvas基本绘图

Canvas绘图(二)

本章将学习Canvas绘图的以下技巧

  • 裁剪区域
  • 图象合成
  • 简单的Canvas变换

一、设置裁切区域

使用Canvas裁切区域可以限制路径以及子路径的绘制区域,通过rect()函数设置一个用来绘图的矩形区域环境属性;然后调用clip()函数用rect()函数定义的矩形区域设置为裁切区域。那么,无论当前环境绘制什么内容,他只显示裁切区域以内的部分,也可以理解成绘图操作的一种蒙版。例如

drawClip() {
    // 绘制一个大方块
    this.context.fillStyle = "#66ccff";
    this.context.fillRect(10, 10, 200, 200);
    this.context.save();
    this.context.beginPath();

    // 裁切画布从(0,0)点到50 x 50的正方形
    this.context.rect(0, 0, 50, 50);
    this.context.clip();

    // 红色圆
    this.context.beginPath();
    this.context.strokeStyle = "red";
    this.context.lineWidth = 5;
    this.context.arc(100, 100, 100, 0, (Math.PI / 180) * 360, false);

    // 整圆
    this.context.stroke();
    this.context.closePath();

    this.context.restore();

    // 再次裁切整个画布
    this.context.beginPath();
    this.context.rect(0, 0, 500, 500);
    this.context.clip();

    // 裁切一个没有 裁切的 蓝线
    this.context.beginPath();
    this.context.strokeStyle = "blue";
    this.context.lineWidth = 5;
    this.context.arc(100, 100, 50, 0, (Math.PI / 180) * 360, false);
    this.context.stroke();
    this.context.closePath();
}
image-20200927115701655.png

首先,我们创建了50*50的矩形区域作为裁切区域,然后绘制一个红色圆弧形,但是所画的红色圆弧线只能看到在这个矩形以内的部分(有点类似overflow :hidden),最后将裁切区域调整为500 * 500,画一个蓝色圆弧。

二、合成

合成是指如何精细控制画布上对象的透明层与分层效果,有两个属性可以控制。 在Canvas中,默认情况下把Canvas之中的某个物体源绘制到另一个物体(目标)上时,浏览器就会简单的把源物体叠放在目标图像上。

另外,Canvas每次绘制一个图形,就会将原有的Canvas图像作为目标图像,将将要绘制的图像作为源图像

  • globalAlpha : 该属性的默认值为1.0(完全不透明),取值范围为0.,0 ~1.0
  • globalCompositeOperation : 该属性在globalAlpah以及所有变换生效后,控制如何在当前Canvas位图中绘制图形,它由以下枚举值
属性 说明
source-over(默认) 默认。在目标图像上显示源图像。
source-atop 在目标图像顶部显示源图像。源图像位于目标图像之外的部分是不可见的。
source-out 在目标图像之外显示源图像。只会显示目标图像之外源图像部分,目标图像是透明的。
source-in 在目标图像中显示源图像。只有目标图像内的源图像部分会显示,目标图像是透明的。
destination-over 在源图像上方显示目标图像。
destination-atop 在源图像顶部显示目标图像。源图像之外的目标图像部分不会被显示。
destination-in 在源图像中显示目标图像。只有源图像内的目标图像部分会被显示,源图像是透明的
destination-out 在源图像外显示目标图像。只有源图像外的目标图像部分会被显示,源图像是透明的。
copy 显示源图像。忽略目标图像。
xor 使用异或操作对源图像与目标图像进行组合。
lighter 显示源图像 + 目标图像。

A. souce-over

this.context.fillStyle = "black";
this.context.fillRect(10, 10, 200, 200);
// 红色正方形会叠加到黑色上面
this.context.globalCompositeOperation = "source-over";
this.context.fillStyle = "red";
this.context.fillRect(1, 1, 50, 50);
image-20200927143312392.png

B. souce-atop

this.context.globalCompositeOperation = "source-atop";
this.context.fillRect(60, 1, 50, 50);
image-20200927143414050.png

C. source-in

只有目标图像内的源图像部分会显示,目标图像是透明的

this.context.globalCompositeOperation = "source-in";
this.context.fillRect(1, 60, 50, 50);
image-20200927143533513.png

D. destination-over

this.context.globalCompositeOperation = 'destination-over'
this.context.fillRect(1, 1, 50, 50);
image-20200927143654071.png

E. destination-atop

this.context.globalCompositeOperation = 'destination-atop'
this.context.fillRect(1, 1, 50, 50);
image-20200927143920874.png

F. destination-in

this.context.globalCompositeOperation = 'destination-in'
this.context.fillRect(1, 1, 50, 50);
image-20200927144044802.png

G.destination-out

this.context.globalCompositeOperation = 'destination-out'
this.context.fillRect(1, 1, 50, 50);
image-20200927144140182.png

三、简单画布转换

画布变换是指用数学方法调整绘制形状的物理数学,例如缩放与旋转,所有变换依赖后台的数学矩阵运算,我们不必去理解这些运算,我们将讨论如何调整Canvas属性来应用旋转和缩放变换。

这里说一下 Canvas坐标变换的方式 :

  • 平移 : translate
  • 缩放 : scale
  • 旋转 : rotate

而上面三种方式,都可以通过transform()矩阵变换做到,transform(m11,m12,m22,dx,dy),这个之后单独开一章节讲。

A. 旋转和平移变换

例如,我们对一个正方形进行旋转 .

const x = 100,
      y = 100,
      width = 50,
      height = 50,
      angleRadians = (45 * Math.PI) / 180;
this.context.translate(x + 0.5 * width, y + 0.5 * height);
this.context.rotate(angleRadians);
this.context.fillStyle = "red";
this.context.fillRect(-0.5 *width, -0.5 * height, width, height);
image-20200927153307450.png

值得注意以下几点 :

  • translate() : 可以设置画布的原点,默认(0,0),这里设置为红色正方形的中点,那么就可以实现围绕中心点进行旋转了
  • fillRect(-05 * width,-0.5 * height...): 为什么绘制的原点是(-25,-25)呢?是因为原点改变了,现在是(125,125),所以要从(-25,-25)开始绘制

例如绘制多个正方形

drawRotateRects() {
    this.drawRect(50, 100, 40, 40, 45);
    this.drawRect(100, 100, 40, 40, 75);
    this.drawRect(150, 100, 40, 40, 90);
    this.drawRect(200, 100, 40, 40, 120);
}

drawRect(x, y, width, height, angle) {
    this.context.setTransform(1, 0, 0, 1, 0, 0);
    const angleInRadians = (angle * Math.PI) / 180;
    this.context.translate(x + 0.5 * width, y + 0.5 * height);
    this.context.rotate(angleInRadians);
    this.context.fillStyle = "red";
    this.context.fillRect(-0.5 * width, -0.5 * height, width, height);
}
image-20200927164013384.png

this.context.setTransform(1, 0, 0, 1, 0, 0);这段代码你可以理解为重置Canvas变换设置

B. 缩放变换

接口 说明
context.scale(x,y) x为x轴的缩放属性,y为y轴缩放属性,默认为1
如果要把一个对象放大两倍,则可以将两个参数都设为2
drawScaleRect() {
    this.context.fillRect(100, 100, 50, 50);
    this.context.setTransform(1, 0, 0, 1, 0, 0);
    this.context.scale(2, 2);
    this.context.fillStyle = "red";
    this.context.fillRect(100, 100, 50, 50);
}
image-20200927170139949.png

这段代码工作方式与旋转差不多,由于没有平移原点对正方形进行缩放,而仍然用画布左上角作为原点,因此红色的正方形向右下方移动了。可以从中心点进行缩放,代码如下所示

drawScaleRectByCenter() {
    this.context.fillRect(100, 100, 50, 50);
    this.context.globalCompositeOperation = 'destination-over'
    this.context.setTransform(1, 0, 0, 1, 0, 0);
    const x = 100,
    y = 100,
    width = 50,
    height = 50;
    this.context.translate(x + 0.5 * width, y + 0.5 * height);
    this.context.scale(2, 2);
    this.context.fillStyle = "red";
    this.context.fillRect(-0.5 * width, -0.5 * height, width, height);
}
image-20200927170539764.png

最佳实践 : 任何形状的中心点都是(x + 0.5 * width, y + 0.5 * height)

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