- 参考资料:《JavaScript高级程序设计(第3版)》 第 15 章 使用 Canvas 绘图
一、canvas基本用法
1.1 兼容性
1.2 基本用法
要使用 <canvas>
元素,必须先设置其 width
和 height
属性,指定可以绘图的区域大小。
出现在开始和结束标签中的内容是后备信息,如果浏览器不支持 <canvas>
元素,就会显示这些信息。
2D
上下文的坐标开始于 <canvas>
元素的左上角,原点坐标是(0,0)。
所有坐标值都基于这个原点计算,x 值越大表示越靠右,y 值越大表示越靠下。
默认情况下,width
和 height
表示水平和垂直两个方向上可用的像素数目。
<canvas width="200" height="300" id="canvas">A drawing of something</canvas>
<script>
// 获取 canvas 元素
const canvas = document.getElementById('canvas')
// 检测浏览器是否支持canvas元素
if (canvas.getContext) {
const ctx = canvas.getContext('2d')
const canvasWidth = canvas.width
const canvasHeight = canvas.height
console.log(ctx)
console.log(canvasWidth, canvasHeight) // 200 300
}
</script>
ctx
:
二、2D 上下文
2.1 填充和描边
2D
上下文的两种基本绘图操作是填充和描边。
- 填充 - 属性
fillStyle
:用指定的样式(颜色、渐变或图像)填充图形; - 描边 - 属性
strokeStyle
:只在图形的边缘画线。
这两个属性的值可以是字符串、渐变对象或模式对象,而且它们的默认值都是” #000000
“。如果为
它们指定表示颜色的字符串值,可以使用 CSS
中指定颜色值的任何格式,包括颜色名、十六进制码、rgb
、rgba
、hsl
或 hsla
。
var drawing = document.getElementById("drawing");
//确定浏览器支持<canvas>元素
if (drawing.getContext){
var context = drawing.getContext("2d");
context.strokeStyle = "red";
context.fillStyle = "#0000ff";
}
所有涉及描边和填充的操作都将使用这两个样式,直至重新设置这两个值。
2.2 绘制矩形
矩形是唯一一种可以直接在 2D 上下文中绘制的形状。与矩形有关的方法包括三个方法:
fillRect
(矩形的 x 坐标,矩形的 y 坐标,矩形宽度,矩形高度)strokeRect
(矩形的 x 坐标,矩形的 y 坐标,矩形宽度,矩形高度)-
clearRect
(矩形的 x 坐标,矩形的 y 坐标,矩形宽度,矩形高度) - demo
<canvas width="200" height="300" id="canvas">A drawing of something</canvas>
<script>
// 获取 canvas 元素
const canvas = document.getElementById('canvas')
// 检测浏览器是否支持canvas元素
if (canvas.getContext) {
const ctx = canvas.getContext('2d')
// 绘制红色矩形
// 通过 fillStyle 属性填充颜色
ctx.fillStyle = `#ff0000`
ctx.fillRect(10, 10, 50, 50)
// 绘制半透明的蓝色矩形
// 修改填充色
ctx.fillStyle = `rgba(0, 0, 255, 0.5)`
ctx.fillRect(30, 30, 50, 50)
// 在两个矩形重得的地方清除一个小矩形
ctx.clearRect(40, 40, 10, 10)
// 绘制红色描边矩形
ctx.strokeStyle = `#ff0000`
ctx.strokeRect(60, 60, 50, 50)
// 绘制半透明的蓝色描边矩形
ctx.strokeStyle = `rgba(0, 0, 255, 0.5)`
ctx.strokeRect(80, 80, 50, 50)
}
</script>
效果图如下:
补充:
lineWidth
:描边线条的宽度由lineWidth
属性控制,该属性的值可以是任意整数lineCap
:通过lineCap
属性可以控制线条末端的形状是平头、圆头还是方头(”butt
“、”round
“或”square
“)lineJoin
:通过lineJoin
属性可以控制线条相交的方式是圆交、斜交还是斜接(”round
“、”bevel
“或”miter
“)。
2.3 绘制路径
通过路径可以创造出复杂的形状和线条。
要绘制路径,首先必须调用 beginPath()
方法,表示要开始绘制新路径。
然后,再通过调用下列方法来实际地绘制路径。
arc(x, y, radius, startAngle, endAngle, counterclockwise)
:以(x,y)
为圆心绘 制一条弧线,弧线半径为 radius,起始和结束角度(用弧度表示)分别为startAngle
和endAngle
。最后一个参数表示startAngle
和endAngle
是否按逆时针方向计算,值为false
表示按顺时针方向计算。arcTo(x1, y1, x2, y2, radius)
:从上一点开始绘制一条弧线,到(x2,y2)为止,并且以 给定的半径 radius 穿过(x1,y1)。bezierCurveTo(c1x, c1y, c2x, c2y, x, y)
:从上一点开始绘制一条曲线,到(x,y)为 止,并且以(c1x,c1y)
和(c2x,c2y)
为控制点。lineTo(x, y)
:从上一点开始绘制一条直线,到(x,y)
为止。moveTo(x, y)
:将绘图游标移动到(x,y),不画线。quadraticCurveTo(cx, cy, x, y)
:从上一点开始绘制一条二次曲线,到(x,y)为止,并且以(cx,cy)作为控制点。rect(x, y, width, height)
:从点(x,y)
开始绘制一个矩形,宽度和高度分别由width
和height
指定。这个方法绘制的是矩形路径,而不是strokeRect()
和fillRect()
所绘制的独立的形状。
创建了路径后,接下来有几种可能的选择。
- 如果想绘制一条连接到路径起点的线条,可以调用
closePath()
。 - 如果路径已经完成,你想用
fillStyle
填充它,可以调用fill()
方法。 - 另外,还可以调用
stroke()
方法对路径描边,描边使用的是strokeStyle
。 - 最后还可以调用
clip()
,这个方法可以在路径上创建一个剪切区域。
在 2D 绘图上下文中,路径是一种主要的绘图方式,因为路径能为要绘制的图形提供更多控制。由
于路径的使用很频繁,所以就有了一个名为 isPointInPath()
的方法。这个方法接收 x 和 y 坐标作为
参数,用于在路径被关闭之前确定画布上的某一点是否位于路径上
if (context.isPointInPath(100, 100)){
alert("Point (100, 100) is in the path.");
}
一直弄不懂,画时钟时,画内外圆的
moveTo
究竟是怎么样移动的,现在终于弄懂了,请看下图:
画时针和分针的时候,不同角度对
x
和y
都是有要求的:(x,y)的限制就是内圆;同时分针必须比时针要长
只有满足了下图的公式,才能保证所画的时针和分针在内圆内!
<canvas width="200" height="300" id="canvas">A drawing of something</canvas>
<script>
// 获取 canvas 元素
const canvas = document.getElementById('canvas')
// 检测浏览器是否支持canvas元素
if (canvas.getContext) {
const ctx = canvas.getContext('2d')
// 第一步:开始路径
ctx.beginPath()
// 绘制外圆
ctx.arc(100, 150, 80, 0, 2 * Math.PI, false)
// 移动绘图游标
ctx.moveTo(170, 150)
// 绘制内圆
ctx.arc(100, 150, 70, 0, 2 * Math.PI, false)
// 移动绘图游标
ctx.moveTo(100, 150)
// 分针
ctx.lineTo(100, 90)
// 移动绘图游标
ctx.moveTo(100, 150)
// 时针
ctx.lineTo(80, 120)
// 调用 stroke()方法才能把图形绘制到画布上
ctx.stroke()
}
</script>
2.4 绘制文本
绘制文本主要有两个方法:
fillText
(要绘制的文本字符串, x 坐标, y 坐标, 可选的最大像素宽度):使用fillStyle
属性绘制文本strokeText
(要绘制的文本字符串, x 坐标, y 坐标, 可选的最大像素宽度):使用strokeStyle
属性为文本描边
而且,这两个方法都以下列 3 个属性为基础。
font
:表示文本样式、大小及字体,用 CSS 中指定字体的格式来指定,例如”10px Arial”。textAlign
:表示文本对齐方式。可能的值有”start”、”end”、”left”、”right”和”center”。 建议使用”start”和”end”,不要使用”left”和”right”,因为前两者的意思更稳妥,能同时 适合从左到右和从右到左显示(阅读)的语言。-
textBaseline
:表示文本的基线。可能的值有”top
“、”hanging
“、”middle
“、”alphabetic
“、”ideographic
“和”bottom
“。 - demo4
<canvas width="200" height="300" id="canvas">A drawing of something</canvas>
<script>
// 获取 canvas 元素
const canvas = document.getElementById('canvas')
// 检测浏览器是否支持canvas元素
if (canvas.getContext) {
const ctx = canvas.getContext('2d')
// 第一步:开始路径
ctx.beginPath()
// 绘制外圆
ctx.arc(100, 150, 80, 0, 2 * Math.PI, false)
// 移动绘图游标
ctx.moveTo(170, 150)
// 绘制内圆
ctx.arc(100, 150, 70, 0, 2 * Math.PI, false)
// 移动绘图游标
ctx.moveTo(100, 150)
// 分针
ctx.lineTo(100, 100)
// 移动绘图游标
ctx.moveTo(100, 150)
// 时针
ctx.lineTo(80, 120)
// 绘制文本
ctx.strokeStyle = 'red'
ctx.textAlign = 'center'
ctx.strokeText('12', 100, 98)
ctx.textAlign = 'start'
ctx.strokeText('12', 100, 115)
ctx.textAlign = 'end'
ctx.strokeText('12', 100, 125)
// 调用 stroke()方法才能把图形绘制到画布上
ctx.strokeStyle = 'black'
ctx.stroke()
}
</script>
效果图如下:
measureText(要绘制的文本)
:
可把文本控制在某一区域中。返回一个 TextMetrics
对象。返回的对象目前只有一个 width
属性,但将来还会增加更多度量属性。
measureText()
方法利用 font
、textAlign
和 textBaseline
的当前值计算指定文本的大小。
比如,假设你想在一个 140 像素宽的矩形区域中绘制文本 Hello world!
,下面的代码从 100 像素的字体大小开始递减,最终会找到合适的字体大小。
<canvas width="200" height="300" id="canvas">A drawing of something</canvas>
<script>
// 获取 canvas 元素
const canvas = document.getElementById('canvas')
// 检测浏览器是否支持canvas元素
if (canvas.getContext) {
const ctx = canvas.getContext('2d')
// 第一步:开始路径
ctx.beginPath()
let fontSize = 100;
ctx.font = fontSize + "px Arial";
while(ctx.measureText("Hello world!").width > 140){
fontSize--;
ctx.font = fontSize + "px Arial";
}
console.log(fontSize) // 26
ctx.fillText("Hello world!", 30, 30);
ctx.fillText("Font size is " + fontSize + "px", 5, 80);
}
</script>
前面提到过,fillText 和 strokeText()方法都可以接收第四个参数, 也就是文本的最大像素宽度。不过,这个可选的参数尚未得到所有浏览器支持 (最早支持它的是 Firefox 4)。提供这个参数后,调用 fillText()或 strokeText()时如果传入的字符串大于最大宽度,则绘制的文本字符的高度 正确,但宽度会收缩以适应最大宽度。