前端知识库(lizh)
  • README
  • Bugs
    • 前端调试随笔
    • 浏览器常见问题概览
    • 浏览器兼容问题概览
    • HTML常见问题概览
    • CSS常见问题概览
    • JS常见问题概览
    • 移动端兼容性问题概览
    • 微信小程序开发
    • NodeJs常见问题概览
    • Mac常见问题概览
    • 微信开发遇坑指南
    • Npm包常见问题概览
    • 其他问题汇总
  • Css探索系列
    • CSS基础知识
    • CSS常见问答
    • CSS常见问答02
    • CSS应用示例
    • CSS应用示例02
    • 由Z Index引发的层叠上下文思考
    • 由浮动塌陷引发的块级格式上下文思考
    • CSS探索系列 Flex布局
    • CSS探索系列 Margin
    • CSS探索系列 Auto关键字
    • CSS探索系列 Gradient
    • CSS探索系列 Line Height
    • CSS探索系列 元素居中
    • CSS探索系列 动画
    • 为什么使用PostCSS处理CSS?
    • 重新认识伪类与伪元素
    • 自定义表单伪元素样式
    • 如何理解Css中的Display属性
    • 视口和软键盘对视口的影响
    • 关于Css
  • Frontend
    • 00 关于Web前端
    • 01 前端知识概览
    • 02 常用前端库概览
    • 基础 00 前端常见问题01
    • 基础 01 浏览器缓存
    • 基础 02 浏览器工作原理
    • 基础 03 谈谈前端跨源问题及解决方法
    • 进阶 01 Web性能优化
    • 进阶 02 搜索引擎优化(SEO)
    • 进阶 03 前端模块化编程
    • 进阶 04 规范代码:Linter、Prettier、EditorConfig
    • 进阶 11 前端自动化测试
    • 高级 01 前端安全
    • Vue2.X原理篇
    • Vue3初步了解及迁移指南
    • 重读Vue教程
    • React17.X原理篇
    • 你必须知道的React问题
    • 重读React教程
    • 聊一聊Cookie的一些问题
    • 如何理解HTTP响应的状态码
    • HTTP的历史演变及概述
    • Webpack4.X原理篇
    • Webpack基础入门篇
    • Webpack构建优化篇
    • TypeScript使用指南
    • 代码规范
      • 前端规范
      • HTML
      • CSS
      • JS
  • Html探索系列
    • HTML基础知识
    • HTML基础知识02
    • HTML常见问答
    • HTML经典实践用例
    • HTML元素的宽高及位置详解
    • Video元素的使用和常见问题总结
    • Html探索系列 Meta标签
    • DOCTYPE:文档类型与浏览器模式
    • DHTML(动态网页)简介
    • HTML标签详解
    • HTML布局的几种方式
    • HTML全局属性
    • 关于Html
  • Js探索系列
    • 基础知识
    • 常见问答
    • 应用示例
    • 趣味示例
    • 基础篇 05 AJAX
    • 基础篇 06 Window对象
    • 基础篇 07 Error、JSON、Math、Console对象
    • 基础篇 08 History、URL、Screen、Navigator、Location对象
    • 基础篇 09 文档对象模型(DOM)
    • 基础篇 10 Document对象
    • 基础篇 11 Element对象
    • 基础篇 12 Event对象
    • 基础篇 13 键盘、鼠标、触摸事件
    • 基础篇 15 CSS对象模型(CSSOM)
    • 进阶篇 01 Prototype对象和继承
    • 进阶篇 02 Promise对象
    • 进阶篇 07 迭代器(Iterator)
    • 进阶篇 08 Generator和Async函数
    • 进阶篇 09 JavaScript异步编程
    • Date对象和日期时间字符串格式
    • Canvas基础入门篇
    • Canvas进阶篇
    • SVG基础入门篇
    • 四种判断数据类型方法的优缺点
    • 深入理解JavaScript的浅拷贝和深拷贝
    • 谈谈JavaScript的作用域和上下文
    • 复制内容到剪贴板
    • 关于Javascript
  • NodeJs
    • 关于Node.Js
    • Node.Js:三种调试方法
    • Npm包管理器简介及一些机制
    • NPM:Package.Json详解(中文)
    • NPM:从零开始,开发一个软件包
    • NPM:常用命令
    • Node.Js:Fs(文件系统)
    • Node.Js:Global(全局变量)
    • Node.Js:HTTP
    • Node.Js:Module(模块)
    • Node.Js:Path(路径)
    • Node.Js:Readline(逐行读取)
  • Research
    • 极细边框(1px边框)实现方式
    • 如何监控前端异常?
    • H5页面跳转和刷新
    • Web主题切换和个性化定制方法总结
    • Vue SSR(服务端渲染)的简单实现
    • 基于Create React App打造代码规范化的React+Ts项目
    • H5可视化编辑
    • Web常用功能
    • Javascript加密混淆
    • Vue如何导入TypeScript
    • 移动端PDF预览
    • 纯CSS绘制箭头
    • 网站性能测量和优化方法
  • Tech
    • GOOGLE浏览器的搜索技巧
    • Curl的用法指南
    • Sublime3插件篇
    • Charles安装及使用
    • Nginx基础使用
    • 排序算法(Javascript)
    • 代码整洁之道(摘录笔记)
    • Java的24种设计模式与7大原则
    • 观察者和发布订阅模式
  • Tools
    • Git
      • Git基础教程
      • Git常见问题
    • Gitbook
      • Gitbook入门篇
      • Gitbook插件篇
      • Gitbook进阶篇
由 GitBook 提供支持
在本页
  • 获取渲染上下文(2D环境)
  • 状态的保存和恢复
  • 从线条开始
  • 填充颜色
  • 绘制矩形
  • 绘制标准圆弧
  • 贝塞尔曲线
  • 文本设置
  • 变换设置
  • 裁剪区域
  • 绘制图像
  • 非零环绕原则与奇偶原则
  • 其他属性
  • 绘制圆角矩形
  • 绘制圆角图片
  • 参考链接

这有帮助吗?

  1. Js探索系列

Canvas基础入门篇

Canvas API 提供了一个通过 JavaScript 和 HTML 的 canvas 元素来绘制图形的方式。它可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。

Canvas API 主要聚焦于2D图形。而同样使用 canvas 元素的 WebGL API 则用于绘制硬件加速的 2D 和 3D 图形。

获取渲染上下文(2D环境)

除一些过时的浏览器不支持 canvas 元素外,所有的新版本主流浏览器都支持它。Canvas 的默认大小为 300px X 150px。但是,可以使用 HTML 的高度和宽度属性来自定义 Canvas 的尺寸。为了在 Canvas 上绘制图形,先使用一个 JavaScript 上下文对象,它能动态创建图像。

注意: CSS 中的 width、height 控制的是画布在文档中的宽高,当宽度比例与 canvas 属性中定义的宽度不一致时,会拉伸 Canvas 元素,相当于把固定的图片拉伸了。

<div class="flex_box">
    <canvas id="canvas" class="flex_item" width="500" height="500"></canvas>
</div>
const canvas = document.getElementById("canvas_base")
const context = canvas.getContext("2d")
function getContext2D () {
    return new Promise((resolve, reject) => {
        const canvas = document.getElementById("canvas_base")
        const context = canvas.getContext("2d")
        if (context) {
            resolve(context)
        } else {
            reject()
        }
    })
}

状态的保存和恢复

Canvas 是基于状态的绘制,包括:当前应用的变形、当前的裁切路径、以及以下属性:

strokeStyle
fillStyle
globalAlpha
lineWidth
lineCap
lineJoin
miterLimit
lineDashOffset
shadowOffsetX
shadowOffsetY
shadowBlur
shadowColor
globalCompositeOperation
font
textAlign
textBaseline
direction
imageSmoothingEnabled

Canvas 可通过以下方法管理状态:

  • save():保存画布的所有状态

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

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

从线条开始

移动画笔:

context.moveTo(x, y)

这句代码的意思是移动画笔至(x, y)这个点(单位是px)。记住,这里是以 canvas 画布的左上角为笛卡尔坐标系的原点,且y轴的正方向向下,x轴的正方向向右。

笔画停点:

context.lineTo(x, y)

这句的意思是从上一笔的停止点绘制到(x, y)这里。不过要清楚,这里的 moveTo()、lineTo() 都只是状态而已,是规划,是我准备要画,还没有开始画,只是一个计划而已!

选择画笔:

context.lineWidth = 2        // 设置画笔(线条)的粗细为2px
context.fillStyle = "#0dd"   // 设置或返回用于填充绘画的颜色、渐变或模式
context.strokeStyle = "#0dd" // 设置或返回用于笔触的颜色、渐变或模式

Canvas是基于状态的绘制,所以我们在选择画笔粗细和颜色的同时,其实也是选择了线条的粗细和颜色。

确定绘制:

**确定绘制只有两种方法,fill() 和 stroke() **,前者是指填充,后者是指描边。

// 绘制线条:青色
function drawLine (context) {
    context.moveTo(100, 100)
    context.lineTo(300, 200)
    context.lineWidth = 2
    context.strokeStyle = "#0dd"
    context.stroke()
}

// 绘制折线:粉红 -> 深蓝
function drawBrokenLine (context) {
    context.lineWidth = 2

    context.moveTo(100, 100)
    context.lineTo(300, 300)
    context.strokeStyle = "#d0d"
    context.stroke()

    context.moveTo(300, 300)
    context.lineTo(100, 500)
    context.strokeStyle = "#00d"
    context.stroke()
}
getContext2D().then(context => {
    drawLine(context) 
    drawBrokenLine(context) 
})

期待绘制结果:第一条线是青色,第二条线是粉红色,第三条线是深蓝。 然而,最终绘制结果:三条线都是深蓝色。这里的原因就是前面强调——Canvas是基于状态的绘制。

什么意思呢?就是说每当状态发生改变,Canvas 会重新渲染。

从上面示例来说:每次使用 stroke() 时,它都会把之前设置的状态再绘制一遍。如,第一次 stroke() 时,绘制一条青色的折线;第二次 stroke() 时,会再重新绘制之前的那条青色的折线,但是这个时候的画笔已经被更换成粉红色的了,所以画出的折线全是粉红色的;第三次 stroke() 同理,画笔更换成了深蓝色,所以最终三条线都是深蓝!

实际上,这里看到的三条折线,其实绘制了 3 次,一共绘制了 6 条折线。

创建新路径:

beginPath() 是 Canvas 2D API 通过清空子路径列表开始一个新路径的方法。

在每次绘制之前加上 beginPath() ,表示下次绘制的起始处为 beginPath() 之后的代码。beginPath() 设置的绘制状态的作用域结束于 stroke()、fill() 或者 closePath() 方法。

function drawMultBrokenLine (context) {
    context.lineWidth = 2

    context.beginPath()
    context.moveTo(100, 100)
    context.lineTo(300, 200)
    context.strokeStyle = "#0dd"
    context.stroke()

    context.beginPath()
    context.moveTo(100, 100)
    context.lineTo(300, 300)
    context.strokeStyle = "#d0d"
    context.stroke()

    context.beginPath()
    context.moveTo(300, 300)
    context.lineTo(100, 500)
    context.strokeStyle = "#00d"
    context.stroke()
}

线条属性概述:

lineCap 属性定义上下文中线的端点,可以有以下 3 个值:

  • butt:默认值,端点是垂直于线段边缘的平直边缘。

  • round:端点是在线段边缘处以线宽为直径的半圆。

  • square:端点是在选段边缘处以线宽为长、以一半线宽为宽的矩形。

lineJoin 属性定义两条线相交产生的拐角,可将其称为连接。在连接处创建一个填充三角形,可以使用 lineJoin 设置它的基本属性。

  • miter:默认值,在连接处边缘延长相接。miterLimit 是角长和线宽所允许的最大比例(默认是 10)。

  • bevel:连接处是一个对角线斜角。

  • round:连接处是一个圆。

lineWidth 属性定义线的宽度(默认值为 1.0)。

strokeStyle 属性定义线和形状边框的颜色和样式。

填充颜色

基本颜色:

context.fillStyle = "red"
context.fillStyle = "#FF0000"
context.fillStyle = "#F00"
context.fillStyle = "rgb(255,0,0)"
context.fillStyle = "rgba(255,0,0,1)"
context.fillStyle = "hsl(0,100%,50%)"
context.fillStyle = "hsla(0,100%,50%,1)"

线性渐变:

const gradient = context.createLinearGradient(x0,y0,x1,y1)
// x0 渐变开始点的 x 坐标
// y0 渐变开始点的 y 坐标
// x1 渐变结束点的 x 坐标
// y1 渐变结束点的 y 坐标

gradient.addColorStop(stop, color)
// stop  介于 0.0 与 1.0 之间的值,表示渐变中开始与结束之间的位置。
// color 在结束位置显示的 CSS 颜色值
function createLinearGradient (context) {
    context.rect(100, 100, 300, 300);
    let grd = context.createLinearGradient(100, 100, 300, 300)
    grd.addColorStop(0, "#dd0")
    grd.addColorStop(0.5, "#fd0")
    grd.addColorStop(1, "#0dd")
    context.fillStyle = grd
    context.fill()
}

径向渐变:

const gradient = context.createRadialGradient(x0,y0,r0,x1,y1,r1)
// x0 渐变的开始圆的 x 坐标
// y0 渐变的开始圆的 y 坐标
// r0 开始圆的半径
// x1 渐变的结束圆的 x 坐标
// y1 渐变的结束圆的 y 坐标
// r1 结束圆的半径

gradient.addColorStop(stop, color)
// stop  介于 0.0 与 1.0 之间的值,表示渐变中开始与结束之间的位置。
// color 在结束位置显示的 CSS 颜色值
function createRadialGradient (context) {
    let grd = context.createRadialGradient(75, 50, 5, 90, 60, 100)
    grd.addColorStop(0, "#0aa")
    grd.addColorStop(1, "white")
    context.fillStyle = grd
    context.fillRect(10, 10, 300, 300)
}

填充纹理: 纹理其实就是图案的重复,填充图案通过 createPattern() 函数进行初始化。

context.createPattern(image,"repeat|repeat-x|repeat-y|no-repeat")
// 第一个参数:Image对象、canvas对象、video对象
// 平面上重复:repeat;
// x轴上重复:repeat-x;
// y轴上重复:repeat-y;
// 不使用重复:no-repeat;
function createPattern (context) {
    let canvas = document.createElement("canvas")
    let context2 = canvas.getContext('2d')
    canvas.width = 200
    canvas.height = 200
    let img = new Image()
    img.src = "./img/pins_3338674420.jpg"
    img.onload = function() {
        context2.drawImage(img, 0, 0, 200, 200)
        let pattern = context.createPattern(canvas, 'no-repeat')
        context.fillStyle = pattern
        context.fillRect(0, 0, 500, 500)
        context.lineWidth = 1
        context.strokeStyle = '#0dd'
        context.strokeRect(0, 0, 500, 500)
    }
}

绘制矩形

context.rect(x,y,width,height)/
context.fillRect(x,y,width,height)
context.strokeRect(x,y,width,height)
context.clearRect(x,y,width,height)
// x 矩形左上角的 x 坐标
// y 矩形左上角的 y 坐标
// width  矩形的宽度,以像素计
// height 矩形的高度,以像素计

绘制标准圆弧

context.arc(x,y,r,sAngle,eAngle,counterclockwise);
// x 圆的中心的 x 坐标。
// y 圆的中心的 y 坐标。
// r 圆的半径。
// sAngle 起始角,以弧度计。(弧的圆形的三点钟位置是 0 度)。
// eAngle 结束角,以弧度计。
// counterclockwise	可选。规定应该逆时针还是顺时针绘图。False = 顺时针(默认),true = 逆时针。
context.arcTo(x1,y1,x2,y2,r);
// x1 弧的起点的 x 坐标
// y1 弧的起点的 y 坐标
// x2 弧的终点的 x 坐标
// y2 弧的终点的 y 坐标
// r  弧的半径
function drawArc (context) {
    context.beginPath()
    context.lineWidth = 1
    context.strokeStyle = '#0dd'
    context.arc(100, 75, 100, 0,  (120 / 180) * Math.PI, false)
    context.stroke()
}
function drawArcTo (context) {
    context.beginPath()
    context.lineWidth = 1
    context.strokeStyle = '#0dd'
    context.moveTo(20, 20)
    context.lineTo(100, 20)
    context.arcTo(150, 20, 150, 70,50)
    context.lineTo(150, 100)
    context.stroke()
}

贝塞尔曲线

二次贝塞尔曲线

context.quadraticCurveTo(cpx,cpy,x,y);
// cpx 贝塞尔控制点的 x 坐标
// cpy 贝塞尔控制点的 y 坐标
// x   结束点的 x 坐标
// y   结束点的 y 坐标

二次贝塞尔曲线需要两个点。第一个点是用于二次贝塞尔计算中的控制点,第二个点是曲线的结束点。曲线的开始点是当前路径中最后一个点。如果路径不存在,那么请使用 beginPath() 和 moveTo() 方法来定义开始点

function drawQuadraticCurveTo (context) {
    context.lineWidth = 1
    context.strokeStyle = '#0dd'
    context.beginPath()
    context.moveTo(20, 20)
    context.quadraticCurveTo(20, 100, 200, 20)
    context.stroke()
}

三次贝塞尔曲线

context.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y);
// cp1x	第一个贝塞尔控制点的 x 坐标
// cp1y	第一个贝塞尔控制点的 y 坐标
// cp2x	第二个贝塞尔控制点的 x 坐标
// cp2y	第二个贝塞尔控制点的 y 坐标
// x	结束点的 x 坐标
// y	结束点的 y 坐标

三次贝塞尔曲线需要三个点。前两个点是用于三次贝塞尔计算中的控制点,第三个点是曲线的结束点。曲线的开始点是当前路径中最后一个点。如果路径不存在,那么请使用 beginPath() 和 moveTo() 方法来定义开始点。

文本设置

font	     // 设置或返回文本内容的当前字体属性
textAlign	 // 设置或返回文本内容的当前对齐方式
textBaseline // 设置或返回在绘制文本时使用的当前文本基线

context.font = "[font-style] [font-variant] [font-weight] [font-size/line-height] [font-family]"
// font-style    // 规定字体样式:normal\italic\oblique
// font-variant  // 规定字体变体:normal\small-caps
// font-weight   // 规定字体的粗细:normal\bold\bolder\lighter\100~900
// font-size/line-height // 规定字号和行高,以像素计。
// font-family   // 规定字体系列。

context.textAlign = "center|end|left|right|start"
// 默认。  文本在指定的位置开始。
// end    文本在指定的位置结束。
// center 文本的中心被放置在指定的位置。
// left   文本左对齐。
// right  文本右对齐。

context.textBaseline = "alphabetic|top|hanging|middle|ideographic|bottom"
// alphabetic  默认。文本基线是普通的字母基线。
// top         文本基线是 em 方框的顶端。。
// hanging     文本基线是悬挂基线。
// middle      文本基线是 em 方框的正中。
// ideographic 文本基线是表意基线。
// bottom      文本基线是 em 方框的底端。
fillText()    // 在画布上绘制“被填充的”文本
strokeText()  // 在画布上绘制文本(无填充)
measureText() // 返回包含指定文本宽度的对象

context.fillText(text,x,y,maxWidth)
context.strokeText(text,x,y,maxWidth)
// text     规定在画布上输出的文本。
// x        开始绘制文本的 x 坐标位置(相对于画布)。
// y        开始绘制文本的 y 坐标位置(相对于画布)。
// maxWidth 可选。允许的最大文本宽度,以像素计。
function drawText (context) {
    context.fillStyle = "#0aa"
    context.strokeStyle = "#0aa"

    context.font = "normal normal bold 30px/150px Times"
    context.textAlign = "center"
    context.textBaseline = "middle"

    context.fillText("Hello Canvas", 200, 250)
    context.strokeText("width:" + context.measureText("Hello Canvas").width, 200, 300, 200)
}

变换设置

context.scale(scalewidth,scaleheight);
// scalewidth  缩放当前绘图的宽度 (1=100%, 0.5=50%, 2=200%, 依次类推)
// scaleheight 缩放当前绘图的高度 (1=100%, 0.5=50%, 2=200%, etc.)

context.rotate(angle);
// angle	旋转角度,以弧度计。
// 如需将角度转换为弧度,请使用 degrees*Math.PI/180 公式进行计算。
// 举例:如需旋转 5 度,可规定下面的公式:5*Math.PI/180。

context.translate(x,y);
// x 添加到水平坐标(x)上的值
// y 添加到垂直坐标(y)上的值

对于缩放变换有两点问题需要注意:

1.缩放时,图像左上角坐标的位置也会对应缩放

2.缩放时,图像线条的粗细也会对应缩放

context.transform(a,b,c,d,e,f);
context.setTransform(a,b,c,d,e,f);
// a 水平缩放(1)
// b 水平倾斜(0)
// c 垂直倾斜(0)
// d 垂直缩放(1)
// e 水平位移(0)
// f 垂直位移(0)

// 平移变换: x’ = x + dx, y’ = y + dy。
// 也即是说可以使用 context.transform (1,0,0,1,dx,dy)代替context.translate(dx,dy)。

// 同理可以使用 context.transform(sx,0,0,sy,0,0)代替context.scale(sx, sy);
// 也可以使用context.transform(0,sy,sx,0,0,0);

// context.transform(Math.cos(θ*Math.PI/180),Math.sin(θ*Math.PI/180), -Math.sin(θ*Math.PI/180),Math.cos(θ*Math.PI/180),0,0)可以替代context.rotate(θ)。

// 也可以使用 context.transform(-Math.sin(θ*Math.PI/180),Math.cos(θ*Math.PI/180), Math.cos(θ*Math.PI/180),Math.sin(θ*Math.PI/180),0,0)替代。

// 区别:transform()变换是指在上一次变换的状态基础上再次变换;setTransform()变换是指先重置到最初始的状态再开始变换。
function transform (context) {
    context.save()
    context.fillStyle = "#0aa"
    context.scale(1, 0.5)
    context.rotate(10 * Math.PI / 180)
    context.translate(20, 0)
    context.fillRect(0, 0, 150, 150)
    console.log('scale')
    context.restore()

    //transform
    context.save()
    context.translate(100, 0)
    context.fillStyle = "yellow"
    context.fillRect(0, 0, 150, 100)

    context.transform(1, 0.5, -0.5, 1, 30, 10)
    context.fillStyle = "red"
    context.fillRect(0, 0, 150, 100)

    context.transform(1, 0.5, -0.5, 1, 30, 10)
    context.fillStyle = "blue"
    context.fillRect(0, 0, 150, 100)
    context.restore()

    //setTransform
    context.save()
    context.translate(400, 0)
    context.fillStyle = "yellow"
    context.fillRect(0, 0, 150, 100)

    context.setTransform(1, 0.5, -0.5, 1, 100, 0)
    context.fillStyle = "red"
    context.fillRect(0, 0, 150, 100)

    context.setTransform(1, 0.5, -0.5, 1, 100, 0)
    context.fillStyle = "blue"
    context.fillRect(0, 0, 150, 100)
    context.restore()
}

注意保存和恢复Canvas状态

变换只会影响 transform() 等方法调用之后的绘图

裁剪区域

clip() 方法来实现 Canvas 的图像裁剪功能。该方法使用路径来对Canvas画布设置一个裁剪区域。因此,必须先创建好路径。创建完整后,调用 clip() 方法来设置裁剪区域。

需要注意的是裁剪是对画布进行的,裁切后的画布不能恢复到原来的大小,也就是说画布是越切越小的,要想保证最后仍然能在canvas最初定义的大小下绘图需要注意 save() 和 restore() 。画布是先裁切完了再进行绘图。并不一定非要是图片,路径也可以放进去~。

function setClip (context) {
    context.save()
    context.beginPath()
    context.fillStyle = '#0aa'
    context.arc(100, 100, 100, 2 * Math.PI, false)
    context.clip()
    let img = new Image()
    img.src = require("@/assets/image/01eb485e44bf8fa8012165187fa336.jpg@1280w_1l_2o_100sh.jpg")
    img.onload = function() {
        context.drawImage(img, 0, 0, 350, 250)
        context.closePath()
        context.restore()

        context.beginPath()
        context.moveTo(100, 100)
        context.lineTo(300, 100)
        context.strokeStyle = '#0aa'
        context.stroke()
        context.closePath()
    }
}

绘制图像

Canvas 更有意思的一项特性就是图像操作能力。可以用于动态的图像合成或者作为图形的背景,以及游戏界面等等。浏览器支持的任意格式的外部图片都可以使用,比如 PNG、GIF、JPEG。 你甚至可以将同一个页面中其他 Canvas 元素生成的图片作为图片源。

Canvas 的 API 可以使用下面这些类型中的一种作为图片的源:

  • HTMLImageElement:Image() 函数构造出来的,或者任何的 img 元素。

  • HTMLVideoElement: video 元素作为图片源,可以从视频中抓取当前帧作为一个图像。

  • HTMLCanvasElement:可以使用另一个 Canvas 元素作为图片源。

  • ImageBitmap:一个高性能的位图。

这些源统一由 CanvasImageSource 类型来引用。

context.drawImage(img,x,y)
context.drawImage(img,x,y,width,height)
context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height)

// img     规定要使用的图像、画布或视频。
// sx      可选。开始剪切的 x 坐标位置。
// sy      可选。开始剪切的 y 坐标位置。
// swidth  可选。被剪切图像的宽度。
// sheight 可选。被剪切图像的高度。
// x       在画布上放置图像的 x 坐标位置。
// y       在画布上放置图像的 y 坐标位置。
// width   可选。要使用的图像的宽度。(伸展或缩小图像)
// height  可选。要使用的图像的高度。(伸展或缩小图像)
function drawImage (context) {
    let img = new Image()
    img.src = "./img/pins_3338674420.jpg"
    img.onload = function() {
        // context.drawImage(img, 0, 0, 500, 500, 0, 0, window.innerWidth, window.innerHeight)
        context.drawImage(img, 0, 0, 350, 250)
    }
}

非零环绕原则与奇偶原则

我们一般使用Canvas的fill方法时,是直接进行填充路径。其实fill方法有两个参数可选:nonzero | evenodd。nonzero - 非零环绕原则,也是默认值;evenodd - 奇偶原则。

非零环绕原则nonzero

是用来判断哪些区域属于路径内( 计算结果非0,即为路径内 )。
* 在路径包围的区域中,随便找一点,向外发射一条射线,
* 和所有围绕它的边相交,
* 然后开启一个计数器,从0计数,
* 如果这个射线遇到顺时针围绕,那么+1,
* 如果遇到逆时针围绕,那么-1,
* 如果最终值非0,则这块区域在路径内。结果为0,代表这块区域不要填充,否则,必须填充

奇偶原则

* 在路径包围的区域中,随便找一点,向外发射一条射线,
* 和所有围绕它的边相交,
* 查看相交线的个数,如果为奇数,就填充,如果是偶数,就不填充
drawCircular (context) {
    context.shadowColor = "#545454";
    context.shadowOffsetX = 5;
    context.shadowOffsetY = 5;
    context.shadowBlur = 2;
    context.globalCompositeOperation = 'source-over'

    context.fillStyle = "#00AAAA"
    context.arc(200, 200, 100, 0, Math.PI * 2 ,false)
    context.arc(200, 200, 115, 0, Math.PI * 2 ,true)
    context.fill()
}

其他属性

阴影设置:

shadowColor   // 设置或返回用于阴影的颜色
shadowBlur    // 设置或返回用于阴影的模糊级别
shadowOffsetX // 设置或返回阴影距形状的水平距离
shadowOffsetY // 设置或返回阴影距形状的垂直距离

需要注意的是,这里的阴影同其他属性设置一样,都是基于状态的设置。因此,如果只想为某一个对象应用阴影而不是全局阴影,需要在下次绘制前重置阴影的这四个属性。

全局透明:

globalAlpha
// 默认值为1.0,代表完全不透明,取值范围是0.0(完全透明)~1.0。这个属性与阴影设置是一样的,如果不想针对全局设置不透明度,就得在下次绘制前重置globalAlpha。

橡皮擦:

context.clearRect(x,y,w,h)
// 清空指定矩形上的画布上的像素。它接受四个参数,和其他绘制矩形的方法一样

检测点:

isPointInPath 用于判断在当前路径中是否包含检测点的方法。

isPointInPath(x, y)
isPointInPath(x, y, fillRule)
isPointInPath(path, x, y)
isPointInPath(path, x, y, fillRule)

/*
x:检测点的 X 坐标
y:检测点的 Y 坐标
fillRule:用来决定点在路径内还是在路径外的算法,允许的值:
	nonzero - 非零环绕规则,默认的规则
	evenodd - 奇偶环绕原则
path:Path2D 应用的路径
*/

绘制圆角矩形

function roundRect (ctx, x, y, w, h, r) {
    var min_size = Math.min(w, h);
    if (r > min_size / 2){
        r = min_size / 2
    }
    ctx.beginPath()
    ctx.moveTo(x + r, y)
    ctx.arcTo(x + w, y, x + w, y + h, r)
    ctx.arcTo(x + w, y + h, x, y + h, r)
    ctx.arcTo(x, y + h, x, y, r)
    ctx.arcTo(x, y, x + w, y, r)
    ctx.closePath()
}

绘制圆角图片

function drawRoundImg (ctx, x, y, w, h, r) {
    ctx.save();
    let img = new Image()
    img.src = "./img/pins_3338674420.jpg"
    img.onload = function() {
        roundRect(ctx, x, y, w, h, r)
        ctx.clip();
        ctx.drawImage(img, x, y, w, h)
        ctx.restore()
    }
}

function drawRoundImg2 (ctx, x, y, w, h, r) {
    ctx.save();
    let img = new Image()
    img.src = "./img/pins_3338674420.jpg"
    img.onload = function() {
        let pattern = ctx.createPattern(img, "no-repeat");
        roundRect(ctx, x, y, w, h, r)
        ctx.fillStyle = pattern
        ctx.fill()
    }
}

参考链接

上一页Date对象和日期时间字符串格式下一页Canvas进阶篇

最后更新于1年前

这有帮助吗?

image-20200802153527937

image-20200802154748374

image-20200802154936529

image-20200802160540181

image-20200802160727162

image-20200802162108256

image-20200802162403917

image-20200802162553067

image-20200802162859005

image-20200802163558011

image-20200802173138214
image-20200802173158563

查看DEMO
查看DEMO
查看DEMO
查看DEMO
查看DEMO
查看DEMO
查看DEMO
查看DEMO
查看DEMO
查看DEMO
查看DEMO
查看DEMO
查看DEMO
查看DEMO
查看DEMO 圆角图片1
查看DEMO 圆角图片2
Canvas API中文文档首页地图
MDN - canvas
原生HTML5 Canvas 参考API文档
canvas
image-20200802152344385
image-20200802152715047
image-20200802153043369
image-20200802154344739