Canvas:开启web上的图形编程之门
一.概述
<canvas>
元素是HTML5引入的一个新标签,它允许浏览器绘制二维图形和图像。这个元素本身并不具备绘图能力。实际上,它提供的只是一个容器,而绘图操作则需要使用JavaScript来完成。Canvas的API极其丰富,支持绘制文本、线条、路径、矩形、圆形、贝塞尔曲线,以及图像操作等功能。
二.图形编程
1.w3c坐标系
在web网页,一般提到坐标系大家就能想到下图,没错,Canvas画布所遵循的坐标系统也是这个
2.对应接口
HTMLCanvasElement接口定义了相应API的一部分,正是因为它的存在,让我们可以用相应方法操作canvas元素的属性等,下面的图片出自官网HTML Standard,目前还没有人翻译这一部分,所以读起来还是需要结合网上的许多其它资料才能完全理解
3. 基础使用
先要像如下开启一个canvas画布(默认画布的宽度为300px,高度为150px),再通过js找到相应的canvas元素,然后根据getContext方法
找到canvas元素的上下文,这个方法根据官网来看可以传入'2D','webgl','webgl2','placeholder',
'bitmaprenderer'或者是'none'这几个参数,入门的话当然是从操作二维元素开始。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style></style>
</head>
<body>
<canvas id="can" width="300" height="300" style="border: 1px solid purple;"></canvas>
<script>
const can = document.getElementById('can')
//alpha: true 表示Canvas绘图表面包含alpha(透明度)通道,允许绘制半透明的图形。
//desynchronized: false 旨在减少绘图延迟,对于需要高性能绘制的应用特别有用。
const context = can.getContext('2d',{alpha: true, desynchronized: false})
</script>
</body>
</html>
更进阶的使用请看下图(自官网截图)
4. 直线
moveTo(x1, y1)
:起点坐标 (x, y)lineTo(x2, y2)
:下一个点的坐标 (x, y)stroke()
:将所有坐标用一条线连起来
(1)一条直线
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
<style>
#can {
margin: 0 auto;
margin-top: 200px;
display: block;
border: 1px solid hotpink;
}
</style>
</head>
<body>
<canvas id="can" width="300" height="300"></canvas>
<script>
const can = document.getElementById('can')
const context = can.getContext('2d')
context.moveTo(100, 100)
context.lineTo(200, 100)
context.stroke()
</script>
</body>
</html>
(2)多条直线
a.相同样式
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
<style>
#can {
margin: 0 auto;
margin-top: 200px;
display: block;
border: 1px solid hotpink;
}
</style>
</head>
<body>
<canvas id="can" width="300" height="300"></canvas>
<script>
const can = document.getElementById('can')
const context = can.getContext('2d')
context.moveTo(20, 100)
context.lineTo(200, 100)
context.stroke()
context.moveTo(20, 150.5)
context.lineTo(200, 150.5)
context.stroke()
</script>
</body>
上面的线条看起来比下面的粗,原因是因为线的中心会和画布像素点的底部对齐, 当像素为整数时,相当于占据了两行像素,一半在上,一半在下。
b.不同样式
也就是说要通过beginPath新开一条路径,分别配上不一样的样式
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
<style>
#can {
margin: 0 auto;
margin-top: 200px;
display: block;
border: 1px solid hotpink;
}
</style>
</head>
<body>
<canvas id="can" width="300" height="300"></canvas>
<script>
const can = document.getElementById('can')
const context = can.getContext('2d')
context.lineWidth = 20
context.strokeStyle = 'red'
context.moveTo(20, 100)
context.lineTo(200, 100)
context.stroke()
context.beginPath()
context.lineWidth = 10
context.strokeStyle = 'aqua'
context.moveTo(20, 120.5)
context.lineTo(200, 120.5)
context.stroke()
</script>
</body>
关于线的样式:
修改线宽:调整lineWidth
修改线帽:context.lineCap = "
butt
"(默认)/"round
"/"square
"修改线条连接方式:context.lineJoin = "miter"(斜接)(默认)/"bevel"(斜面)/"round"(圆角)
设置斜接长度限制:context.miterLimit = ??
设置为虚线:context.setLineDash([?,?,?]) //单参的时候实虚线都为该参数的值;双参时实线长度为前值,虚线长度为后值;三参的时候进行轮换。
获取当前虚线样式:context.getLineDash()
设置虚线偏移量:context.lineDashOffset = ?
5.文本(Text)
a.示例
<!DOCTYPE html>
<html>
<head>
<title>Canvas Text Style 示例</title>
<style>
#myCanvas {
display: block;
margin: 0 auto;
border: 1px solid #000;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="600" height="400"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
// 设置文本样式
ctx.font = '20px Arial'; // 字体大小和字体类型
ctx.textAlign = 'center'; // 文本对齐方式
ctx.textBaseline = 'middle'; // 文本基线
ctx.direction = 'ltr'; // 文本方向
// 设置填充和描边样式
ctx.fillStyle = 'blue'; // 填充颜色
ctx.strokeStyle = 'red'; // 描边颜色
// 绘制填充文本
ctx.fillText('Hello Canvas', canvas.width / 2, canvas.height / 2 - 30);
// 绘制描边文本
ctx.strokeText('Hello Canvas', canvas.width / 2, canvas.height / 2 + 30);
</script>
</body>
</html>
b.重点样式详解
(1)textAlign
文本水平对齐的方式,中间这条线是基准线
<!DOCTYPE html>
<html>
<head>
<title>Canvas Text Style 示例</title>
<style>
#myCanvas {
display: block;
margin: 0 auto;
border: 1px solid #000;
margin-top: 200px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="600" height="400"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
ctx.moveTo(300, 0)
ctx.lineTo(300, 400)
ctx.strokeStyle = 'red'
ctx.stroke()
ctx.font = '30px Arial'
// 横坐标开始位对齐
ctx.textAlign = 'start' // 默认值,
ctx.fillText('得意 start', 300, 40)
// 横坐标结束位对齐
ctx.textAlign = 'end' // 结束对齐
ctx.fillText('得意 end', 300, 100)
// 左对齐
ctx.textAlign = 'left' // 左对齐
ctx.fillText('得意 left', 300, 160)
// 右对齐
ctx.textAlign = 'right' // 右对齐
ctx.fillText('得意 right', 300, 220)
// 居中对齐
ctx.textAlign = 'center' // 右对齐
ctx.fillText('得意 center', 300, 280)
</script>
</body>
</html>
(2)textBaseline
文本垂直对齐方式
<!DOCTYPE html>
<html>
<head>
<title>Canvas Text Style 示例</title>
<style>
#myCanvas {
display: block;
margin: 0 auto;
border: 1px solid #000;
margin-top: 200px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="700" height="400"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
ctx.moveTo(0, 200)
ctx.lineTo(700, 200)
ctx.strokeStyle = 'red'
ctx.stroke()
ctx.font = '15px Arial'
ctx.textBaseline = 'alphabetic'
ctx.fillText('吉运 alphabetic', 10, 200)
ctx.textBaseline = 'top'
ctx.fillText('吉运 top', 130, 200)
ctx.textBaseline = 'bottom'
ctx.fillText('吉运 bottom', 200, 200)
ctx.textBaseline = 'middle'
ctx.fillText('吉运 middle', 300, 200)
ctx.textBaseline = 'hanging'
ctx.fillText('吉运 hanging', 400, 200)
ctx.textBaseline = 'ideographic'
ctx.fillText('吉运 ideographic', 500, 200)
</script>
</body>
</html>
(3)direction
确定文字方向
<!DOCTYPE html>
<html>
<head>
<title>Canvas Text Style 示例</title>
<style>
#myCanvas {
display: block;
margin: 0 auto;
border: 1px solid #000;
margin-top: 200px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="400" height="200"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
ctx.moveTo(200, 0)
ctx.lineTo(200, 200)
ctx.strokeStyle = 'hotpink'
ctx.stroke()
ctx.font = '20px Arial';
ctx.fillText('Hello Canvas (ltr)', 20, 50);
ctx.direction = 'rtl'; // 设置文本方向为从右到左
ctx.fillText('Hello Canvas (rtl)', 380, 50);
</script>
</body>
</html>
6.绘制图形
1. 绘制圆角矩形 (roundRect
)
HTML5 Canvas API 原生并不包含roundRect
方法,但我们可以通过扩展CanvasRenderingContext2D
来添加这个方法。
if (!CanvasRenderingContext2D.prototype.roundRect) {
CanvasRenderingContext2D.prototype.roundRect = function(x, y, width, height, radius) {
if (width < 2 * radius) radius = width / 2;
if (height < 2 * radius) radius = height / 2;
this.beginPath();
this.moveTo(x + radius, y);
this.arcTo(x + width, y, x + width, y + height, radius);
this.arcTo(x + width, y + height, x, y + height, radius);
this.arcTo(x, y + height, x, y, radius);
this.arcTo(x, y, x + width, y, radius);
this.closePath();
return this;
}
}
// 使用roundRect
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
ctx.roundRect(10, 10, 100, 100, 20);
ctx.fill();
2. 绘制矩形 (rect
)
var ctx = document.getElementById('myCanvas').getContext('2d');
ctx.beginPath();
ctx.rect(20, 20, 150, 100);// rect(x, y, width, height)
ctx.fillStyle = 'green';
ctx.fill();
3. 绘制椭圆 (ellipse
)
ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, [anticlockwise]);
x
:椭圆中心的x坐标。y
:椭圆中心的y坐标。radiusX
:椭圆的水平半径。radiusY
:椭圆的垂直半径。rotation
:椭圆旋转的角度,以弧度表示。这个参数定义了椭圆的主轴相对于正x轴的顺时针旋转角度。startAngle
:绘制椭圆的起始角,以弧度表示(在椭圆的圆周上,从正x轴顺时针测量)。endAngle
:绘制椭圆的结束角,以弧度表示。endAngle
的值应该大于startAngle
的值,以定义椭圆的绘制范围。anticlockwise
(可选):布尔值,表示绘制方向。如果为true
,则在两个角度之间逆时针绘制椭圆。如果省略或为false
,则顺时针绘制。
var ctx = document.getElementById('myCanvas').getContext('2d');
ctx.beginPath();
ctx.ellipse(100, 100, 50, 75, Math.PI / 4, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill();
4. 绘制圆弧 (arc
)
ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
x
:圆弧中心的x坐标。y
:圆弧中心的y坐标。radius
:圆弧的半径。startAngle
:绘制圆弧的起始角度,以弧度表示。角度的计算是从正x轴(Canvas的水平轴)开始,顺时针方向。endAngle
:绘制圆弧的结束角度,以弧度表示。就像startAngle
,这个值也是以弧度计算,从正x轴开始。anticlockwise
(可选):布尔值,表示绘制方向。如果为true
,则逆时针绘制圆弧。如果省略或为false
,则顺时针绘制圆弧。
var ctx = document.getElementById('myCanvas').getContext('2d');
ctx.beginPath();
ctx.arc(100, 75, 50, 0, Math.PI * 2); // 绘制一个圆
ctx.fillStyle = 'yellow';
ctx.fill();
5. 使用arcTo
绘制圆角路径
ctx.arcTo(x1, y1, x2, y2, radius);
x1, y1
:第一个控制点的坐标,圆弧起点的方向由当前路径的最后点到此点的直线定义。x2, y2
:第二个控制点的坐标,圆弧的终点的方向由此点的坐标定义。实际的圆弧会在x1, y1
和x2, y2
定义的方向之间绘制。radius
:圆弧的半径。如果半径设置得过大以至于无法根据控制点和半径绘制圆弧,那么会导致错误,或者浏览器会自动调整半径大小。
var ctx = document.getElementById('myCanvas').getContext('2d');
ctx.beginPath();
ctx.moveTo(20, 20);
ctx.arcTo(100, 20, 100, 200, 20);
ctx.arcTo(100, 200, 20, 200, 20);
ctx.lineTo(20, 200);
ctx.strokeStyle = 'purple';
ctx.stroke();
6. 绘制贝塞尔曲线 (bezierCurveTo
)
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
cp1x, cp1y
:第一个控制点的坐标,这个点影响曲线起始部分的弯曲方向和程度。cp2x, cp2y
:第二个控制点的坐标,这个点影响曲线末端部分的弯曲方向和程度。x, y
:曲线的终点坐标。
var ctx = document.getElementById('myCanvas').getContext('2d');
ctx.beginPath();
ctx.moveTo(20, 20);
ctx.bezierCurveTo(20, 100, 200, 100, 200, 20);
ctx.strokeStyle = 'pink';
ctx.stroke();
7. 绘制二次贝塞尔曲线 (quadraticCurveTo
)
ctx.beginPath();
ctx.moveTo(20, 20);
ctx.quadraticCurveTo(20, 100, 200, 20);
ctx.strokeStyle = 'orange';
ctx.stroke();
7.图片
渲染图片的方式有两种,一种是在JS里加载图片再渲染,另一种是把DOM里的图片拿到 canvas
里渲染 。这里我将采用第二种方式
<!DOCTYPE html>
<html>
<head>
<title>Canvas示例</title>
<style>
#myCanvas {
display: block;
margin: 0 auto;
border: 1px solid #000;
margin-top: 200px;
}
#img {
display: none;
}
</style>
</head>
<body>
<img src="../duola2.jpg" id="img">
<canvas id="myCanvas" width="400" height="400"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
const image = document.getElementById('img')
ctx.drawImage(image, 30, 30) //从(30px,30px)开始渲染,渲染到画布结束
</script>
</body>
</html>
如果要设置图片宽高,则采用
drawImage(image, dx, dy, dw, dh)
之前代码的 ctx.drawImage(image, 30, 30)修改成ctx.drawImage(image, 30, 30,200,200),图像展示为下方的样子
如果要截取图片,并定义截取部分的尺寸,则采用
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
将对应代码改成:
ctx.drawImage(image, 100, 100, 100, 100, 20, 20, 100, 100)
三.总结
官网的解释更加详细,这边只是语义化更强一点,遇到模棱两可的地方当然还是光顾官网HTML Standard (whatwg.org) ,当然canvas的部分当然不止这一点,之后会慢慢补充的。
原文地址:https://blog.csdn.net/weixin_73810008/article/details/136219845
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!