Canvas前端绘图工具,Canvas API 提供了一个通过JavaScript 和 HTML的<canvas>元素来绘制图形的方式。它可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。
<canvas id="canvas"></canvas>
标签属性:
获取画笔:
const ctx = canvas.getContext('2d');
API:
线条相关:
绘制矩形:
路径
绘制路径
填充和描边
渐变
返回的CanvasGradient对象的addColorStop(offset, color)添加渐变颜色
绘制文本
文本样式
阴影
变换
合成
绘制图像
像素控制
canvas状态
下面实践下看看:
签名
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<canvas id="canvas" width="400" height="400"/>
</body>
<script type="text/javascript">
canvas.style.background="pink" //设置背景颜色为粉色
let ctx = canvas.getContext('2d') //获取画笔
ctx.save() //保存当前的画笔状态
ctx.strokeStyle="blue" //设置画笔颜色为蓝色
ctx.lineWidth="2" //线条的宽度为2
ctx.beginPath() //开始一个新的路径
ctx.strokeRect(100,100,100,100) //绘制矩形100x100
ctx.restore() //恢复画笔,此时画笔为黑色了
ctx.moveTo(100,100) //画笔移至坐标(100,100)
ctx.arc(200,200,100,0,Math.PI*2/3,true) //绘制圆弧,原心坐标(200,200),半径100,起始弧度为0,结束弧度为2/3
ctx.stroke() //画弧
//随鼠标按下有绘制痕迹
canvas.onmousedown = function(e){
ctx.save() //保存画笔状态
ctx.strokeStyle="green" //配置画笔颜色为绿色
ctx.beginPath() //开始新的路径
//使用鼠标相对浏览器的x,y减去画布距离浏览器的x,y获取到在画布里鼠标的坐标。
ctx.moveTo(e.clientX-canvas.offsetLeft,e.clientLeft-canvas.offsetTop)//移动到鼠标按下的坐标处
console.log(canvas.offsetLeft,canvas.offsetTop);
//鼠标移动时候画线到鼠标所到位置
document.onmousemove=function(e){
ctx.lineTo(e.clientX-canvas.offsetLeft,e.clientY-canvas.offsetTop)
ctx.stroke() //画图
ctx.restore() //恢复画笔状态
}
document.onmouseup=function(){
document.onmousemove = document.onmouseup=null //画完结束恢复鼠标移动的鼠标事件
}
return
}
</script>
</html>
旋转放大缩小:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<canvas id="canvas" width="300" height="300"></canvas>
</body>
<script type="text/javascript">
let flag=0
let scale = 0 //放大的比例
let flagScale=0
canvas.style.background="pink"
let ctx = canvas.getContext("2d")
ctx.save()
ctx.translate(150,150)//画笔平移到坐标(150,150)
ctx.beginPath() //开始新的路径
ctx.fillRect(-50,-50,100,100) //以(150,150为矩形中心画矩形)
ctx.restore()
//设置计时器自动调整放大和缩小
setInterval(()=>{
flag++
ctx.clearRect(0,0,canvas.width,canvas.height) //清除画布
ctx.save()
ctx.translate(150,150)
ctx.rotate(flag*Math.PI/180) //旋转一定的角度
//scale最大为100,超过100那么开始缩小,到0开始增大
if(scale==100){
flagScale=-1
}else if(scale==0){
flagScale=1
}
scale+=flagScale
ctx.scale(scale/50,scale/50)
ctx.beginPath()
ctx.fillRect(-50,-50,100,100)
ctx.restore()
},1000/60)
</script>
</html>
绘制钟表:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<canvas id="canvas" width="400" height="400" />
</body>
<script type="text/javascript">
canvas.style.backgroundColor = "pink"
let ctx = canvas.getContext("2d")
// setInterval(()=>{
// ctx.clearRect(0,0,canvas.width,canvas.height)//每秒清除画布
// move() //绘制钟表
// },1000)
move() //初始化钟表
function move(){
ctx.save()
ctx.lineWidth = "8"
ctx.strokeStyle = "black"
ctx.lineCap = "round"
ctx.translate(200, 200)
ctx.rotate(-90 * Math.PI / 180) //逆时针旋转90度,修改了坐标的方向右上为正
ctx.beginPath()
ctx.save()
ctx.strokeStyle = "blue"
ctx.lineWidth = "14"
ctx.beginPath()
ctx.arc(0, 0, 140, 0, 360 * Math.PI / 180)//画圆弧,最外层的圆半径为140
ctx.stroke()
ctx.restore()
//时针刻度
ctx.save()
for (let i = 0; i < 12; i++) {
ctx.rotate(30 * Math.PI / 180) //将圆盘分割成12份为时针的刻度
ctx.beginPath()
ctx.moveTo(100, 0) //距离圆环40处
ctx.lineTo(120, 0) //画长度20
ctx.stroke()
}
ctx.restore()
// 分针刻度
ctx.save()
ctx.lineWidth = "4"
for (let i = 0; i < 60; i++) { //将圆盘分割成60份为分针的刻度
if (i % 5 != 0) { //避开5的倍数绘制,因为已经存在了时针的刻度
ctx.beginPath()
ctx.moveTo(117, 0)
ctx.lineTo(120, 0)
ctx.stroke()
}
ctx.rotate(6 * Math.PI / 180) //每次旋转6度
}
ctx.restore()
//获取当前时间以便绘制指针的位置
let seconds = new Date().getSeconds()
let minutes = new Date().getMinutes() + seconds / 60
let hour = new Date().getHours() + minutes / 60
hour = hour > 12 ? hour - 12 : hour
//时针
ctx.save()
ctx.lineWidth = 14
ctx.rotate(hour * 30 * Math.PI / 180)
ctx.beginPath()
ctx.moveTo(-20, 0)
ctx.lineTo(80, 0)
ctx.stroke()
ctx.restore()
//分针
ctx.save()
ctx.lineWidth=10
ctx.rotate(minutes * 6 * Math.PI / 180)
ctx.beginPath()
ctx.moveTo(-28,0)
ctx.lineTo(112,0)
ctx.stroke()
ctx.restore()
//秒针
ctx.save()
ctx.strokeStyle="#D40000"
ctx.rotate(seconds * 6 * Math.PI / 180)
ctx.lineWidth=6
ctx.beginPath()
ctx.moveTo(-30,0)
ctx.lineTo(83,0)
ctx.stroke()
ctx.restore()
ctx.save()
ctx.fillStyle="#D40000"
ctx.beginPath()
ctx.moveTo(0,0)
ctx.arc(0,0,10,0,360*Math.PI/180)
ctx.fill()
ctx.restore()
ctx.save()
ctx.lineWidth=6
ctx.strokeStyle="#D40000"
ctx.rotate(seconds * 6 * Math.PI / 180)
ctx.beginPath()
ctx.arc(96,0,10,0,360*Math.PI/180)
ctx.stroke()
ctx.restore()
ctx.restore()
}
</script>
</html>
canvas 搭配video
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<video id="video" loop autoplay src="https://imgs-qn.51miz.com/preview/video/00/00/12/72/V-127220-21D61AFB.mp4" width="0" height="0"></video>
<canvas id="canvas" width="300" height="200"></canvas>
</body>
<script type="text/javascript">
let ctx = canvas.getContext('2d')
video.addEventListener("loadeddata", function () {
console.log(123)
setInterval(function(){
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
})
})
</script>
</html>
写入图片:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<canvas id="canvas" width="400" height="400" />
</body>
<script type="text/javascript">
canvas.style.background = "pink"
let ctx = canvas.getContext("2d")
let img = new Image() //创建image对象
img.src = "img.jpg" //添加资源路径
img.onload=()=>{
ctx.drawImage(img, 0, 0,canvas.width, canvas.height)
}
</script>
</html>
canvas实现刮刮卡的效果:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<style>
*{
margin: 0;
padding: 0
}
html,body{
height:100%;
overflow: hidden;
}
#canvas{
position: absolute;
top:0;
left:0;
transition:1s
}
.wrap,.wrap>div{
height: 100%
}
.wrap>div{
background-image: url("./img2.jpg");
background-size: 100% 100%;
}
</style>
<body>
<div class="wrap">
<canvas id="canvas"></canvas>
<div></div>
</div>
</body>
<script type="text/javascript">
// 获取宽高,
canvas.width=document.documentElement.clientWidth
canvas.height=document.documentElement.clientHeight
let ctx = canvas.getContext("2d")
//获取canvas画的图片,父级元素的背景是底图
let img =new Image()
img.src="img.jpg"
let imgData = ctx.getImageData(0,0,canvas.width,canvas.height)
img.onload=function (){
draw()
}
function draw(){
ctx.drawImage(img,0,0,imgData.width,imgData.height) //绘画图片
canvas.addEventListener("touchstart",(e)=>{ //点击到屏幕时触发
console.log(e)
let touchC= e.changedTouches[0]
let x = touchC.clientX-canvas.offsetLeft
let y = touchC.clientY-canvas.offsetTop
ctx.lineWidth=20
ctx.lineCap="round"
ctx.lineJoin="round"
ctx.globalCompositeOperation="destination-out"
ctx.save()
ctx.beginPath()
ctx.moveTo(x,y)
ctx.lineTo(x+1,y+1)
ctx.stroke()
ctx.restore()
})
canvas.addEventListener("touchmove",(e)=>{ //滑动时触发
let touchC= e.changedTouches[0]
let x = touchC.clientX-canvas.offsetLeft
let y = touchC.clientY-canvas.offsetTop
console.log(x,y)
ctx.save()
// ctx.arc(x,y,20,0,360*Math.PI/180)
ctx.lineTo(x+1,y+1)
ctx.stroke()
ctx.restore()
})
canvas.addEventListener("touchend",(e)=>{ //离开屏幕时触发
let flag =0
let imgData = ctx.getImageData(0,0,canvas.width,canvas.height)
let allPixle = imgData.width*imgData.height //获取图片像素
for(let i=0;i<allPixle;i++){
if(imgData.data[4*i+3]==0){
flag++
}
}
if(flag>allPixle*1/2){ //滑过超过一半的话,隐藏画布
canvas.style.opacity=0
}
})
canvas.addEventListener("transitionend",function(){
this.remove()//隐藏画布过渡时触发,删除画布
})
}
ctx.save()
ctx.beginPath()
ctx.restore()
</script>
</html>
canvas马赛克:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<canvas id="canvas" />
</body>
<script type="text/javascript ">
let ctx = canvas.getContext('2d')
let oldImg = new Image()
oldImg.src = "img2.jpg"
oldImg.onload=()=>{
canvas.width=oldImg.width*2
canvas.height=oldImg.height
draw()
}
function draw(){
ctx.drawImage(oldImg,0,0)
//获取原图的imageData
let oldImgData = ctx.getImageData(0,0,oldImg.width,oldImg.height)
// 创建新的图片数据,按照原图的尺寸
let newImgData = ctx.createImageData(oldImg.width,oldImg.height)
console.log(newImgData)
let size = 20 //指定马赛克块的大小
for(let i = 0;i<oldImgData.width/size;i++){
for(let j =0;j<oldImgData.height/size;j++){
// (i,j)每个马赛克的巨型坐标
//获取原图颜色,在像素区域内随机选取一个颜色
let color = getColor(oldImgData,i*size+Math.floor(Math.random()*size),j*size+Math.floor(Math.random()*size))
//把整个20以内的所有像素全部设置为一样的颜色
for(let a=0;a<size;a++){
for(let b=0;b<size;b++){
setColor(newImgData,i*size+a,j*size+b,color)
}
}
}
}
ctx.putImageData(newImgData,oldImg.width,0)
}
function getColor(imgData,x,y){ //每个像素是由rgba的形式出现
let data = imgData.data
let w = imgData.width
let h = imgData.height
let color = Array(4)
//y*w+x是指该元素前有多少个像素,来定位当前的像素
color[0]=data[(y*w+x)*4]
color[1]=data[(y*w+x)*4+1]
color[2]=data[(y*w+x)*4+2]
color[3]=data[(y*w+x)*4+3]
return color
}
function setColor(imgData,x,y,color){
let data = imgData.data
let w = imgData.width
let h = imgData.height
data[(y*w+x)*4]=color[0]
data[(y*w+x)*4+1]=color[1]
data[(y*w+x)*4+2]=color[2]
data[(y*w+x)*4+3]=color[3]
}
</script>
</html>