canvas组件
1.了解canvas
1.1 canvas就是在HTML上可供js实现绘图的区域,通过标签来在HTML定义
1.2 画布具体的大小需要直接用width和heigh来定义,并且不需要带单位
<canves id='canvas' width='600' heigt='600'>
// ie9 以下不兼容,将不支持的提示语写在标签内部
浏览器不支持canvas,请更换浏览器
</canves>
1.3 canvas本质就是一张空白的图片,只不过内容需要我们自己画上去
2.初体验canvas
2.1 想要使用canvas进行绘制,需要先获取到canvas工具箱
const canvas = document.querySelector('#canvas')
// 获取工具箱,选择2d模式
const ctx = canvas.getContext('2d')
2.2 绘画基本逻辑:通过api和xy坐标实现绘制,比如要在画布上画一条线,看图

// 以线的形式移动到位置,如果只执行这一步是看不到线的,他仅仅是移动的轨迹,并没有画上去
ctx.lineTo(600, 100)
// 设置线宽,不需要单位,如果不写线的颜色默认是黑色,1px宽,会产生绘制线颜色和宽度的问题
ctx.lineWidth = 2
// 对走过的轨迹描边,此时才会对走过的轨迹进行绘制,才能在页面上看到
ctx.stroke() // 对于线叫描边,对于面叫填充
2.3 绘制线颜色和宽度的问题
出现该问题是因为canvas的绘制机制,即canvas绘制的线是通过中心对齐的方式对齐坐标,所以当线宽只有1px是从坐标的x.5到x.5进行绘制,但是浏览器会 把1px以下的线当1px显示,所以canvas绘制1px线实际为2px,对应颜色也被其拉伸。
提示:所以当对绘制图像有要求就应该以2px及以上绘制
3.重新绘制轨迹(绘制三条平行线)
// 绘制第一条
ctx.moveTo(100, 100)
ctx.lineTo(300, 100)
ctx.lineWidth = 10
// 设置线的颜色
ctx.strokeStyle = 'red'
ctx.stroke()
// 绘制第二条
// ctx.beginPath() 绘制一条新轨迹
ctx.moveTo(100, 150)
ctx.lineTo(300, 150)
ctx.lineWidth = 6
ctx.strokeStyle = 'yellow'
ctx.stroke()
// 绘制第三条
// ctx.beginPath()
ctx.moveTo(100, 200)
ctx.lineTo(300, 200)
ctx.lineWidth = 2
ctx.strokeStyle = 'blue'
ctx.stroke()
执行上述代码后会发现会是以下效果:
红色的线被绘制了一次,黄色的线绘制了两次,蓝色的线绘制了三次,并且每次绘制都会在上次的线上再绘制一次,这显然不是我们想要的。出现这个原因是 因为我们每次绘制的笔并没有断掉或者说重新开始,三次绘制其实都是一次绘制的结果。要想实现三条互不相干的线还要重新开始笔迹。解开代码中注释内容 ctx.beginPath()即可
4.自动闭合和填充(绘制三角形)
ctx.moveTo(100, 100)
ctx.lineTo(300, 100)
ctx.lineTo(250, 200)
ctx.lineWidth = 8
ctx.strokeStyle = 'black'
// ctx.closePath() 自动闭合
ctx.stroke()
4.1 执行上述代码后会发现会是以下效果:此时需要对缺角部分进行闭合,解开注释代码 ctx.closePath() 即可
4.2 或者可以通过填充实现
ctx.moveTo(100, 100)
ctx.lineTo(300, 100)
ctx.lineTo(250, 200)
// 填充方式必须返回起点
ctx.lineTo(100, 100)
ctx.lineWidth = 8
ctx.strokeStyle = 'black'
// 填充三条线形成的区域
ctx.fill()
5.非零填充规则(绘制回型填充)
5.1 绘制回型区域
// 第一个正方形顺时针
ctx.moveTo(x - 200, y - 200)
ctx.lineTo(x + 200, y - 200)
ctx.lineTo(x + 200, y + 200)
ctx.lineTo(x - 200, y + 200)
ctx.lineWidth = 4
// 第二个正方形逆时针
ctx.moveTo(x - 100, y - 100)
ctx.lineTo(x - 100, y + 100)
ctx.lineTo(x + 100, y + 100)
ctx.lineTo(x + 100, y - 100)
// 第三个正方形顺时针
ctx.moveTo(x + 50, y + 50)
ctx.lineTo(x + 150, y + 50)
ctx.lineTo(x + 150, y + 150)
ctx.lineTo(x + 50, y + 150)
ctx.fill()
5.2 在浏览器上执行5.1代码后可以看到效果,那为什么会这样填充,为什么需要区分顺逆方向呢?其实填充区域是有对应规范的,这就是非零填充原则,具体原理如图;
6. 绘制线样式
6.1 线段两端的样式
ctx.lineCap = 'butt' // 默认两端没有样式
ctx.lineCap = 'round' // 默认两端为圆形
ctx.lineCap = 'square' // 默认两端为方形
6.2 线段拐点样式(线段拐弯时候的样式)
ctx.lineJoin = 'miter' // 默认跟随线段样式
ctx.lineJoin = 'round' // 圆角拐点样式
ctx.lineJoin = 'bevel' // 平角拐点样式
6.3 绘制虚线
// 绘制2个像素实线,3个像素空白,循环往复直至线段结束
ctx.setLineDash([2,3])
// 绘制2个像素实线,3个像素空白,4个像素实线,5个像素空白,6个像素实线,2个像素空白... 循环往复直至线段结束
ctx.setLineDash([2,3,4,5,6])
6.4 绘制渐变线
canvas本身具有的绘制渐变色只针对面,对于线无法操作(最新版本已经支持),所以需要换个思路来绘制线。
思路:将要画的线分成多个1像素线组合起来,通过rgb颜色绘制每1像素长度
// 绘制条长度300的由红色渐变到蓝色的线,将其分成300个1px线组成的线
for (let i = 0; i <= 300; i++) {
ctx.beginPath()
ctx.moveTo(100 + i, 100)
ctx.lineTo(100 + i + 1, 100)
ctx.lineWidth = 10
// 红色是(255,0,0) 蓝色是(0,0,255)
// https://www.zxgj.cn/g/yansezhi 一个免费的在线查颜色工具
ctx.strokeStyle = `rgb(${255 - i},0,${i})`
ctx.stroke()
}
效果图:
7.折线图的绘制(案例1)
折线图的使用es6Class进行书写,此处只是个简易的模型,如需要更多自定义样式需要自己再封装下;比如坐标轴带刻度,点样式修改,显示点参数,颜色…
const canvas = document.querySelector('#canvas')
// 绘制参数
const config = {
axis: {
x: 20,
y: 20,
width: 2,
color: 'black'
},
points: [
{ x: 100, y: 100 },
{ x: 200, y: 150 },
{ x: 300, y: 100 },
{ x: 400, y: 200 },
{ x: 500, y: 170 }
]
}
class LineChart {
constructor(el, config) {
this.init(el)
this.setAxis(this.ctx, config.axis)
this.drawPoint(this.ctx, this.zero, config.points)
}
init(el) {
this.ctx = el.getContext('2d')
this.width = this.ctx.canvas.width
this.height = this.ctx.canvas.height
}
// 绘制坐标轴
setAxis(ctx, axis) {
const zeroX = axis.x
const zeroY = this.height - axis.y
this.zero = {
zeroX,
zeroY
}
ctx.moveTo(zeroX, zeroY)
ctx.lineTo(this.width - zeroX, zeroY)
ctx.moveTo(zeroX, zeroY)
ctx.lineTo(zeroX, axis.y)
ctx.lineCap = 'round'
ctx.lineWidth = axis.width
ctx.strokeStyle = axis.color
ctx.stroke()
ctx.beginPath()
ctx.moveTo(this.width - zeroX, zeroY)
ctx.lineTo(this.width - zeroX - 10, zeroY + 5)
ctx.lineTo(this.width - zeroX - 10, zeroY - 5)
ctx.closePath()
ctx.fill()
ctx.beginPath()
ctx.moveTo(axis.x, axis.y)
ctx.lineTo(axis.x + 5, axis.y + 10)
ctx.lineTo(axis.x - 5, axis.y + 10)
ctx.closePath()
ctx.fill()
}
// 绘制折现
drawPoint(ctx, zero, points) {
points.forEach(p => {
ctx.beginPath()
ctx.moveTo(p.x - 5, p.y - 5)
ctx.lineTo(p.x + 5, p.y - 5)
ctx.lineTo(p.x + 5, p.y + 5)
ctx.lineTo(p.x - 5, p.y + 5)
ctx.closePath()
ctx.fill()
});
console.log(zero)
ctx.beginPath()
ctx.moveTo(zero.zeroX, zero.zeroY)
points.forEach(p => {
console.log(p)
ctx.lineTo(p.x, p.y)
})
ctx.stroke()
}
}
const temp = new LineChart(canvas, config)
8.矩形的绘制
8.1 矩形
// x,y为原点坐标,width,height是矩形宽高
// 方法一:划线描边填充
ctx.rect(100, 100, 200, 200)
ctx.stroke()
ctx.fill()
// 方法二:直接描边矩形
ctx.strokeRect(100, 100, 200, 200)
// 方法三:直接填充矩形
ctx.fillRect(100, 100, 200, 200)
// 清楚矩形区域
ctx.clearRect(100, 100,50,50)
8.2 矩形渐变(渐变方案)
// 获取渐变方案 (起始x,起始y,终点x,终点y)
const linearGradient = ctx.createLinearGradient(100, 100, 400, 100)
// 设置渐变方案 起始颜色为red从0%处开始渐变
linearGradient.addColorStop(0, 'red')
// 设置渐变方案 结束颜色为skyblue从100%处开始完成
linearGradient.addColorStop(1, 'skyblue')
// 将渐变方案赋值给填充样式
ctx.fillStyle = linearGradient
ctx.fillRect(100, 200, 300, 100)
// 将渐变方案赋值给描边(可以看出当前也已经支持线描边了)
ctx.moveTo(100, 400)
ctx.lineTo(400, 400)
ctx.lineWidth = 10
ctx.strokeStyle = linearGradient
ctx.stroke()
9.曲线绘制
9.1 圆弧
// 圆心
const circleX = 100
const circleY = 100
// 半径
const radius = 50
// 开始弧度
const startArc = 0
// 结束弧度 计算公式 a=π/180*n n为圆心角
const endArc = Math.PI / 180 * 60
// 绘制方向
const direction = false
ctx.arc(circleX, circleY, radius, startArc, endArc, direction)
ctx.stroke()
// 如果需要填充扇形区域
ctx.moveTo(circleX, circleY)
ctx.closePath()
ctx.arc(circleX, circleY, radius, startArc, endArc, direction)
ctx.fill()
9.2 饼图
const data = [80, 65, 98, 50, 60, 90, 55, 32]
const total = 80 + 65 + 98 + 50 + 60 + 90 + 55 + 32
let startArc = 0
data.forEach(t => {
ctx.beginPath()
ctx.moveTo(circleX, circleY)
const endArc = startArc + Math.PI * 2 * (t / total)
ctx.arc(300, 300, 100, startArc, endArc, false)
ctx.fillStyle = randomColor()
startArc = endArc
ctx.fill()
})
// 随机颜色
function randomColor() {
const r = Math.floor(Math.random() * 256)
const g = Math.floor(Math.random() * 256)
const b = Math.floor(Math.random() * 256)
return `rgb(${r},${g},${b})`
}
10.绘制文本
效果图:
// 显示文本
const text = 'i am so niu bi'
// 字体直接写电脑里的字体名字
ctx.font = '30px 楷书'
// 可选 bottom/top/middle
ctx.textBaseline = 'bottom'
// 对齐方式 left/middle/right
ctx.textAlign = 'left'
// 字体颜色也接受渐变方案
const liner = ctx.createLinearGradient(100, 100, 200, 100)
liner.addColorStop(0, 'red')
liner.addColorStop(1, 'blue')
ctx.fillStyle = liner
// ctx.strokeText(text, 100, 100)
ctx.fillText(text, 100, 100)
11.绘制图片
11.1 canvas也可以将js创建的img或者html创建的img元素内容绘制到canvas画布上,以创建元素举例
// 创建图形元素
const img = new Image()
img.src = 'meinv.jpeg'
// 由于img的src是一个异步状态,此处使用回调来绘制
img.onload = function () {
// drawImage一共9个参数
// 前五个分别是 图片源,绘制起点xy坐标,绘制图片缩放width/height
// 后四个分别是截图用,截图起点xy坐标,截图宽高width/height
ctx.drawImage(img, 100, 100, 300, 300, 100, 100, 100, 100)
}
END
就这些了,canvas还有其他功能比如状态,变形,合成,遮罩,和动画本文为涉及,如有需要请参考 做人要厚道2013 的这篇文章 https://blog.csdn.net/u012468376/article/details/73350998 里边写的很详细详细详细!!!