canvas 必须懂的方法

  1. 验证浏览器是否支持

    <canvas id="myCanvas" width="400" height="400"> Your browser does not support HTML5 Canvas. </canvas>
    
    function canvasAPP () { 
        var myCanvas = document.getElementById('myCanvas');
        if (!myCanvas || !myCanvas.getContext) { return; } 
        //  这里开始绘制 
        var ctx = myCanvas.getContext('2d');
        function drawScreen () { 
            //  ... 
        }
        drawScreen();
     }
    
  2. 绘制线段

    • ctx.strokeStyle 用于设置画笔绘制路径的颜色、渐变和模式

    • ctx.lineWidth 定义绘制线条的宽度

    • ctx.beginPath() 开始一个新的绘制路径

    • ctx.moveTo(x,y) 移动画笔到指定的坐标点(x,y),该点就是新的子路径的起始点

    • ctx.lineTo(x,y) 使用直线边接当前端点和指定的坐标点(x,y)

    • ctx.stroke() 沿着绘制路径的坐标点顺序绘制直线

    • ctx.closePath() 如果当前的绘制路径是打开的,则关闭掉该绘制路径

    • ctx.setLineDash([]) 绘制虚线,数组内可以传一个元素,两个或者三个分别表示线段,间隔大小

    • ctx.lineCap 线帽,取值有butt、round和square,其中默认的值是butt。

      线帽.png

    • ctx.lineJoin 线段的连接点,取值有round、bevel和miter,其中miter是其默认值,取值为miter时,还可以指定一个miterLimit属性。

  3. 如果绘制一条真正1像素宽的线段,你必须将该线段绘制在某两个像素之间的那个像素中(比如从moveTo(30.5,100.5) lineTo(50.5,120.5)),而不能将它绘制在两个像素的交界处。

    像素边界.png
  1. 对于save()和restore()方法,一开始有一个错误的理解,以为每一步都save()之后restore()就等同于command + z(或者ctrl + z),其实save()保存的只是CanvasRenderingContext2D对象的状态以及对象的所有属性,并不包括这个对象上绘制的图形。官方文档描述如下:

    save()和restore()方法允许你保存和恢复一个CanvasRenderingContext2D对象的状态。save()把当前状态推入到绘图堆栈中,而restore()从绘图堆栈中的顶端弹出最近保存的状态,并且根据这些存储的值来设置当前绘图状态。

  2. 绘制矩形api

    • fillRect(x, y, width, height):绘制一个填充的矩形
    • strokeRect(x, y, width, height):绘制一个矩形的边框
    • clearRect(x, y, width, height):清除指定矩形区域,让清除部分完全透明.经常用来清除干净画布,`ctx.clearRect(0,0,myCanvas.width,myCanvas.height);
    • rect(x, y, width, height):也是Canvas中路径的一个方法,需要配合fill()和stroke()。
    function drawScreen () {
        ctx.strokeStyle = '#000'; 
        ctx.fillStyle = '#9f9' ;
        ctx.lineWidth = 4; 
        // 绘制
        ctx.beginPath(); 
        ctx.rect(30,30,200,200); 
        ctx.stroke(); 
        // 绘制
        ctx.beginPath(); 
        ctx.rect(300,30,200,200); 
        ctx.fill(); 
        // 绘制既有边框又填充,注意因为lineWidth为4,fill方法的值大小
        ctx.strokeRect(240,10,100,100); 
        ctx.fillRect(242,12,96,96);
    }
    
  3. 在Canvas中,CanvasRenderingContext2D对象提供了两个方法(arc()和arcTo())来绘制圆和圆弧。其中arc()即可绘制弧线,圆,也可以绘制扇形,但arcTo()仅能绘制出弧线。但arcTo()可以更轻易的帮助我们实现带圆角的矩形。

    • arc(x, y, radius, startRad, endRad, [anticlockwise]) 坐标点(x,y)为圆心、半么为radius的圆上的一段弧线。这段弧线的起始弧度是startRad,结束弧度是endRad。这里的弧度是以x轴正方向为基准、进行顺时针旋转的角度来计算。其中anticlockwise表示arc()绘制圆或圆弧是以顺时针还是逆时针方向开始绘制。如果其值为true表示逆时针,如果是false表示为顺时针。该参数是一个可选参数,如果没有显式设置,其值是false(也是anticlockwise的默认值)。
    • arcTo(x1, y1, x2, y2, radius) arcTo()方法将利用当前端点、端点一(x1, y1)和端点二(x2, y2)这三点所形成的夹角,然后绘制一段与夹角的两边相切并且半径为radius的圆上的弧线。弧线的起点就是当前端点所在边与圆的切点,弧线的终点就是商端点二(x2,y2)所在边与圆的切点,并且绘制的弧线是两个切点之间长度最短的那个圆弧。此外,如果当前端点不是弧线起点,arcTo()方法还将添加一条当前端点到弧线起点的直线线段。
  4. Canvas坐标变换有移动、旋转和缩放

    • ctx.translate(x, y) :translate方法接受两个参数。x是左右偏移量,y是上下偏移量。当偏移量操作出Canvas的width或height时,坐标将会移出Canvas的画布,这个时候你绘制的东西都将看不到。
    • ctx.rotate(angle) :rotate()方法只接受一个参数,旋转的角度angle,它是顺时针方向的,以弧度为单位的值。
    • ctx.scale(x, y) :scale()方法接受两个参数。x和y分别是横轴和纵轴的缩放因子。其缩放因子默认是1,如果比1小是缩小,如果比1大则放大。在绘制了某个图形后,可以调用ctx.scale(-1, 1)来绘制其水平镜像或者调用ctx.scale(1, -1)来绘制其垂直镜像(这里只是把坐标系翻转,翻转后还需要再绘制一遍要镜像的图形)。
  5. Canvas中可以通过ctx.fillText("内容",x,y,maxWidth)绘制填充文本,ctx.strokeText()可以绘制描边文本,另外通过ctx.textAlign和ctx.textBaseline设置所绘制文本的位置,并且使用ctx.measureText('text').width可以得到所绘制文本text的宽度值,虽然这个值并不精确

  6. 在Canvas中有两个属性globalAlpha和globalCompositeOperation来控制图像合成操作:

    • globalAlpha:设置图像的透明度。globalAlpha属性默认值为1,表示完全不透明,并且可以设置从0(完全透明)到1(完全不透明)。这个值必须设置在图形绘制之前。
    • globalCompositeOperation:该属性的值在globalAlpha以及所有变换都生效后控制在当前Canvas位图中绘制图形
      // 上面是目标图像(已有),下面是源图像(新的);
      // source-over  默认。在目标图像上显示源图像。
      // source-atop  在目标图像顶部显示源图像。源图像位于目标图像之外的部分是不可见的。
      // source-in    在目标图像中显示源图像。只有目标图像内的源图像部分会显示,目标图像是透明的。
      // source-out   在目标图像之外显示源图像。只会显示目标图像之外源图像部分,目标图像是透明的。
      // destination-over 在源图像上方显示目标图像。
      // destination-atop 在源图像顶部显示目标图像。源图像之外的目标图像部分不会被显示。
      // destination-in   在源图像中显示目标图像。只有源图像内的目标图像部分会被显示,源图像是透明的。
      // destination-out  在源图像外显示目标图像。只有源图像外的目标图像部分会被显示,源图像是透明的。
      // lighter  显示源图像 + 目标图像。
      // copy 显示源图像。忽略目标图像。
      // source-over  使用异或操作对源图像与目标图像进行组合。
      ctx.globalCompositeOperation = "source-over";
      
  7. clip()方法将剪切区域设置为当前剪切区域与当前路径的交集。在第一次调用clip()方法之前,剪切区域与整个Canvas画布大小一致。因为clip()方法会将剪切区域设置为当前剪切区域与当前路径的交集,所以对该方法的调用一般都是嵌入save()和restore()方法之间的。否则,剪切区域将会越变越小,这通常不是我们想要的效果。

    • Canvas中的clip()方法用于从原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内(不能访问画布上的其他区域)。也可以在使用clip()方法前通过使用save()方法对当前画布区域进行保存,并在以后的任意时间通过restore()方法对其进行恢复。
    • [实现探照灯效果](https:// www.w3cplus.com/canvas/clip.html)
  8. ImageData操作,无论时getImageData还是putImageData都是针对画布来说的,拿到画布上的像素。一般可以结合drawIamge来用,先把图片通过drawImage绘制到图像上,然后再通过getImageData获取像素数据,循环修改其中的data属性,最后再通过putImageData,把修改后的imageData放回到画布。

    • createImageData() 方法创建新的空白 ImageData 对象。新对象的默认像素值 transparent black。
      对于 ImageData 对象中的每个像素,都存在着四方面的信息,即 RGBA 值:
      R - 红色 (0-255)
      G - 绿色 (0-255)
      B - 蓝色 (0-255)
      A - alpha 通道 (0-255; 0 是透明的,255 是完全可见的)
      因此 ,transparent black 表示 (0,0,0,0)。
      color/alpha 以数组形式存在,并且既然数组包含了每个像素的四条信息,数组的大小是 ImageData对象的四倍。(获得数组大小有更简单的办法,就是使用 ImageDataObject.data.length)。
      注意:包含 color/alpha 信息的数组存储于 ImageData 对象的 data 属性中。

      // 1. 以指定的尺寸(以像素计)创建新的 ImageData 对象:
      var imgData=ctx.createImageData(width,height);
      // 2. 创建与指定的另一个 ImageData 对象尺寸相同的新 ImageData 对象(不会复制图像数据):
      var imgData=ctx.createImageData(imageData);
      
    • getImageData() 方法返回 ImageData 对象,该对象拷贝了画布指定矩形的像素数据。

      // x    开始复制的左上角位置的 x 坐标。相对与canvas
      // y    开始复制的左上角位置的 y 坐标。
      // width    将要复制的矩形区域的宽度。
      // height 将要复制的矩形区域的高度。
      var imgData=context.getImageData(x,y,width,height);
      // 返回的 ImageData 对象中第一个像素的 color/alpha 信息,注意这里只是第一个像素,每个像素占数组的四个元素
      red=imgData.data[0];
      green=imgData.data[1];
      blue=imgData.data[2];
      alpha=imgData.data[3];
      
    • putImageData() 方法将图像数据(从指定的 ImageData 对象)放回画布上

      // imgData  规定要放回画布的 ImageData 对象。
      // x    ImageData 对象左上角的 x 坐标,以像素计。
      // y    ImageData 对象左上角的 y 坐标,以像素计。
      // dirtyX   可选。水平值(x),以像素计,在画布上放置图像的位置。
      // dirtyY   可选。水平值(y),以像素计,在画布上放置图像的位置。
      // dirtyWidth   可选。在画布上绘制图像所使用的宽度。
      // dirtyHeight  可选。在画布上绘制图像所使用的高度。
      ctx.putImageData(imgData,x,y,dirtyX,dirtyY,dirtyWidth,dirtyHeight);
      
    • ImageDate对象的三个属性
      width 返回 ImageData 对象的宽度
      height 返回 ImageData 对象的高度
      data 返回一个对象,其包含指定的 ImageData 对象的图像数据

  9. 图像绘制,向画布上绘制图像、画布或视频

    // img  规定要使用的图像、画布或视频。
    // sx   可选。开始剪切的 x 坐标位置。
    // sy   可选。开始剪切的 y 坐标位置。
    // swidth   可选。被剪切图像的宽度。
    // sheight  可选。被剪切图像的高度。
    // x    在画布上放置图像的 x 坐标位置。
    // y    在画布上放置图像的 y 坐标位置。
    // width    可选。把图像绘制到画布上的宽度。(伸展或缩小图像)
    // height   可选。把图像绘制到画布上的高度。(伸展或缩小图像)
    ctx.drawImage(img,x,y);
    ctx.drawImage(img,x,y,width,height);
    ctx.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
    
  10. 如何把canvas画布转换成img图像,[更多canvas压缩图片操作](http:// www.zhangxinxu.com/wordpress/2017/07/html5-canvas-image-compress-upload/)

    • canvas.toDataURL()方法,注意是canvas对象调用的方法
    canvas.toDataURL(mimeType, qualityArgument)
    

    可以把图片转换成base64格式信息,纯字符的图片表示法。
    其中:
    mimeType表示canvas导出来的base64图片的类型,默认是png格式,也即是默认值是'image/png',我们也可以指定为jpg格式'image/jpeg'或者webp等格式。file对象中的file.type就是文件的mimeType类型,在转换时候正好可以直接拿来用(如果有file对象)。
    qualityArgument表示导出的图片质量,只要导出为jpg和webp格式的时候此参数才有效果,默认值是0.92,是一个比较合理的图片质量输出参数,通常情况下,我们无需再设定。

    • canvas.toBlob()方法
    canvas.toBlob(callback, mimeType, qualityArgument)
    

    可以把canvas转换成Blob文件,通常用在文件上传中,因为是二进制的,对后端更加友好。
    和toDataURL()方法相比,toBlob()方法是异步的,因此多了个callback参数,这个callback回调方法默认的第一个参数就是转换好的blob文件信息,本文demo的文件上传就是将canvas图片转换成二进制的blob文件,然后再ajax上传的,代码如下:

    //  canvas转为blob并上传
    canvas.toBlob(function (blob) {
      //  图片ajax上传
      var xhr = new XMLHttpRequest();
      //  开始上传
      xhr.open("POST", 'upload.php', true);
      xhr.send(blob);    
    });
    
  11. beginPath 的重要性,从下面代码开始

    var ctx = document.getElementById('cvs').getContext('2d');
    ctx.beginPath();
    ctx.moveTo(100.5,20.5);
    ctx.lineTo(200.5,20.5);
    ctx.stroke();
    ctx.moveTo(100.5,40.5);
    ctx.lineTo(200.5,40.5)
    ctx.strokeStyle = '#f00';
    ctx.stroke();
    

    其中的0.5是为了避免”1px线条模糊问题“,你懂的。那么上面的代码会得到什么样的图形呢?是不是一条黑线一条红线呢?
    从代码上看,我们的逻辑毫无问题,但结果是我们得到的是两条红线,并不是一黑一红。
    canvas中的绘制方法(如stroke,fill),都会以“上一次beginPath”之后的所有路径为基础进行绘制。比如上面的代码里面我stroke了两次,其实这两次都是以第一次beginPath后的所有路径为基础画的。也就是说第一条路径我们stroke了两下,第一下是黑的,第二下是红的,所以最终也是红的。

    • 不管你用moveTo把画笔移动到哪里,只要不beginPath,那你一直都是在画一条路径。
    • fillRect与strokeRect这种直接画出独立区域的函数,也不会打断当前的path.
      说到beginPath,就不得不提到closePath,两者是不是有很“紧”的联系呢?答案是几乎没有关系

    closePath的意思不是结束路径,而是关闭路径,它会试图从当前路径的终点连一条路径到起点,让整个路径闭合起来。但是,这并不意味着它之后的路径就是新路径了!
    我们在上面的代码的第一个lineTo后面加上closePath,可以发现还是得到了两条红线。但如果我们在第一个stroke后面加上beginPath,则会如愿得到一条黑线一条红线。

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

推荐阅读更多精彩内容