1. Canavs绘制直线
<canvas id="canvas"></canvas>
<script>
const myCanvas = document.querySelector('#canvas')
myCanvas.width = 800
myCanvas.height = 800
const context = myCanvas.getContext("2d")
context.moveTo(100, 100)
context.lineTo(700, 700)
context.stroke()
</script>
- ctx.lineWidth = 10, 设置线条的宽度
- ctx.strokeStyle = "#058", 设置线条的宽度
- canvas是基于状态的绘制
2. Canavs绘制多条线段
<canvas id="canvas"></canvas>
<script>
const myCanvas = document.querySelector('#canvas')
myCanvas.width = 800
myCanvas.height = 800
const context = myCanvas.getContext("2d")
context.lineWidth = 10
context.beginPath()
context.moveTo(100, 200)
context.lineTo(300, 400)
context.lineTo(100, 600)
context.strokeStyle = "red"
context.stroke()
context.beginPath()
context.moveTo(300, 200)
context.lineTo(500, 400)
context.lineTo(300, 600)
context.strokeStyle = "green"
context.stroke()
context.beginPath()
context.moveTo(500, 200)
context.lineTo(700, 400)
context.lineTo(500, 600)
context.strokeStyle = "blue"
context.stroke()
</script>
- ctx.beginPath(), 清空子路径列表开始一个新路径的方法。
3. Canavs绘制封闭的多边形
<canvas id="canvas"></canvas>
<script>
const myCanvas = document.querySelector('#canvas')
myCanvas.width = 800
myCanvas.height = 800
const context = myCanvas.getContext("2d")
context.beginPath()
context.moveTo(100, 200)
context.lineTo(300, 400)
context.lineTo(100, 600)
context.closePath()
context.lineWidth = 10
context.strokeStyle = "green"
context.fillStyle = 'yellow'
context.fill()
context.stroke()
</script>
- ctx.closePath(), 将笔点返回到当前子路径起始点的方法。
- ctx.fill(), 填充
- 填充和描边,下面的会覆盖上面的部分
4. Canavs绘制矩形
<canvas id="canvas"></canvas>
<script>
const myCanvas = document.querySelector('#canvas')
myCanvas.width = 800
myCanvas.height = 800
const context = myCanvas.getContext("2d")
drawRect(context, 100, 100, 400, 400, 10, "#058", "red")
function drawRect(ctx, x, y, width, height, borderWidth, borderColor, fillColor) {
ctx.beginPath()
ctx.moveTo(x, y)
ctx.lineTo(x + width, y)
ctx.lineTo(x + width, y + height)
ctx.lineTo(x, y + height)
ctx.closePath()
ctx.lineWidth = borderWidth
ctx.strokeStyle = borderColor
ctx.fillStyle = fillColor
ctx.fill()
ctx.stroke()
}
// 方法二
function drawRect2(ctx, x, y, width, height, borderWidth, borderColor, fillColor) {
ctx.lineWidth = borderWidth
ctx.strokeStyle = borderColor
ctx.fillStyle = fillColor
ctx.fillRect(x, y, width, height)
ctx.strokeRect(x, y, width, height)
}
</script>
- ctx.closePath(), 将笔点返回到当前子路径起始点的方法。
- ctx.lineCap,指定如何绘制每一条线段末端的属性。
5. Canavs绘制五角星
<canvas id="canvas"></canvas>
<script>
const myCanvas = document.querySelector('#canvas')
myCanvas.width = 800
myCanvas.height = 800
const context = myCanvas.getContext("2d")
drawStar(context, 150, 300, 400, 400, 30)
function drawStar(ctx, r, R, x, y, rotate) {
ctx.beginPath()
for (let i = 0; i < 5; i++) {
ctx.lineTo(Math.cos((18 + i * 72 - rotate) / 180 * Math.PI) * R + x, -Math.sin((18 + i * 72 - rotate) / 180 * Math.PI) * R + y)
ctx.lineTo(Math.cos((54 + i * 72 - rotate) / 180 * Math.PI) * r + x, -Math.sin((54 + i * 72 - rotate) / 180 * Math.PI) * r + y)
}
ctx.closePath()
ctx.stroke()
}
</script>
- ctx.lineJoin,用来设置 2 个长度不为 0 的相连部分(线段、圆弧、曲线)如何连接在一起的属性
6. Canavs图像变换和状态保存
Canavs使用图像变换,需要使用状态的保存和恢复;因为Canavs图像变换操作是叠加的
(1)图像变换
- 位移translate(x, y)
- 旋转rotate(deg)
- 缩放scale(sx, sy)
(2)状态的保存和恢复
- save()
- restore()
const myCanvas = document.querySelector('#canvas')
myCanvas.width = 800
myCanvas.height = 800
const context = myCanvas.getContext('2d')
context.fillStyle = "black"
context.fillRect(0, 0, myCanvas.width, myCanvas.height)
for (let i = 0; i < 200; i++) {
const r = Math.random() * 10 + 10;
const x = Math.random() * myCanvas.width;
const y = Math.random() * myCanvas.height;
const a = Math.random() * 360
drawStar(context, r / 2.0, r, x, y, a)
}
function drawStar(ctx, r, R, x, y, rot) {
ctx.save()
ctx.translate(x, y)
ctx.rotate(rot / 180 * Math.PI)
starPath(ctx, r, R)
ctx.fillStyle = "#fb3"
ctx.strokeStyle = "#fd5"
ctx.lineWidth = 3
ctx.lineJoin = "round"
ctx.fill()
ctx.stroke()
ctx.restore()
}
function starPath(ctx, r, R) {
ctx.beginPath()
for (let i = 0; i < 5; i++) {
ctx.lineTo(Math.cos((18 + i * 72) / 180 * Math.PI) * R, -Math.sin((18 + i * 72) / 180 * Math.PI) * R)
ctx.lineTo(Math.cos((54 + i * 72) / 180 * Math.PI) * r, -Math.sin((54 + i * 72) / 180 * Math.PI) * r)
}
ctx.closePath()
}
- ctx.scale(sx, sy),会缩放左上端点的坐标值,会缩放描边宽度,及会缩放图形宽高
7. Canavs矩阵变换transform
// a, d 水平、垂直缩放
// b, c 水平、垂直倾斜
// e, f 水平、垂直位移
ctx.transform(a, b, c, d, e, f)
- ctx.settransform(),覆盖之前所有的ctx.transform()
8. Canavs渐变色
(1)线性渐变
const myCanvas = document.querySelector('#canvas')
myCanvas.width = 800
myCanvas.height = 800
const context = myCanvas.getContext('2d')
const linearGrad = context.createLinearGradient(0, 0, 800, 800)
linearGrad.addColorStop(0.0, '#fff')
linearGrad.addColorStop(1.0, '#000')
context.fillStyle = linearGrad
context.fillRect(0, 0, 800, 800)
(2)径向渐变
const myCanvas = document.querySelector('#canvas')
myCanvas.width = 800
myCanvas.height = 800
const context = myCanvas.getContext('2d')
const radialGrad = context.createRadialGradient(400, 400, 0, 400, 400, 500)
radialGrad.addColorStop(0.0, 'white')
radialGrad.addColorStop(0.25, 'yellow')
radialGrad.addColorStop(0.5, 'green')
radialGrad.addColorStop(0.75, 'blue')
radialGrad.addColorStop(1.0, 'black')
context.fillStyle = radialGrad
context.fillRect(0, 0, 800, 800)
(3)背景图片、画布或video
-
context.createPattern(image/canvas/video, repeat-style)
// 图片作为背景图
const myCanvas = document.querySelector('#canvas')
myCanvas.width = 800
myCanvas.height = 800
const context = myCanvas.getContext('2d')
const backgroundImage = new Image()
backgroundImage.src = './img/flower.png'
backgroundImage.onload = function() {
const pattern = context.createPattern(backgroundImage, 'repeat')
context.fillStyle = pattern
context.fillRect(0, 0, 800, 800)
}
// canvas作为背景图
const myCanvas = document.querySelector('#canvas')
myCanvas.width = 800
myCanvas.height = 800
const context = myCanvas.getContext('2d')
const pattern = context.createPattern(createBackgroundCanvas(), 'repeat')
context.fillStyle = pattern
context.fillRect(0, 0, 800, 800)
function createBackgroundCanvas() {
const backgroundCanvas = document.createElement('canvas')
backgroundCanvas.width = 100
backgroundCanvas.height = 100
const bgCtx = backgroundCanvas.getContext('2d')
drawStar(bgCtx, 25, 50, 50, 50, 0)
return backgroundCanvas
}
function drawStar(ctx, r, R, x, y, rot) {
ctx.save()
ctx.translate(x, y)
ctx.rotate((rot / 180) * Math.PI)
starPath(ctx, r, R)
ctx.fillStyle = '#fb3'
ctx.strokeStyle = '#fd5'
ctx.lineWidth = 3
ctx.lineJoin = 'round'
ctx.fill()
ctx.stroke()
ctx.restore()
}
function starPath(ctx, r, R) {
ctx.beginPath()
for (let i = 0; i < 5; i++) {
ctx.lineTo(
Math.cos(((18 + i * 72) / 180) * Math.PI) * R,
-Math.sin(((18 + i * 72) / 180) * Math.PI) * R
)
ctx.lineTo(
Math.cos(((54 + i * 72) / 180) * Math.PI) * r,
-Math.sin(((54 + i * 72) / 180) * Math.PI) * r
)
}
ctx.closePath()
}
9. Canavs绘制圆角矩形
-
ctx.arc(cx, cy, r, startDeg, endDeg),绘制圆弧路径。
const myCanvas = document.querySelector('#canvas')
myCanvas.width = 800
myCanvas.height = 800
const context = myCanvas.getContext('2d')
// 绘制4x4的网格
fillRoundRect(context, 150, 150, 500, 500, 10, '#bbada0')
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 4; j++) {
fillRoundRect(context, 170 + i * 120, 170 + j * 120, 100, 100, 6, '#ccc0b3')
}
}
// 填充的圆角矩形
function fillRoundRect(ctx, x, y, width, height, radius, fillStyle = 'black') {
if (2 * radius > width || 2 * radius > height) return
ctx.save()
ctx.translate(x, y)
roundRectPath(ctx, width, height, radius)
ctx.fillStyle = fillStyle
ctx.fill()
ctx.restore()
}
// 描边的圆角矩形
function strokeRoundRect(ctx, x, y, width, height, radius, lineWidth = 1, strokeStyle = 'black') {
if (2 * radius > width || 2 * radius > height) return
ctx.save()
ctx.translate(x, y)
roundRectPath(ctx, width, height, radius)
ctx.lineWidth = lineWidth
ctx.strokeStyle = strokeStyle
ctx.stroke()
ctx.restore()
}
function roundRectPath(ctx, width, height, radius) {
ctx.beginPath()
ctx.arc(width - radius, height - radius, radius, 0, Math.PI / 2)
ctx.lineTo(radius, height)
ctx.arc(radius, height - radius, radius, Math.PI / 2, Math.PI)
ctx.lineTo(0, radius)
ctx.arc(radius, radius, radius, Math.PI, Math.PI * 3 / 2)
ctx.lineTo(width - radius, 0)
ctx.arc(width - radius, radius, radius, Math.PI * 3 / 2, Math.PI * 2)
ctx.closePath()
}
10. Canavs绘制弯月
-
ctx.moveTo(x0, y0),开始点
-
ctx.arcTo(x1, y1, x2, y2, radius),控制点和结束点
const myCanvas = document.querySelector('#canvas')
myCanvas.width = 800
myCanvas.height = 800
const context = myCanvas.getContext('2d')
fillMoon(context, 2, 400, 400, 300, 0)
function fillMoon(ctx, d, x, y, R, rotate, fillStyle = '#fb5') {
ctx.save()
ctx.translate(x, y)
ctx.rotate(rotate * Math.PI / 180)
ctx.scale(R, R)
moonPath(ctx, d)
ctx.fillStyle = fillStyle
ctx.fill()
ctx.restore()
}
function moonPath(ctx, d) {
ctx.beginPath()
ctx.arc(0, 0, 1, 0.5 * Math.PI, 1.5 * Math.PI, true)
ctx.moveTo(0, -1)
ctx.arcTo(d, 0, 0, 1, dis(0, -1, d, 0) / d)
ctx.closePath()
}
function dis(x1, y1, x2, y2) {
return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2))
}
11. 贝赛尔曲线
-
ctx.moveTo(x0, y0),开始点
-
ctx.quadraticCurveTo(cpx, cpy, x, y),二次贝塞尔曲线路径。cpx,cpy为控制点的 x, y 轴坐标,x, y为终点的 x, y 轴坐标。
-
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y),三次贝塞尔曲线路径。cpx,cpy为控制点的 x, y 轴坐标,x, y为终点的 x, y 轴坐标。
// 绘制波浪线
const myCanvas = document.querySelector('#canvas')
myCanvas.width = 800
myCanvas.height = 800
const context = myCanvas.getContext('2d')
drawLand(context)
function drawLand(ctx) {
ctx.save()
ctx.beginPath()
ctx.moveTo(0, 600)
ctx.bezierCurveTo(540, 400, 660, 800, 1200, 600)
ctx.lineTo(1200, 800)
ctx.lineTo(0, 800)
ctx.closePath()
const landStyle = ctx.createLinearGradient(0, 800, 0, 0)
landStyle.addColorStop(0.0, '#030')
landStyle.addColorStop(1.0, '#580')
ctx.fillStyle = landStyle
ctx.fill()
ctx.restore()
}
12. canvas文字渲染
- ctx.fillText(text, x, y, [maxWidth])
- ctx.font = font-style 属性, font-variant 属性, font-weight 属性, font-size 属性(必须), line-height 属性, font-family 属性(必须)。例如: ctx.font = "bold 48px serif"
- ctx.textAlign = "left" || "right" || "center" || "start" || "end"
- ctx.textBaseline = "top" || "hanging" || "middle" || "alphabetic" || "ideographic" || "bottom",当前文本基线的属性
- ctx.measureText(text),返回一个关于被测量文本TextMetrics 对象包含的信息(例如它的宽度)。
const myCanvas = document.querySelector('#canvas')
myCanvas.width = 800
myCanvas.height = 800
const context = myCanvas.getContext('2d')
context.font = 'bold 48px Arial'
context.fillStyle = '#058'
context.fillText('canvas text', 40, 100)
context.strokeStyle = '#058'
context.lineWidth = 3
context.strokeText('canvas text', 40, 200)
13. canvas阴影
- ctx.shadowColor = "#058"
- ctx.shadowOffsetX = 10
- ctx.shadowOffsetY = 10
- ctx.shadowBlur = 5
14. canvas全局变量
- ctx.globalAlpha = 1.0,设置图形和图片透明度的属性。
- ctx.globalCompositeOperation = "source-over",设置要在绘制新形状时应用的合成操作的类型。
15. canvas路径方向(非零环绕原则)
// 绘制圆环
const myCanvas = document.querySelector('#canvas')
myCanvas.width = 800
myCanvas.height = 800
const context = myCanvas.getContext('2d')
context.beginPath()
context.arc(400, 400, 300, 0, Math.PI * 2, false)
context.arc(400, 400, 150, 0, Math.PI * 2, true)
context.closePath()
context.fillStyle = "#058"
context.shadowColor = "gray"
context.shadowOffsetX = 10
context.shadowOffsetY = 10
context.shadowBlur = 10
context.fill()
16. canvas检测是否在图形路径里面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>canvas</title>
<style>
.canvas-box {
text-align: center;
}
</style>
</head>
<body>
<div class="canvas-box">
<canvas id="cvs" width="400" height="400">不支持canvas</canvas>
</div>
<script>
let cvs = document.getElementById('cvs')
let ctx = cvs.getContext('2d')
let balls = []
cvs.width = 400
cvs.height = 400
for (let i = 0; i < 10; i++) {
balls[i] = {
x: Math.random() * cvs.width,
y: Math.random() * cvs.height,
r: Math.random() * 50 + 20,
}
circle(balls[i].x, balls[i].y, balls[i].r)
ctx.fill()
}
function circle(x, y, r) {
ctx.fillStyle = '#058'
ctx.beginPath()
ctx.arc(x, y, r, 0, Math.PI * 2)
ctx.closePath()
}
// 监听鼠标事件
cvs.addEventListener('mousemove', function (e) {
// 获取鼠标的坐标
let x = e.clientX - cvs.getBoundingClientRect().left,
y = e.clientY - cvs.getBoundingClientRect().top
ctx.clearRect(0, 0, 400, 400)
// 遍历绘制图形
for (let i = balls.length; i--; ) {
circle(balls[i].x, balls[i].y, balls[i].r)
// 每绘制一个图形就判断一次当前鼠标的坐标是否在这个图形上,然后进行自定义操作
if (ctx.isPointInPath(x, y)) {
ctx.fillStyle = '#f00'
} else {
ctx.fillStyle = '#058'
}
ctx.fill()
}
})
</script>
</body>
</html>
17. canvas剪辑区域clip()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>canvas</title>
<style>
.canvas-box {
text-align: center;
}
</style>
</head>
<body>
<div class="canvas-box">
<canvas id="cvs" width="400" height="400">不支持canvas</canvas>
</div>
<script>
let cvs = document.getElementById('cvs')
let ctx = cvs.getContext('2d')
cvs.width = 400
cvs.height = 400
function circle(x, y, r) {
ctx.fillStyle = '#058'
ctx.beginPath()
ctx.arc(x, y, r, 0, Math.PI * 2)
ctx.closePath()
}
circle(100, 100, 100)
ctx.fill()
ctx.clip()
ctx.font = 'bold 48px Arial'
ctx.fillStyle = 'white'
ctx.fillText('canvas text', 0, 100)
ctx.lineWidth = 3
ctx.strokeStyle = 'white'
ctx.strokeText('canvas text', 0, 150)
</script>
</body>
</html>