Canvas入门教程

  • 1. Canvas初识
    • 1.1 Canvas基础
    • 1.2 Canvas填充与路径绘制
    • 1.3 Canvas绘制圆
    • 1.4 Canvas绘制折线线段
    • 1.5 贝塞尔曲线实现
    • 1.6 封装路径 `Path2D`
    • 1.7 颜色样式控制
    • 1.8 线型渐变&径向渐变&圆锥渐变
    • 1.9 pattern印章填充
  • 2. Canvas初阶
    • 2.1 线段及虚线样式
    • 2.2 阴影
    • 2.3 图像和视频
    • 2.4 文字绘制
    • 2.5 位移&缩放&旋转&变换
    • 2.6 合成图像
    • 2.7 裁剪
    • 2.8 状态的保存&恢复
    • 2.9 像素操作
    • 2.10 封装绘制
  • 3. Canvas实例
    • 3.1 时钟绘制

1. Canvas初识

1.1 Canvas基础

  1. 初始化canvas
<canvas id="canvas" width="600" height="400"><canvas><script>const canvas = document.getElementById('canvas')let ctx = canvas.getContext('2d')orlet ctx = document.getElementById('canvas').getContext('2d')// ...
</script>
  1. 开始绘制到结束绘制 beginPath&closePath
ctx.beginPath();
// ....
ctx.closePath();
  1. 移动画笔 moveTo 可防止绘制一条连续的路径
ctx.moveTo(x, y);

1.2 Canvas填充与路径绘制

  1. 绘制图形路径 strokeRect
ctx.strokeRect(x, y, 矩形宽度, 矩形高度);
or
ctx.rect(x, y, 矩形宽度, 矩形高度)
ctx.stroke()
  1. 清除图像 clearRect
ctx.clearRect(x, y, 清除宽度(clientWidth), 清除高度(clientHeight));
  1. 填充图形 fillRect
ctx.fillRect(x, y, 矩形宽度, 矩形高度);
or
ctx.rect(x, y, 矩形宽度, 矩形高度)
ctx.fill()

1.3 Canvas绘制圆

  1. 绘制圆形 arc(圆心x, 圆心y, radius, 开始角度, 结束角度, 逆时针&顺时针)
// false = 顺时针,true = 逆时针,默认值是false
ctx.arc(300, 200, 50, 0, Math.PI / 2, true)
ctx.fill()
  1. 绘制圆弧 arcTo
// ctx.arcTo(x1, y1, x2, y2, radius)
ctx.arcTo(300, 200, 50, 0)
ctx.fill()

1.4 Canvas绘制折线线段

  1. 绘制直线 lineTo
ctx.lineTo(x, y);

1.5 贝塞尔曲线实现

  1. 二次贝塞曲线 quadraticCurveTo(控制点cpx, 控制点cpy, 终点x, 终点y)

例:绘制气泡框

ctx.moveTo(200, 300)
ctx.quadraicCurveTo(150, 300, 150, 200)
ctx.quadraicCurveTo(150, 100, 300, 100)
ctx.quadraicCurveTo(450, 100, 450, 200)
ctx.quadraicCurveTo(450, 300, 250, 300)ctx.quadraicCurveTo(250, 350, 150, 350)
ctx.quadraicCurveTo(200, 350, 200, 300)ctx.stroke()
  1. 三次贝塞曲线 bezierCurveTo(控制点cpx1, 控制点cpy1, 控制点cpx2, 控制点cpy2, 终点x, 终点y)

例:绘制爱心

ctx.moveTo(300, 200)
ctx.bezierCurveTo(350, 150, 400, 240, 300, 280)ctx.moveTo(300, 200)
ctx.bezierCurveTo(250, 150, 200, 240, 300, 280)ctx.stroke()

1.6 封装路径 Path2D

  1. 通过 Path2D 封装路径,更好的开发
let heartPath = new Path2D()
heartPath.moveTo(300, 200)
heartPath.bezierCurveTo(350, 150, 400, 240, 300, 280)heartPath.moveTo(300, 200)
heartPath.bezierCurveTo(250, 150, 200, 240, 300, 280)ctx.stroke(heartPath)
  • SVG写法 可填路径

    • 位置 M x y
    • 水平 h
    • 垂直 v
    • 回到起点 z
let polyline = new Path2D('M10 10 h 80 v 80 h-80 z')
ctx.stroke(polyline)

1.7 颜色样式控制

  1. 设置画笔颜色 strokeStyle
ctx.strokeStyle = '#f00'
  1. 设置填充颜色 fillStyle
ctx.fillStyle = 'rgba(255, 0, 0)'
  1. 设置全局透明度 globalAlpha
ctx.globalAlpha = 0.5

1.8 线型渐变&径向渐变&圆锥渐变

  1. 线型渐变 createLinearGradient
let linearGradient = ctx.createLinearGradient(0, 0, 600, 400)
linearGradient.addColorStop(0, 'red')
linearGradient.addColorStop(0.3, 'deeppink')
linearGradient.addColorStop(1, 'blue')
ctx.fillStyle = linearGradient
ctx.fillRect(100, 200, 300, 300)
  1. 径向渐变 createRadialGradient(startX, startY, r0, endX, endY, r1)
let radialGradient = ctx.createRadialGradient(300, 200, 0, 300, 200, 100)
radialGradient.addColorStop(0, 'red')
radialGradient.addColorStop(0.3, 'deeppink')
radialGradient.addColorStop(1, 'blue')
ctx.fillStyle = radialGradient
ctx.fillRect(100, 200, 300, 300)
  1. 圆锥渐变 createConicGradient(角度, x, y)
let conicGradient = ctx.createConicGradient(0, 300, 200)
conicGradient.addColorStop(0, 'red')
conicGradient.addColorStop(1, 'blue')
ctx.fillStyle = conicGradient
ctx.fillRect(0, 0, 300, 200)
  1. 请求动画帧 requestAnimationFrame
let index = 0
let render = () => {ctx.clearRect(0, 0, 600, 400)index += 0.01if (index > 1) index = 0let linearGradient = ctx.createLinearGradient(0, 0, 600, 400)linearGradient.addColorStop(0, 'red')linearGradient.addColorStop(index, 'deeppink')linearGradient.addColorStop(1, 'blue')ctx.fillStyle = linearGradientctx.fillRect(100, 200, 300, 300)requestAnimationFrame(render)
}requestAnimationFrame(render)

1.9 pattern印章填充

  1. 印章 createPattern(img, 重复方式)
let img = new Image()
img.src = ''img.onload = () => {// img 可以是image,也可以是canvas对象let pattern = ctx.createPattern(img, 'no-repeat')ctx.fillStyle = patternctx.fillRect(0, 0, 600, 400)
}

2. Canvas初阶

2.1 线段及虚线样式

  1. 线段 lineTo
ctx.moveTo(200, 150)
ctx.lineTo(300, 200)
ctx.lineTo(400, 150)
  • 线段宽度 lineWidth
  • 线条端点样式 lineCap
    • 平齐 butt
    • 半圆 round
    • 正方形 square
  • 连接处样式 lineJoin
    • 尖角 mitter
    • 圆滑 round
    • 折断 bevel
  • 斜截面限制 miterLimit
  1. 虚线 setLineDash([虚线长度, 空白长度])
  • 虚线偏移 lineDashOffset
let index = 0
let render = () => {ctx.clearRect(0, 0, 600, 400)index++if (index > 400) index = 0ctx.moveTo(150, 150)ctx.lineTo(300, 200)ctx.lineTo(450, 150)ctx.setLineDash([20, 30])ctx.lineDashOffset = indexctx.stroke()requestAnimationFrame(render)
}render()

2.2 阴影

  1. 设置阴影
ctx.shadowOffsetX = 10
ctx.shadowOffsetY = 10
ctx.shadowBlur = 5
ctx.shadowColor = 'rgba(255, 100, 100, 1)'

2.3 图像和视频

  1. 图像绘制 drawImage
  • drawImage(img, x, y) 简单显示
  • drawImage(img, x, y, 缩放宽度, 缩放高度) 缩放
  • drawImage(img, 裁切位置x, 裁切位置y, 裁切宽度, 裁切高度, x, y, 缩放宽度, 缩放高度) 裁切
let img = new Image()
img.src = ''
img.onload = () => {// 1. drawImage(img, x, y)ctx.drawImage(img, 0, 0)// 2. drawImage(img, x, y, 缩放宽度, 缩放高度)ctx.drawImage(img, 0, 0, 600, 400)
}
  1. 视频绘制
let video = document.createElement('video')
video.src = ''
video.play()
let render = () => {ctx.drawImage(video, 0, 0, 600, 400)requestAnimationFrame(render)
}
render()

2.4 文字绘制

  1. 字体 font
ctx.font = '100px Microsoft YaHei'
  1. 填充渲染文字 fillText(text, x, y, 文字最大宽度)
ctx.fillText('txt', 300, 200, 100)
  1. 轮廓 stokeText(text, x, y, 文字最大宽度)
ctx.strokeText('txt', 300, 200)
  1. 文本对齐 textAlign
  2. 文本基线对齐 textBaseLine textBaseline top bottom alphabetic
  3. 文本方向 direction 反向:rtl
  4. 预测文本宽度 measureText
let txt = ctx.measureText('txt')

2.5 位移&缩放&旋转&变换

注: 位移&缩放&旋转的是坐标系

  1. 位移 translate(x, y)
ctx.translate(100, 100)
ctx.fillRect(0, 0, 50, 50)
  1. 缩放 scale(h, v)
ctx.scale(5, 2)
ctx.fillRect(0, 0, 50, 50)
  1. 旋转 rotate(deg)
ctx.rotate(Math.PI / 6)
ctx.fillRect(0, 0, 50, 50)
  1. 变形 transform(a, b, c, d, e, f)

[ a c e b d f 0 0 1 ] \left[ \begin{matrix} a & c & e \\ b & d & f \\ 0 & 0 & 1 \end{matrix} \right] ​ab0​cd0​ef1​ ​

  • a 和 b为水平坐标轴
  • c 和 d为垂直坐标轴
  • e 和 f为位移距离
ctx.transform(1, 0, 0, 1, 100, 100)
ctx.fillRect(0, 0, 50, 50)

2.6 合成图像

图层合成 [globalCompositeOperation](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation)

  • source-over 默认设置,并在现有画布上下文之上绘制新图形。
  • source-in 在新图形和目标画布重叠的地方绘制。其他的都是透明的。
  • source-out 在不与现有画布内容重叠的地方绘制新图形。
  • source-atop 新图形只在与现有画布内容重叠的地方绘制。
  • destination-over 在现有的画布内容后面绘制新的图形。
  • destination-in 现有的画布内容保持在新图形和现有画布内容重叠的位置。其他的都是透明的。
  • destination-out 现有内容保持在新图形不重叠的地方。
  • destination-atop 现有的画布只保留与新图形重叠的部分,新的图形是在画布内容后面绘制的。
  • lighter 两个重叠图形的颜色是通过颜色值相加来确定的。
  • copy 只显示新图形。
  • xor 图像中,那些重叠和正常绘制之外的其他地方是透明的。
  • multiply 将顶层像素与底层相应像素相乘,结果是一幅更黑暗的图片。
  • screen 像素被倒转,相乘,再倒转,结果是一幅更明亮的图片。
  • overlay multiply 和 screen 的结合,原本暗的地方更暗,原本亮的地方更亮。
  • darken 保留两个图层中最暗的像素。
  • lighten 保留两个图层中最亮的像素。
  • color-dodge 将底层除以顶层的反置。
  • color-burn 将反置的底层除以顶层,然后将结果反过来。
  • hard-light 屏幕相乘(A combination of multiply and screen)类似于叠加,但上下图层互换了
  • soft-light 用顶层减去底层或者相反来得到一个正值。
  • difference 一个柔和版本的强光(hard-light)。纯黑或纯白不会导致纯黑或纯白。
  • exclusion 和 difference 相似,但对比度较低。
  • hue 保留了底层的亮度(luma)和色度(chroma),同时采用了顶层的色调(hue)。
  • saturation 保留底层的亮度(luma)和色调(hue),同时采用顶层的色度(chroma)。
  • color 保留了底层的亮度(luma),同时采用了顶层的色调 (hue) 和色度 (chroma)。
  • luminosity 保持底层的色调(hue)和色度(chroma),同时采用顶层的亮度(luma)。

2.7 裁剪

裁剪路径 clip

let heartPath = new Path2D()
heartPath.moveTo(300, 200)
heartPath.bezierCurveTo(350, 150, 400, 240, 300, 280)heartPath.moveTo(300, 200)
heartPath.bezierCurveTo(250, 150, 200, 240, 300, 280)ctx.clip(heartPath)
ctx.stroke(heartPath)let img = new Image()
img.src = ''
img.onload = () => {ctx.drawImage(img, 0, 0, 600, 400)
}

2.8 状态的保存&恢复

  1. 状态保存 save
ctx.fillStyle = 'red'
ctx.fillRect(0, 0, 100, 100)
ctx.save()
  1. 状态恢复 restore
ctx.fillStyle = 'red'
ctx.fillRect(0, 0, 100, 100)
ctx.save()ctx.fillStyle = 'blue'
ctx.fillRect(100, 100, 100, 100)
ctx.save()ctx.fillStyle = 'yellow'
ctx.fillRect(200, 200, 100, 100)ctx.restore()
ctx.fillRect(300, 300, 100, 100) // color: bluectx.restore()
ctx.fillRect(400, 400, 100, 100) // color: red

2.9 像素操作

  1. 获取像素 getImageData
  2. 渲染像素 putImageData(imagedata, dx, dy, dirtyX<可选>, dirtyY<可选>, dirtyWidth<可选>, dirtyHeight<可选>)
let img = new Image()
img.src = ''
img.onload = () => {ctx.drawImage(img, 0, 0, 600, 400)
}/* 将图层改成灰色 */
// 获取像素数据
let imageData = ctx.getImageData(0, 0, 600, 400)
// 循环修改数据
for (let i = 0; i < imageData.data.length; i += 4) {// 计算当前像素的平均值let avg = (imageData.data[i] + imageData.data[i + 1] + imageData.data[i + 2]) / 3imageData.data[i] = avgimageData.data[i + 1] = avgimageData.data[i + 2] = avgimageData.data[i + 3] = 255
}
// 将修改的数据重新渲染到画布上
ctx.putImageData(imageData, 0, 0)

2.10 封装绘制

  1. 封装体
class Heart {constructor(x, y) {this.x = xthis.y = y}draw () {this.heartPath = new Path2D()// 起点this.heartPath.moveTo(this.x, this.y)this.heartPath.bezierCurveTo(this.x + 50, this.y - 50, this.x + 100, this.y + 40, this.x, this.y + 80)this.heartPath.moveTo(this.x, this.y)this.heartPath.bezierCurveTo(this.x - 50, this.y - 50, this.x - 100, this.y + 40, this.x, this.y + 80)ctx.save()ctx.fillStyle = `rgba(${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, 0.5)`ctx.fill(this.heartPath)// ctx.translate(this.x, this.y)ctx.scale(0.5, 0.5)// ctx.stroke(heartPath)ctx.restore()}
}const render = () => {ctx.clearRect(0, 0, 600, 400)let heart = new Heart(300, 200)heart.draw()requestAnimationFrame(render)
}render()

3. Canvas实例

3.1 时钟绘制

<canvas id="canvas" width="800" height="600"></canvas>
let ctx = document.getElementById('canvas').getContext('2d')const render = () => {ctx.clearRect(0, 0, 800, 600)ctx.save()ctx.translate(400, 300)ctx.rotate(-Math.PI / 2)ctx.lineCap = 'round'ctx.save()for (let i = 0; i < 12; i++) {ctx.beginPath()ctx.moveTo(170, 0)ctx.lineTo(190, 0)ctx.lineWidth = 8ctx.strokeStyle = 'gray'ctx.stroke()ctx.closePath()ctx.rotate(2 * Math.PI / 12)}ctx.restore()ctx.save()for (let i = 0; i < 60; i++) {ctx.beginPath()ctx.moveTo(180, 0)ctx.lineTo(190, 0)ctx.lineWidth = 2ctx.strokeStyle = 'gray'ctx.stroke()ctx.closePath()ctx.rotate(2 * Math.PI / 60)}ctx.restore()// 获取当前时间let time = new Date()// 绘制秒针ctx.save()let second = time.getSeconds()ctx.rotate(2 * Math.PI / 60 * second)ctx.beginPath()ctx.moveTo(-30, 0)ctx.lineTo(190, 0)ctx.lineWidth = 2ctx.strokeStyle = 'red'ctx.stroke()ctx.closePath()ctx.restore()// 绘制分针ctx.save()let minute = time.getMinutes()ctx.rotate(2 * Math.PI / 60 * minute + 2 * Math.PI / 60 / 60 * second)ctx.beginPath()ctx.moveTo(-20, 0)ctx.lineTo(130, 0)ctx.lineWidth = 4ctx.strokeStyle = '#888'ctx.stroke()ctx.closePath()ctx.restore()// 绘制时针ctx.save()let hour = time.getHours() >= 12 ? time.getHours() - 12 : time.getHours()ctx.rotate(2 * Math.PI / 12 * hour + 2 * Math.PI / 12 / 60 * minute + 2 * Math.PI / 12 / 60 / 60 * second)ctx.beginPath()ctx.moveTo(-15, 0)ctx.lineTo(110, 0)ctx.lineWidth = 8ctx.strokeStyle = '#333'ctx.stroke()ctx.closePath()ctx.restore()ctx.restore()requestAnimationFrame(render)
}
render()

Canvas入门教程相关推荐

  1. 最【通俗易懂】的 canvas 入门教程(多图预警)

    最[通俗易懂]的 canvas 入门教程(多图预警) canvas画布的宽度与高度 canvas 标签只有两个常用的标签属性:width 和 height. 当没有设置宽度和高度的时候,canvas ...

  2. canvas学习笔记(下篇) -- canvas入门教程--保存状态/变形/旋转/缩放/矩阵变换/综合案例(星空/时钟/小球)...

    [下篇] -- 建议学习时间4小时  课程共(上中下)三篇 此笔记是我初次接触canvas的时候的学习笔记,这次特意整理为博客供大家入门学习,几乎涵盖了canvas所有的基础知识,并且有众多练习案例, ...

  3. canvas详细入门教程(1W字 吐血分享)

    大家好,我是潘潘 今天为大家带来的是我已经写了很久了的canvas详细教程,对入门canvas很有帮助. 点击跳转原文: canvas详细教程原文 canvas是什么? 简单来说,<canvas ...

  4. Android基础入门教程——8.3.18 Canvas API详解(Part 3)Matrix和drawBitmapMash

    Android基础入门教程--8.3.18 Canvas API详解(Part 3)Matrix和drawBitmapMash 标签(空格分隔): Android基础入门教程 本节引言: 在Canva ...

  5. WPF入门教程(七)---依赖属性(3)(转)

    WPF入门教程(七)---依赖属性(3) 2018年08月24日 08:33:43 weixin_38029882 阅读数:50 四. 只读依赖属性 在以前在对于非WPF的功能来说,对于类的属性的封装 ...

  6. canvas入门实战--邀请卡生成与下载

    1.前言 写了很多的javascript和css3的文章,是时候写一篇canvas的了.canvas是html5提供的一个新的功能!至于作用,就是一个画布.然后画笔就是javascript.canva ...

  7. 最新Android基础入门教程目录(完结版)

    第一章:环境搭建与开发相关(已完结 10/10) https://blog.csdn.net/coder_pig/article/details/50000773 Android基础入门教程--1.1 ...

  8. Python基础教程,Python入门教程

    Python 是一门上手简单.功能强大.通用型的脚本编程语言.Python 类库极其丰富,这使得 Python 几乎无所不能,网站开发.软件开发.大数据分析.网络爬虫.机器学习等都不在话下. 这套 P ...

  9. D3.js的v5版本入门教程(第六章)——做一个简单的图表

    D3.js的v5版本入门教程(第六章) 从这一章开始,进入正式的d3,js绘图阶段,有了前面几章基本知识的积累,这样看接下来的绘图代码才不会觉得比较辛苦 做一个简单的图表 为了做一个简单的图表,我们还 ...

最新文章

  1. NeurIPS 2020 :ReID任务大幅领先,港中文开源自步对比学习框架,充分挖掘无监督学习样本...
  2. 计算机禁用了网络怎么qtyong,如何禁用Qpushqt按钮?
  3. 从JSON数据中取出相关数据
  4. 二叉树先序遍历递归算法(图解)
  5. 配置 --- vscode中react格式化解决方案
  6. SELECT的学习以及在socket中的应用
  7. 牛客16464 神奇的幻方
  8. MySQL-回表查询与索引覆盖
  9. 如何花式计算20的阶乘?
  10. Python的Django框架中的URL配置与松耦合
  11. BP神经网络预测实例(matlab代码,神经网络工具箱)
  12. xshell复制粘贴快捷键
  13. C#调用Outlook发送邮件
  14. 集合的特性(成员操作符、for循环遍历)
  15. xshell4 设置自动记录日志
  16. Plus and Multiply
  17. jQuery添加、删除元素
  18. k8s v1.17 新特性预告: 拓扑感知服务路由
  19. 学计算机的建议大几买电脑,大学生什么时候买电脑合适?来看看学长的建议
  20. 机器学习基础-关于matplotlib的动态图显示操作

热门文章

  1. python个人笔录
  2. 前端js之浮动和定位
  3. 延迟审核终极解决办法,就没有搞不定的苹果审核!2周成功上架AppStore!附教程!
  4. 怎样做好一个管理者?
  5. 计算机组装常见问题,组装电脑常见错误有哪些 组装电脑常见错误汇总【详细介绍】...
  6. (超级有用)ThinkPHP 5漏洞分析之SQL注入
  7. 计算机类免费电子书共享
  8. AcWing 4956. 冶炼金属
  9. 阿里云视频点播 批量上传
  10. 【angular-实践】导入/导出excel