2022-08-28 canvas 学习笔记

Canvas

基本用法

  1. 绘制矩形的三种方式

    • fillRect(x, y, width, height) 绘制填充矩形
    • strokeRect(x, y, width, height) 绘制一个矩形的边框
    • clearRect(x, y, width, height) 清除指定矩形区域,让清除部分完全透明。
  2. 绘制路径

    • beginPath() 新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。 (函数准备绘制一个新的形状路径开始)
    • moveTo(x, y) 要在设置路径(beginPath)之后专门指定你的起始位置。
    • closePath() 闭合路径之后图形绘制命令又重新指向到上下文中。
    • stroke() 通过线条来绘制图形轮廓。
    • fill() 通过填充路径的内容区域生成实心的图形。
  3. moveTo 移动笔触

    • 将笔触移动到指定的坐标 x 以及 y 上。
  4. 绘制直线: lineTo(x, y) 绘制一条从当前位置指定 (x,y) 位置的直线。

  5. 绘制圆弧:

    • arc(x, y, radius, startAngle, endAngle, anticlockwise): 画一个以(x,y)为圆心的以 radius 为半径的圆弧(圆),从 startAngle 开始到 endAngle 结束,按照 anticlockwise 给定的方向(默认为顺时针)来生成。
    • arcTo(x1, y1, x2, y2, radius): 根据给定的控制点和半径画一段圆弧,再以直线连接两个控制点。
    • 弧度=(Math.PI/180)*角度。
    // 弧度方向 (顺时针的计算)
                2 * PI
    3/2 * PI                  0
                PI
    
  6. 二次/三次贝塞尔曲线

    • quadraticCurveTo(cp1x, cp1y, x, y)
    • bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
  7. 直接在画布上绘制矩形的三个额外方法

    • rect(x, y, width, height) 当该方法执行的时候,moveTo() 方法自动设置坐标参数(0,0)。也就是说,当前笔触自动重置回默认坐标。
  8. Path2D 对象: 用来缓存或记录绘画命令

    • Path2D.addPath(path [, transform])

      new Path2D(); // 空的 Path 对象
      new Path2D(path); // 克隆 Path 对象
      new Path2D(d); // 从 SVG 建立 Path 对象
      
      const rectangle = new Path2D();
      rectangle.rect(10, 10, 50, 50);
      
      const circle = new Path2D();
      circle.moveTo(125, 35);
      circle.arc(100, 35, 25, 0, 2 * Math.PI);
      
      ctx.stroke(rectangle);
      ctx.fill(circle);
      
    • 使用 svg Paths

      // 先移动到点 (M10 10) 然后再水平移动 80 个单位(h 80),然后下移 80 个单位 (v 80),接着左移 80 个单位 (h -80),再回到起点处 (z)。
      const p = new Path2D("M10 10 h 80 v 80 h -80 Z");
      

使用样式和颜色

  1. 色彩 Colors

    • ctx.fillStyle = color 设置图形的填充颜色
    • ctx.strokeStyle = color 设置图形轮廓的颜色
    // 这些 fillStyle 的值均为 '橙色'
    ctx.fillStyle = "orange";
    ctx.fillStyle = "#FFA500";
    ctx.fillStyle = "rgb(255,165,0)";
    ctx.fillStyle = "rgba(255,165,0,1)";
    
  2. 设置全局对象的透明度值: ctx.globalAlpha = 0.2; 取值在[0, 1]

    • globalAlpha是让整个画布变的透明了
    • 使用之前save, 使用完成之后restore
  3. 线型 Line styles

    • 影响描边样式的因素

      • strokeStyle
      • lineWidth
      • lineCap 描边断点样式 butt/round/square ...
      • lineJoin 描边拐点类型 milter/round/bevel ...
      • meterLimit
    • ctx.lineWidth = value; 设置线条宽度。

    • ctx.lineCap = type; 设置线条末端样式。 (butt,round 和 square。默认是 butt。)

    • ctx.lineJoin = type; 设定线条与线条间接合处的样式。(round, bevel 和 miter。默认是 miter。)

    • ctx.miterLimit = numberValue; 限制当两条线相交时交接处最大长度;所谓交接处长度(斜接长度)是指线条交接处内角顶点到外角顶点的长度。 (只适用于lineJoin="milter"的情况, 裁掉拐角

    • ctx.getLineDash() 返回一个包含当前虚线样式,长度为非负偶数的数组。

    • ctx.setLineDash(segments) 设置当前虚线样式。setLineDash([20,30,40]) (20长度实线,30虚线, 40实线)

    • ctx.lineDashOffset = numberValue 设置虚线样式的起始偏移量。

  4. 使用虚线

    • 用 setLineDash 方法和 lineDashOffset 属性来制定虚线样式。setLineDash 方法接受一个数组,来指定线段与间隙的交替;lineDashOffset 属性设置起始偏移量。
  5. 渐变 Gradients

    • 线性渐变: createLinearGradient(x1, y1, x2, y2)
      • createLinearGradient 方法接受 4 个参数,表示渐变的起点 (x1,y1) 与终点 (x2,y2)。
    • 径向渐变: createRadialGradient(x1, y1, r1, x2, y2, r2)
      • createRadialGradient 方法接受 6 个参数,前三个定义一个以 (x1,y1) 为原点,半径为 r1 的圆,后三个参数则定义另一个以 (x2,y2) 为原点,半径为 r2 的圆。
    • gradient.addColorStop(rate, color) rate 值域从 0 到 1, 表示渐变的比例点位
    const linearGradient = ctx.createLinearGradient(0, 0, 150, 150);
    const radialGradient = ctx.createRadialGradient(75, 75, 0, 75, 75, 100);
    
    linearGradient.addColorStop(0, "white");
    radialGradient.addColorStop(1, "black");
    
    ctx.fillStyle = linearGradient;
    
    • gradient.addColorStop(position, color), position 参数必须是一个 0.0 与 1.0 之间的数值,表示渐变中颜色所在的相对位置。例如,0.5 表示颜色会出现在正中间。color 参数必须是一个有效的 CSS 颜色值(如 #FFF,rgba(0,0,0,1),等等)。
  6. 纹理 - 图案样式 Patterns

    • createPattern(image, type) 该方法接受两个参数。Image 可以是一个 Image 对象的引用,或者另一个 canvas 对象。Type 必须是下面的字符串值之一:repeat,repeat-x,repeat-y 和 no-repeat。
    // 创建图案
    const pattern = ctx.createPattern(img, "repeat");
    ctx.fillStyle = pattern;
    ctx.fillRect(0, 0, 150, 150);
    
  7. 阴影 Shadows

    • 横向偏移: shadowOffsetX = float
    • 纵向偏移: shadowOffsetY = float
    • 模糊度: shadowBlur = float
    • shadowColor = color
    const ctx = document.getElementById("canvas").getContext("2d");
    
    ctx.shadowOffsetX = 2;
    ctx.shadowOffsetY = 2;
    ctx.shadowBlur = 2;
    ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
    
    ctx.font = "20px Times New Roman";
    ctx.fillStyle = "Black";
    ctx.fillText("Sample String", 5, 30);
    
  8. ctx.fill('nonzero' | 'evenodd') 填充规则

    • 填充规则根据某处在路径的外面或者里面来决定该处是否被填充

绘制文本

  1. 文本属性

    • 字体font ctx.font = 'bold 18px serif';
    • 水平对齐
    • 垂直对齐
  2. 绘制文本

    • fillText(text, x, y [, maxWidth]) 在指定的 (x,y) 位置填充指定的文本,绘制的最大宽度是可选的。
    • strokeText(text, x, y [, maxWidth]) 在指定的 (x,y) 位置绘制文本边框,绘制的最大宽度是可选的。
  3. 有样式的文本

    • ctx.font = value; 使用和 CSS font 属性相同的语法。默认的字体是 10px sans-serif。
    • ctx.textAlign = value;
    • ctx.textBaseline = value; (top middle bottom hanging alphabetic ideographic)
    • ctx.direction = value;
  4. 预测量文本宽度

    • measureText() 将返回一个 TextMetrics 对象的宽度、所在像素,这些体现文本特性的属性。

使用图片

  1. 引入图像到 canvas 里需要以下两步基本操作:

    • 获得一个指向 HTMLImageElement 的对象或者另一个 canvas 元素的引用作为源,也可以通过提供一个 URL 的方式来使用图片
    • 使用 drawImage()函数将图片绘制到画布上
  2. 绘制图片

    • drawImage(image, x, y, width?, height?) 绘图 + 位移 + 缩放: 在canvas中(x,y)坐标处绘制宽度为width高度为height区域的图片
    • drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) 绘图+裁剪+移动+缩放, 用于控制做切片显示的。 后 4 个则是定义切片的目标显示位置和大小。
      • sx: 相机视口位置横向坐标 (相当于画布左侧区域移动了sx) 对图像进行了横向裁剪

      • sy: 相机视口位置纵向坐标 (相当于画布左侧区域移动了sy) 对图像进行了纵向裁剪

      • sWidth: 相机视口的宽度 (将相机视口里的内容放置在渲染区域)

      • sHeight: 相机视口的宽度 (将相机视口里的内容放置在渲染区域)

      • 在画布上放置图像的 dx 坐标位置。

      • 在画布上放置图像的 dy 坐标位置。

      • 要使用的图像的宽度。dWidth(伸展或缩小图像)

      • 要使用的图像的高度。dHeight(伸展或缩小图像)

  3. 缩放时启用平滑缩放

    ctx.mozImageSmoothingEnabled = false;
    ctx.webkitImageSmoothingEnabled = false;
    ctx.msImageSmoothingEnabled = false;
    ctx.imageSmoothingEnabled = false;
    

变形 transformations

  1. 状态的保存和恢复

    • ctx.save(); ,每当 save()方法被调用后,当前的状态就被推送到栈中保存。
      • 当前应用的变形(即移动,旋转和缩放,见下)
      • 以及下面这些属性:strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, lineDashOffset, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation, font, textAlign, textBaseline, direction, imageSmoothingEnabled
      • 当前的裁切路径(clipping path)
    • ctx.restore();
    • 可以调用任意多次 save 方法。每一次调用 restore 方法,上一个保存的状态就从栈中弹出,所有设定都恢复。
  2. 移动 Translate (将画布进行移动)

    • translate(x, y) 方法接受两个参数。x 是左右偏移量,y 是上下偏移量
    function draw() {
      let ctx = document.getElementById("canvas").getContext("2d");
      for (let i = 0; i < 3; i++) {
        for (let j = 0; j < 3; j++) {
          ctx.save();
          ctx.fillStyle = "rgb(" + 51 * i + ", " + (255 - 51 * i) + ", 255)";
          ctx.translate(10 + j * 50, 10 + i * 50);
          ctx.fillRect(0, 0, 25, 25);
          ctx.restore();
        }
      }
    }
    
  3. rotate(Math.PI / 4) ,它用于以原点(0, 0)为中心顺时针旋转 canvas。

  4. scale(x, y) 方法可以缩放画布的水平和垂直的单位。两个参数都是实数,可以为负数,x 为水平缩放因子,y 为垂直缩放因子,如果比 1 小,会缩小图形,如果比 1 大会放大图形。默认值为 1,为实际大小。

  5. transform(a, b, c, d, e, f) 将当前的变形矩阵乘上一个基于自身参数的矩阵

    // 如下面的矩阵所示:
    [
       a c   e
       b d   f
       0 0   1
    ]
    a (m11) 水平方向的缩放
    b(m12)  竖直方向的倾斜偏移
    c(m21)  水平方向的倾斜偏移
    d(m22)  竖直方向的缩放
    e(dx)   水平方向的移动
    f(dy)   竖直方向的移动
    
    • setTransform(a, b, c, d, e, f)
    • resetTransform() 重置当前变形为单位矩阵,它和调用以下语句是一样的:ctx.setTransform(1, 0, 0, 1, 0, 0);

组合 Compositing

  1. ctx.globalCompositeOperation 不仅可以在已有图形后面再画新图形,还可以用来遮盖指定区域,清除画布中的某些部分(清除区域不仅限于矩形,像 clearRect()方法做的那样)以及更多其他操作。


    image.png
  2. 路径裁切 clip() 将当前正在构建的路径转换为当前的裁剪路径。

    • 状态save
    • 定义路径
    • clip
    • 绘制其他图形 (之后绘制的图形只显示在这个路径之中)
    • 状态restore (用完路径裁剪之后, 仍然想绘制其他的话)

像素操作

  1. ImageData 对象

    • data:Uint8ClampedArray [r,g,b,a, r,g,b,a, r,g,b,a, ...]

    • width 图片宽度,单位是像素

    • height 图片高度,单位是像素

    • data Uint8ClampedArray 类型的一维数组,包含着 RGBA 格式的整型数据,范围在 0 至 255 之间(包括 255)。data 属性返回一个 Uint8ClampedArray,它可以被使用作为查看初始像素数据。每个像素用 4 个 1bytes 值 (按照红,绿,蓝和透明值的顺序; 这就是"RGBA"格式) 来代表。每个颜色值部份用 0 至 255 来代表。

    • Uint8ClampedArray 包含高度 × 宽度 × 4 bytes 数据,索引值从 0 到 (高度 × 宽度 ×4)-1

    // 要读取图片中位于第 50 行,第 200 列的像素的蓝色(0=R.1=G,2=B)部份
    blueComponent = imageData.data[50 * (imageData.width * 4) + 200 * 4 + 2];
    
    // 根据行、列读取某像素点的 R/G/B/A 值的公式:
    imageData.data[50 * (imageData.width * 4) + 200 * 4 + 0 / 1 / 2 / 3];
    
    // 使用 Uint8ClampedArray.length 属性来读取像素数组的大小(以 bytes 为单位):
    const numBytes = imageData.data.length;
    
  2. 创建 ImageData 对象

    • const myImageData = ctx.createImageData(width, height); 创建了一个新的具体特定尺寸的 ImageData 对象。所有像素被预设为透明黑。
    • const myImageData = ctx.createImageData(anotherImageData); 创建一个被 anotherImageData 对象指定的相同像素的 ImageData 对象。这个新的对象像素全部被预设为透明黑。这个并非复制了图片数据。
  3. 得到场景像素数据

    • const myImageData = ctx.getImageData(left, top, width, height);
  4. 在场景中写入像素数据

    • ctx.putImageData(myImageData, dx, dy, x,y,w,h); dx 和 dy 参数表示您希望在场景内左上角绘制的像素数据所得到的设备坐标。 x,y,w,h表示选取相对图片位置的坐标取宽度为w高度h的区域渲染
    const invert = function () {
      ctx.drawImage(img, 0, 0);
      const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
      const data = imageData.data;
      for (let i = 0; i < data.length; i += 4) {
        data[i] = 255 - data[i]; // red
        data[i + 1] = 255 - data[i + 1]; // green
        data[i + 2] = 255 - data[i + 2]; // blue
      }
      ctx.putImageData(imageData, 0, 0);
    };
    
  5. 缩放与反锯齿

    • 反锯齿默认是启用的,关闭它以看到清楚的像素。可以通过切换 imageSmoothingEnabled 属性查看不同的效果(不同浏览器需要不同前缀)。
  6. 保存图片

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

推荐阅读更多精彩内容