Canvas入门教程
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基础
- 初始化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>
- 开始绘制到结束绘制
beginPath&closePath
ctx.beginPath();
// ....
ctx.closePath();
- 移动画笔
moveTo
可防止绘制一条连续的路径
ctx.moveTo(x, y);
1.2 Canvas填充与路径绘制
- 绘制图形路径
strokeRect
ctx.strokeRect(x, y, 矩形宽度, 矩形高度);
or
ctx.rect(x, y, 矩形宽度, 矩形高度)
ctx.stroke()
- 清除图像
clearRect
ctx.clearRect(x, y, 清除宽度(clientWidth), 清除高度(clientHeight));
- 填充图形
fillRect
ctx.fillRect(x, y, 矩形宽度, 矩形高度);
or
ctx.rect(x, y, 矩形宽度, 矩形高度)
ctx.fill()
1.3 Canvas绘制圆
- 绘制圆形
arc(圆心x, 圆心y, radius, 开始角度, 结束角度, 逆时针&顺时针)
// false = 顺时针,true = 逆时针,默认值是false
ctx.arc(300, 200, 50, 0, Math.PI / 2, true)
ctx.fill()
- 绘制圆弧
arcTo
// ctx.arcTo(x1, y1, x2, y2, radius)
ctx.arcTo(300, 200, 50, 0)
ctx.fill()
1.4 Canvas绘制折线线段
- 绘制直线
lineTo
ctx.lineTo(x, y);
1.5 贝塞尔曲线实现
- 二次贝塞曲线
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()
- 三次贝塞曲线
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
- 通过 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 颜色样式控制
- 设置画笔颜色
strokeStyle
ctx.strokeStyle = '#f00'
- 设置填充颜色
fillStyle
ctx.fillStyle = 'rgba(255, 0, 0)'
- 设置全局透明度
globalAlpha
ctx.globalAlpha = 0.5
1.8 线型渐变&径向渐变&圆锥渐变
- 线型渐变
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)
- 径向渐变
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)
- 圆锥渐变
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)
- 请求动画帧
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印章填充
- 印章
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 线段及虚线样式
- 线段
lineTo
ctx.moveTo(200, 150)
ctx.lineTo(300, 200)
ctx.lineTo(400, 150)
- 线段宽度
lineWidth
- 线条端点样式
lineCap
- 平齐
butt
- 半圆
round
- 正方形
square
- 平齐
- 连接处样式
lineJoin
- 尖角
mitter
- 圆滑
round
- 折断
bevel
- 尖角
- 斜截面限制
miterLimit
- 虚线
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 阴影
- 设置阴影
ctx.shadowOffsetX = 10
ctx.shadowOffsetY = 10
ctx.shadowBlur = 5
ctx.shadowColor = 'rgba(255, 100, 100, 1)'
2.3 图像和视频
- 图像绘制
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)
}
- 视频绘制
let video = document.createElement('video')
video.src = ''
video.play()
let render = () => {ctx.drawImage(video, 0, 0, 600, 400)requestAnimationFrame(render)
}
render()
2.4 文字绘制
- 字体
font
ctx.font = '100px Microsoft YaHei'
- 填充渲染文字
fillText(text, x, y, 文字最大宽度)
ctx.fillText('txt', 300, 200, 100)
- 轮廓
stokeText(text, x, y, 文字最大宽度)
ctx.strokeText('txt', 300, 200)
- 文本对齐
textAlign
- 文本基线对齐
textBaseLine
textBaseline top bottom alphabetic - 文本方向
direction
反向:rtl - 预测文本宽度
measureText
let txt = ctx.measureText('txt')
2.5 位移&缩放&旋转&变换
注: 位移&缩放&旋转的是坐标系
- 位移
translate(x, y)
ctx.translate(100, 100)
ctx.fillRect(0, 0, 50, 50)
- 缩放
scale(h, v)
ctx.scale(5, 2)
ctx.fillRect(0, 0, 50, 50)
- 旋转
rotate(deg)
ctx.rotate(Math.PI / 6)
ctx.fillRect(0, 0, 50, 50)
- 变形
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] ab0cd0ef1
- 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 状态的保存&恢复
- 状态保存
save
ctx.fillStyle = 'red'
ctx.fillRect(0, 0, 100, 100)
ctx.save()
- 状态恢复
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 像素操作
- 获取像素
getImageData
- 渲染像素
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 封装绘制
- 封装体
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入门教程相关推荐
- 最【通俗易懂】的 canvas 入门教程(多图预警)
最[通俗易懂]的 canvas 入门教程(多图预警) canvas画布的宽度与高度 canvas 标签只有两个常用的标签属性:width 和 height. 当没有设置宽度和高度的时候,canvas ...
- canvas学习笔记(下篇) -- canvas入门教程--保存状态/变形/旋转/缩放/矩阵变换/综合案例(星空/时钟/小球)...
[下篇] -- 建议学习时间4小时 课程共(上中下)三篇 此笔记是我初次接触canvas的时候的学习笔记,这次特意整理为博客供大家入门学习,几乎涵盖了canvas所有的基础知识,并且有众多练习案例, ...
- canvas详细入门教程(1W字 吐血分享)
大家好,我是潘潘 今天为大家带来的是我已经写了很久了的canvas详细教程,对入门canvas很有帮助. 点击跳转原文: canvas详细教程原文 canvas是什么? 简单来说,<canvas ...
- Android基础入门教程——8.3.18 Canvas API详解(Part 3)Matrix和drawBitmapMash
Android基础入门教程--8.3.18 Canvas API详解(Part 3)Matrix和drawBitmapMash 标签(空格分隔): Android基础入门教程 本节引言: 在Canva ...
- WPF入门教程(七)---依赖属性(3)(转)
WPF入门教程(七)---依赖属性(3) 2018年08月24日 08:33:43 weixin_38029882 阅读数:50 四. 只读依赖属性 在以前在对于非WPF的功能来说,对于类的属性的封装 ...
- canvas入门实战--邀请卡生成与下载
1.前言 写了很多的javascript和css3的文章,是时候写一篇canvas的了.canvas是html5提供的一个新的功能!至于作用,就是一个画布.然后画笔就是javascript.canva ...
- 最新Android基础入门教程目录(完结版)
第一章:环境搭建与开发相关(已完结 10/10) https://blog.csdn.net/coder_pig/article/details/50000773 Android基础入门教程--1.1 ...
- Python基础教程,Python入门教程
Python 是一门上手简单.功能强大.通用型的脚本编程语言.Python 类库极其丰富,这使得 Python 几乎无所不能,网站开发.软件开发.大数据分析.网络爬虫.机器学习等都不在话下. 这套 P ...
- D3.js的v5版本入门教程(第六章)——做一个简单的图表
D3.js的v5版本入门教程(第六章) 从这一章开始,进入正式的d3,js绘图阶段,有了前面几章基本知识的积累,这样看接下来的绘图代码才不会觉得比较辛苦 做一个简单的图表 为了做一个简单的图表,我们还 ...
最新文章
- NeurIPS 2020 :ReID任务大幅领先,港中文开源自步对比学习框架,充分挖掘无监督学习样本...
- 计算机禁用了网络怎么qtyong,如何禁用Qpushqt按钮?
- 从JSON数据中取出相关数据
- 二叉树先序遍历递归算法(图解)
- 配置 --- vscode中react格式化解决方案
- SELECT的学习以及在socket中的应用
- 牛客16464 神奇的幻方
- MySQL-回表查询与索引覆盖
- 如何花式计算20的阶乘?
- Python的Django框架中的URL配置与松耦合
- BP神经网络预测实例(matlab代码,神经网络工具箱)
- xshell复制粘贴快捷键
- C#调用Outlook发送邮件
- 集合的特性(成员操作符、for循环遍历)
- xshell4 设置自动记录日志
- Plus and Multiply
- jQuery添加、删除元素
- k8s v1.17 新特性预告: 拓扑感知服务路由
- 学计算机的建议大几买电脑,大学生什么时候买电脑合适?来看看学长的建议
- 机器学习基础-关于matplotlib的动态图显示操作