canvas
canvas元素简介
1.是个container元素<canvas width='100' height='100'></canvas>
,有开闭标签
2.有且只有width
和height
两个attribute
,不需要写单位
canvas的基本使用
const canvasEl = document.getElementById('canvas01')
const ctx = canvasEl.getContext('2d') //ctx包含了各种属性和画图的函数
ctx.fillStyle='#d55'
ctx.fillRect(200,20,200,50) //填充矩形
ctx.strokeStyle='#55d'
ctx.lineWidth=10
ctx.strokeRect(100,60,-20,-50) //描边矩形
ctx.lineWidth=2
ctx.strokeStyle='#5dd'
ctx.strokeRect(150,100,-20,-50)
canvas路径绘制
画多线段
ctx.beginPath()
ctx.moveTo(100,100)//用moveTo将画笔移动到下一个开始绘制的点,和之前绘制的尾部不连接
ctx.lineCap='round'
ctx.lineJoin='bevel'
ctx.lineWidth=5
ctx.lineTo(150,100)
ctx.lineTo(200,120)
ctx.moveTo(220,100)
ctx.lineTo(220,150)
ctx.stroke()
ctx.closePath()
线条的属性:
1.
ctx.lineCap
:线条端点样式,可以设置'round'|'butt'|'square'
圆角|截断|方角
2.
ctx.lineJoin
: 线条连接处样式'round'|'bevel'|'miter'
圆角|倒角|默认
画三角形描边(stroke):需要用到ctx.closePath()
ctx.beginPath()
ctx.moveTo(200,200)
ctx.lineTo(200,250)
ctx.lineTo(300,100)
ctx.stroke()
ctx.closePath()
画三角填充(fill):可以没有ctx.closePath()
ctx.beginPath()
ctx.moveTo(200,200)
ctx.lineTo(200,250)
ctx.lineTo(300,100)
ctx.fill()
ctx.closePath()
画弧
CanvasPath.arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise?: boolean)
ctx.beginPath()
ctx.arc(300,300,20,0,Math.PI*2,false)
ctx.moveTo(370,350)
ctx.arc(350,350,20,0,Math.PI,false)
ctx.stroke()
ctx.closePath()
画矩形
ctx.beginPath()
ctx.fillStyle='#e00'
ctx.rect(20,300,20,50)
ctx.fill()
ctx.closePath()
ctx.beginPath()
ctx.lineStyle='#e00'
ctx.lineWidth=3
ctx.rect(20,400,50,80)
ctx.stroke()
ctx.closePath()
canvas绘制文字
ctx.font = "50px 仿宋"
ctx.textAlign = 'center'
ctx.textBaseline = 'bottom'
ctx.fillStyle = '#d00'
ctx.fillText("哈哈哈", 250, 90,200);
ctx.font = "20px 黑体"
ctx.strokeStyle = '#d000dd'
ctx.strokeText("嘿嘿嘿", 250, 200,200);
canvas绘制图片
1.ctx.drawImage(image, dx, dy)
2.ctx.drawImage(image, dx, dy, dWidth, dHeight)
3.ctx.drawImage(image,sx,sy,swidth,sheight,dx,dy,dwidth,dheight)
s开头的是裁剪坐标,d开头的是绘制坐标
示例:
const dogImage = new Image(200,200)
dogImage.src = './dog.jpg'
dogImage.onload = function(){
ctx.drawImage(dogImage,10,10,300,300,0,0,100,100)
}
补充:
canvas绘制的图都是栅格图
canvas绘制状态
保存绘图状态,在进行复杂的绘图以后,可以随时返回当前的状态,主要是保存某个绘图时期的字体,颜色,线条宽度,坐标变换等状态,这样不用重复设置绘图样式,被保存的绘图状态会被推入一个栈中,每次调用restore()就会弹出到栈最顶层的状态,也就是让canvas的各类属性设置回到上一次保存的设置
ctx.save()
ctx.restore()
ctx.fillRect(0,0,100,100)
ctx.save()
ctx.translate(10,150)
ctx.fillRect(0,0,100,100)
ctx.restore()
canvas变形
1.ctx.translate(dx,dy)
平移画布
2.ctx.rotate(deg)
旋转画布
3.ctx.scale(sx,sy)
缩放画布,只能在原坐标系统缩放
ctx.save()
ctx.beginPath()
ctx.translate(100,150)
ctx.scale(2, 2)
ctx.rotate(45)
ctx.fillRect(-50,-100,100,100)
ctx.restore()
canvas动画
1.通过setInterval
,setTimeout
和requestAnimationFrame
三种方法来控制时间
2.画一帧的步骤
1.ctx.clearRect()
2.ctx.save()
3.绘制动画图形
4.ctx.resotre()
示例:绘制表盘
<!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>
.container{
position: relative;
width: 500px;
height: 500px;
border-radius: 5px;
margin: 50px auto;
box-shadow: 1px 1px 10px 5px #d5d5d5;
}
canvas{
position: absolute;
top: 0;
left: 0;
}
</style>
</head>
<body>
<div class="container">
<canvas id="canvas01" width="500" height="500"></canvas>
</div>
<script>
const canvasEl = document.getElementById('canvas01')
const ctx = canvasEl.getContext('2d')
function drawClock(pHours,pMinutes,pSeconds){
let oldHours = pHours <= 12?(pHours/12)*60-15:((pHours-12)/12)*60-15
let minutes = pMinutes - 15
let seconds = pSeconds -15
let hours = oldHours+Math.round((pMinutes/12))
ctx.clearRect(0,0,500,500)
ctx.save()
//表盘
ctx.beginPath()
ctx.arc(250,250,105,0,Math.PI*2,false)
ctx.fillStyle='#fff'
ctx.fill()
ctx.closePath()
//表外框线条
ctx.beginPath()
ctx.strokeStyle= '#000'
ctx.lineWidth = 5
ctx.moveTo(356,250)
ctx.arc(250,250,106,0,Math.PI*2,false)
ctx.stroke()
ctx.closePath()
//表内框线
ctx.beginPath()
ctx.strokeStyle= '#000'
ctx.lineWidth = 1
ctx.moveTo(351,250)
ctx.arc(250,250,101,0,Math.PI*2,false)
ctx.stroke()
ctx.closePath()
//刻度线
ctx.beginPath()
ctx.lineWidth=1
for(let j = 0;j<60;j++ ){
ctx.save()
ctx.translate(250,250)
ctx.rotate(Math.PI*2/60*j)
if(j%5===0){
ctx.moveTo(95,0)
ctx.lineTo(100,0)
}
else if(j%15===0){
ctx.moveTo(90,0)
ctx.lineTo(100,0)
}else{
ctx.moveTo(98,0)
ctx.lineTo(100,0)
}
ctx.restore()
}
ctx.stroke()
ctx.closePath()
//刻度数字
const numbers = [3,4,5,6,7,8,9,10,11,0,1,2]
ctx.save()
ctx.translate(247,254)
for(let i = 0; i<numbers.length;i++){
let x = Math.cos(Math.PI*2/12*i)*83
let y = Math.sin(Math.PI*2/12*i)*83
ctx.fillStyle='#000'
ctx.textAlign = 'left'
ctx.fillText(numbers[i],x,y)
}
ctx.restore()
//秒针
ctx.beginPath()
ctx.strokeStyle= '#d33'
ctx.lineCap = 'round'
ctx.lineWidth = 1
ctx.moveTo(250,250)
ctx.arc(250,250,96,Math.PI*2/60*seconds,Math.PI*2/60*seconds,true)
ctx.stroke()
ctx.closePath()
//分针
ctx.beginPath()
ctx.strokeStyle= '#111'
ctx.lineCap = 'round'
ctx.lineWidth = 2
ctx.moveTo(250,250)
ctx.arc(250,250,80,Math.PI*2/60*minutes,Math.PI*2/60*minutes,true)
ctx.stroke()
ctx.closePath()
//时针
ctx.beginPath()
ctx.strokeStyle= '#222'
ctx.lineCap = 'round'
ctx.lineWidth = 4
ctx.moveTo(250,250)
ctx.arc(250,250,50,Math.PI*2/60*hours,Math.PI*2/60*hours,true)
ctx.stroke()
ctx.closePath()
//表心
ctx.beginPath()
ctx.moveTo(250,250)
ctx.fillStyle= '#fff'
ctx.arc(250,250,1,0,Math.PI*2)
ctx.fill()
ctx.closePath()
}
let lastSecond = -1;
function playAnimation(){
const date = new Date()
const seconds = date.getSeconds()
const minutes = date.getMinutes()
const hours = date.getHours()
if(lastSecond !== seconds){
drawClock(hours,minutes,seconds)
lastSecond = seconds
}
requestAnimationFrame(()=>{
playAnimation()
})
}
playAnimation()
</script>
</body>
</html>