JS高程:读书摘要(十三)Canvas

使用

要使用<canvas>元素,必须先设置其widthheight属性,指定可以绘图的区域大小。开始和结束标签中的内容是后备信息,如果浏览器不支持<canvas>元素,就会显示这些信息。使用toDataURL()方法,可以导出在<canvas>元素上绘制的图像。这个方法接受一个参数,即图像的MIME类型格式

<canvas id="drawing" width=" 200" height="200">A drawing of something.</canvas>
<script>
var drawing = document.getElementById("drawing");
    //确定浏览器支持<canvas>元素
    if (drawing.getContext){
        // 取得2D上下文对象
        var context = drawing.getContext("2d");

        //取得图像的数据URI
        var imgURI = drawing.toDataURL("image/png");
        //显示图像
        var image = document.createElement("img");
        image.src = imgURI;
        document.body.appendChild(image);
}
</script>

绘制

1. 填充和描边
//确定浏览器支持<canvas>元素
if (drawing.getContext){
    var context = drawing.getContext("2d");
    context.strokeStyle = "red"; // 描边
    context.fillStyle = "#0000ff"; // 填充
}

之后所有涉及描边和填充的操作都将使用这两个样式,直至重新设置这两个值。这两个属性的值也可以是渐变对象模式对象。上下文对象还有以下属性来设置描边线条样式,lineWidth设置线宽;lineCap控制线条末端形状,("butt"平头、"round"圆头或"square"方头);lineJoin控制相交方式样式("round"圆交、"bevel"斜交或"miter"斜接)

2. 绘制矩形

fillRect()strokeRect()clearRect()。这三个方法都能接收4个参数:矩形的x坐标矩形的y坐标矩形宽度矩形高度。这些参数的单位都是像素

if (drawing.getContext){
    var context = drawing.getContext("2d");
    //绘制红色填充矩形
    context.fillStyle = "#ff0000";
    context.fillRect(10, 10, 50, 50);

    //绘制半透明的蓝色描边矩形
    context.strokeStyle = "rgba(0,0,255,0.5)"; // 也可以使用 rgb ,hsl ,hsla
    context.strokeRect(30, 30, 50, 50);
}

clearRect()方法用于清除画布上的矩形区域。

3. 绘制路径

要绘制路径,首先必须调用beginPath()方法,表示要开始绘制新路径。然后,再通过调用下列方法来实际地绘制路径。

  • arc(x, y, r, sAngle, eAngle, counterclockwise):画圆;xy圆心坐标、半径、开始弧度、结束弧度、是否逆时针(false为顺时针,可选)

  • arcTo(x1, y1, x2, y2, radius):从上一点开始绘制一条弧线,到(x2,y2)为止,并且以给定的半径radius穿过(x1,y1)(相切)。

    arcTo

  • bezierCurveTo(c1x, c1y, c2x, c2y, x, y):从上一点开始绘制一条曲线,到(x,y)为止,并且以(c1x,c1y)(c2x,c2y)为控制点,两个控制点。

    bezierCurveTo

  • lineTo(x, y):从上一点开始绘制一条直线,到(x,y)为止。

  • moveTo(x, y):将绘图游标移动到(x,y),不画线。

  • quadraticCurveTo(cx, cy, x, y):从上一点开始绘制一条二次曲线,到(x,y)为止,并且以(cx,cy)作为控制点,一个控制点。

    quadraticCurveTo

  • rect(x, y, width, height):这个方法绘制的是矩形路径,而不是strokeRect()fillRect()所绘制的独立的形状。

创建路径后,如果是绘制连接路径起点的条线,可以调用closePath();也可以调用fill()方法进行路径填充(应用fillStyle样式);也可以调用stroke()方法进行路径描边(应用strokeStyle样式),也可以调用clip()方法在路径上创建一个剪切区域。

var drawing = document.getElementById("drawing");
//确定浏览器支持<canvas>元素
if (drawing.getContext){
    var context = drawing.getContext("2d");
    //开始路径
    context.beginPath();
    //绘制外圆
    context.arc(100, 100, 99, 0, 2 * Math.PI, false);
    //绘制内圆 移动到内圆圆边上,以免产生不必要的线条
    // moveTo 只移动游标,不画线。
    context.moveTo(194, 100);
    context.arc(100, 100, 94, 0, 2 * Math.PI, false);
    //绘制分针
    context.moveTo(100, 100);
    context.lineTo(100, 15);
    //绘制时针
    context.moveTo(100, 100);
    context.lineTo(35, 100);
    //描边路径
    context.stroke();
}
4. 绘制文本

fillText()和strokeText(),这两个方法都可以接收4个参数:要绘制的文本字符串、x坐标y坐标和可选的最大像素宽度(也就是文本的最大像素宽度,调用时如果传入的字符串大于最大宽度,则绘制的文本字符的高度正确,但宽度会收缩以适应最大宽度)。这两个方法都以下列3个属性为基础且3个属性都有默认值。

  • font:表示文本样式、大小及字体;默认值"10px sans-serif"context.font = "bold 14px Arial";
  • textAlign:表示文本对齐方式。可能的值有"start""end""left""right""center"。建议使用"start""end",不要使用"left""right",因为前两者的意思更稳。默认值"start"
  • textBaseline:表示文本的基线。可能的值有"top""hanging""middle""alphabetic""ideographic""bottom"。默认值"alphabetic"

其中关于textAlign属性,设置为center,则以参数坐标画与y轴的平行线,以该线作为字体分布的中间线;设置为start则是以该线作为文本左端的位置。关于textBaseline,设置为top,则以参数坐标画与x轴的平行线,以该线作为字体分布的顶端;设置为alphabetic,则是以该线作为字母书写线。

measureText()方法利用fonttextAligntextBaseline的当前值计算指定文本的大小。这个方法接收一个参数,即要绘制的文本,返回绘制完该文本需要多大的width值。

// 假设你想在一个140 像素宽的矩形区域中绘制文本Hello world!
var fontSize = 100;
context.font = fontSize + "px Arial";

while(context.measureText("Hello world!").width > 140){
    fontSize-=2;
    context.font = fontSize + "px Arial";
}
// 以100px递减 直到找到合适的fontSize绘制文本以适配140px
5. 变换
  • rotate(angle):围绕原点(0,0)旋转图像angle弧度。

  • scale(scaleX, scaleY):缩放图像,在x方向乘以scaleX,在y 方向乘以scaleYscaleXscaleY的默认值都是1.0

  • translate(x, y):将坐标原点移动到(x,y)。执行这个变换之后,坐标(0,0)会变成之前由(x,y)表示的点。

  • transform(a, b, c, d, e, f):修改变换矩阵,a,d代表缩放的值,默认为1e,f代表平移的值,默认为0b,c代表倾斜的值,默认为0。即默认``transform(1, 0, 0, 1, 0, 0)`

// 如果第一个正方形要旋转45°并且放大两倍,位移到(100,100)
var context = drawing.getContext("2d");
var deg = Math.PI/180;
context.fillRect(200,0,50,50) 
// 变化前的正方形显示在(200,0),宽高50

ctx.setTransform(1,0,0,1,0,0);
ctx.beginPath();
context.transform(2,0,0,2,0,0);
context.transform(Math.cos(45*deg),Math.sin(45*deg),-Math.sin(45*deg),Math.cos(45*deg),100,100);
ctx.fillRect(200,0,50,50); 
// 变换后的正方形 宽高100,与x轴平行线相交45度 ,左上角坐标(300,100)
// 或者直接context.transform(2*Math.cos(deg*45),2*Math.sin(deg*45),-2*Math.sin(deg*45),2*Math.cos(deg*45),0,0);
// 记忆点CS-SC
  • setTransform(a, b, c, d, e, f):将变换矩阵重置为默认状态,然后再调用transform()

无论是刚才执行的变换,还是fillStylestrokeStyle 等属性,都会在当前上下文中一直有效,除非再对上下文进行什么修改。可以调用save()方法保存当时的样式设置,这个设置都会进入一个栈结构,得以妥善保管。然后可以对上下文进行其他修改。等想要回到之前保存的设置时,可以调用restore()方法,在保存设置的栈结构中向前返回一级,恢复之前的状态。连续调用save()可以把更多设置保存到栈结构中,之后再连续调用restore()则可以一级一级返回。

context.fillStyle = "red";
context.save();

context.fillStyle = "green";
context.translate(100, 100);
context.save();

context.fillStyle = "blue"; 
// 执行此行代码之前 100,100 green。
// 执行时平移已经完成 执行后只修改了颜色 所以变成 100,100 blue
context.fillRect(0, 0, 100, 200); 
//从点(100,100)开始绘制蓝色矩形

context.restore(); 
// 这次释放 blue被保存的green 替换 。变成100,100 green
context.fillRect(10, 10, 100, 200);
//从点(110,110)开始绘制绿色矩形

context.restore();
// 这次释放 变换就被取消了 释放之后 100,100,green 变成 red 
context.fillRect(0, 0, 100, 200); //从点(0,0)开始绘制红色矩形

save()方法保存的只是对绘图上下文的设置和变换,不会保存绘图上下文的内容。

6. 绘制图像

如果你想把一幅图像绘制到画布上,可以使用drawImage()方法,该可以使用三种不同的参数组合。

var image = document.images[0];
context.drawImage(image, 10, 10);
// img元素 绘制图像的起点坐标 (与图片大小一致)

context.drawImage(image, 50, 10, 20, 30);
// img元素 绘制图像的起点坐标 目标宽高 (大小取决于后两个参数)

context.drawImage(image, 0, 10, 50, 50, 0, 100, 40, 60);
// img元素 源图片-->坐标x,y,width,height, 画在哪-->坐标,宽高
// 可以理解为(从源图片的哪一点作为左上角截出一个多大的矩形画在画布的哪一点 以多大的宽高展示)

第一个参数除了能穿img元素外,也可以传另一个canvas元素,这样就可以把另一个画布内容绘制到当前画布上。

对画布的操作结果,可以使用canvas对象的toDataURL()方法获得,但是图片不能来自其他域,否则会抛出错误。

7.阴影

2D 上下文会根据以下几个属性的值,自动为形状或路径绘制出阴影。

  • shadowColor:用CSS颜色格式表示的阴影颜色,默认为黑色。
  • shadowOffsetX:形状或路径x轴方向的阴影偏移量,默认为0。
  • shadowOffsetY:形状或路径y轴方向的阴影偏移量,默认为0。
  • shadowBlur:模糊的像素数,默认0,即不模糊。
var context = drawing.getContext("2d");
//设置阴影
context.shadowOffsetX = 5;
context.shadowOffsetY = 5;
context.shadowBlur = 4;
context.shadowColor = "rgba(0, 0, 0, 0.5)";
//绘制红色矩形
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50);
//绘制蓝色矩形
context.fillStyle = "rgba(0,0,255,1)";
context.fillRect(30, 30, 50, 50);

// 红上蓝下 拥有相同的阴影  
8.渐变
  • createLinearGradient()

创建线性渐变,接收4 个参数:起点的x 坐标、起点的y 坐标、终点的x 坐标、终点的y 坐标。创建了渐变对象后,下一步就是使用addColorStop()方法来指定色标。这个方法接收两个参数:色标位置和CSS 颜色值。色标位置是一个0(开始的颜色)到1(结束的颜色)之间的数字。

var gradient = context.createLinearGradient(30, 30, 70, 70);
gradient.addColorStop(0, "white");
gradient.addColorStop(1, "black");
// 连线两点:(30,30)和(70,70),以此为对角线取一个矩形范围,从white到black开始渐变
// 然后就可以把fillStyle 或strokeStyle 设置为这个对象,从而使用渐变来绘制形状或描边:

//绘制红色矩形
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50);
//绘制渐变矩形
context.fillStyle = gradient;
context.fillRect(30, 30, 50, 50);

// 渐变的范围在x轴30~70,y轴30~70组成的矩形 
// 所以绘制渐变矩形的起点坐标 所以要看到全部的渐变效果 绘制的起点坐标要设置为(30,30)

确保渐变与形状对齐非常重要,有时候可以考虑使用函数来确保坐标合适。

function createRectLinearGradient(context, x, y, width, height){
    return context.createLinearGradient(x, y, x+width, y+height);
}
//  此时只需要考虑在画布的哪个地方创建一个多大的范围的渐变就可以了
//  显示全部渐变范围的终点坐标已经被计算出来
  • createRadialGradient()

创建径向渐变。这个方法接收6 个参数,对应着两个圆的圆心和半径。如果想从某个形状的中心点开始创建一个向外扩散的径向渐变效果,就要将两个圆定义为同心圆。如果把一个圆形开口定义得比另一个小很多,那这个圆桶就变成了圆锥
体,而通过移动每个圆形开口的位置,就可达到像旋转这个圆锥体一样的效果。

var gradient = context.createRadialGradient(55, 55, 10, 55, 55, 30);
gradient.addColorStop(0, "white");
gradient.addColorStop(1, "black");
//绘制红色矩形
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50);
//绘制渐变矩形
context.fillStyle = gradient;
context.fillRect(30, 30, 50, 50);
9.模式

模式其实就是重复的图像, 可以用来填充或描边图形。要创建一个新模式, 可以调用createPattern()方法并传入两个参数:一个HTML <img>元素和一个表示如何重复图像的字符串。其中,第二个参数的值与CSSbackground-repeat 属性值相同,包括"repeat""repeat-x""repeat-y""no-repeat"

var image = document.images[0],
    pattern = context.createPattern(image, "repeat");
//绘制矩形
context.fillStyle = pattern;
context.fillRect(10, 10, 150, 150);

模式与渐变一样,都是从画布的原点(0,0)开始的。将填充样式(fillStyle)设置为模式对象。即图片在画布上,从画布的原点(0,0)开始平铺的重复,调用fillRect(10, 10, 150, 150),相当于在坐标(10,10)处开了一个150宽高的视口去看到被平铺的图像,实际上重复是发生在整个画布上的。

10.使用图像数据

可以通过getImageData()取得画布原始图像数据。这个方法接收4个参数:要取得其数据的画面区域的x 和y 坐标以及该区域的像素宽度和高度。

var imageData = context.getImageData(10, 5, 50, 50);

这里返回的对象是ImageData的实例。每个ImageData 对象都有三个属性:widthheightdata。其中data 属性是一个数组,保存着图像中每一个像素的数据。(数组的每4项都代表1pxrgba四个值,这些值都是0~255之间,这些值也可以被修改)

11. 合成
  • globalAlphaglobalCompositionOperation

globalAlpha 是一个介于0 和1 之间的值(包括0 和1),用于指定所有绘制的透明度。默认值为0。context.globalAlpha = 0.5;

globalCompositionOperation 表示后绘制的图形怎样与先绘制的图形结合。

  • source-over(默认值):后绘制的图形位于先绘制的图形上方。
  • source-in:后绘制的图形与先绘制的图形重叠的部分可见,两者其他部分完全透明。
  • source-out:后绘制的图形与先绘制的图形不重叠的部分可见,先绘制的图形完全透明。
  • source-atop:后绘制的图形与先绘制的图形重叠的部分可见,先绘制图形不受影响。
  • destination-over:后绘制的图形位于先绘制的图形下方,只有之前透明像素下的部分才可见。
  • destination-in:后绘制的图形位于先绘制的图形下方,两者不重叠的部分完全透明。
  • destination-out:后绘制的图形擦除与先绘制的图形重叠的部分。
  • destination-atop:后绘制的图形位于先绘制的图形下方,在两者不重叠的地方,先绘制的图形会变透明。
  • lighter:后绘制的图形与先绘制的图形重叠部分的值相加,使该部分变亮。
  • copy:后绘制的图形完全替代与之重叠的先绘制图形。
  • xor:后绘制的图形与先绘制的图形重叠的部分执行“异或”操作。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • 一:canvas简介 1.1什么是canvas? ①:canvas是HTML5提供的一种新标签 ②:HTML5 ...
    GreenHand1阅读 4,660评论 2 32
  • canvas元素的基础知识 在页面上放置一个canvas元素,就相当于在页面上放置了一块画布,可以在其中进行图形的...
    oWSQo阅读 10,268评论 0 19
  • 元素负责在页面中设定一个区域,然后就可以通过 JavaScript 动态地在这个区域中绘制图形。 一、基本用法 要...
    LemonnYan阅读 18,549评论 0 7
  •   HTML5 添加的最受欢迎的功能就是 元素。这个元素负责在页面中设定一个区域,然后就可以通过 JavaScri...
    霜天晓阅读 2,987评论 0 2
  • 跟你说个事,Y神秘的跟我说,F退学了,今天已经去院里交了退学申请书了。 什么?退学?我诧异地睁大眼睛,怀疑自己听错...
    渐和阅读 1,241评论 3 0