canvas
介绍
H5新特性,canvas被用来绘制图形,制作图片集合,甚至用来实现动画效果
属性值
只有width和height属性
画线
- 通过const cvs = document.querySelector(‘canvas’)拿到DOM元素
- 通过const ctx = cvs.getcontext(“2d”)拿到CanvasRenderingContext2D的类型对象
- 开启一条路径ctx.beginPath()
- 开始位置ctx.moveTo(x,y)
- 结束位置ctx.lineTo(x,y)
- 进行上色ctx.stroke()
- 关闭路径ctx.closePath()
- 设置绘线的粗细ctx.lineWidth,默认值为1.0
- 设置线段端点显示的样子,butt,round 和 square
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Document</title>
<style>
canvas{
margin: 0 auto;
border: 1px solid #000;
display: block;
}
</style>
</head>
<body>
<canvas width="500" height="500"></canvas>
<script>
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
// 画虚线
for (let i = 0; i < 20; i++) {
drawLine(100+10*i,100,105+10*i,100,'green',2)
}
function drawLine(x1,y1,x2,y2,color,width){
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.lineTo(x2,y2);
ctx.strokeStyle=color;
ctx.lineWidth=width;
ctx.stroke();
ctx.closePath();
}
</script>
</body>
</html>
画矩形
- rect(x,y,width,height)
- x,y矩形坐标
- width,height宽高
- strokeRect(x,y,width,height)
- 描边矩形
- fillRect(x,y,width,height)
- 填空矩形
- 会自动闭合路径
- clearRect(x,y,width,height)
- 清除矩形
- 清空画布,类似与橡皮檫
画圆
- arc(x,y,radius,startAngle,endAngle,counterclockwise)
- x,y描述圆心坐标
- radius圆形半径
- startAngle,endAngle起始角度和结束角度
- counterclockwise顺时针和逆时针
绘制文字
- fillText()绘制无填充文字
- strokeText()绘制有填充文字
- measureText()返回文本宽度对象
- font='red’CSS样式
- textBaseline='bottom’设置底线对齐绘制基线
- textAlign='left’设置文字对齐方式
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Document</title>
<style>
canvas{
margin: 0 auto;
border: 1px solid #000;
display: block;
}
</style>
</head>
<body>
<canvas width="600" height="600"></canvas>
<script>
// 绘制饼状图并显示比例
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const data = [{
value:'苹果',
color:'red',
num:0.1,
},{
value: '香蕉',
color:'yellow',
num:0.3,
},{
value: '梨子',
color: 'green',
num:0.4,
},{
value: '橘子',
color: 'orange',
num:0.2
}]
let temAngle=-90;
for (let i = 0; i < data.length; i++) {
const angle=data[i].num*360;
drawArc(300,300,200,angle,data[i].color,data[i].num)
}
function drawArc(x1,y1,radius,angle,color,num){
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.fillStyle=color;
const startAngle=temAngle*Math.PI/180;
const endAngle=(temAngle+angle)*Math.PI/180;
ctx.arc(x1,y1,radius,startAngle,endAngle);
// 绘制文字
const text=num*100+'%';
const textAngle=temAngle+1/2*angle;
const x=x1+Math.cos(textAngle*Math.PI/180)*(radius+20);
const y=y1+Math.sin(textAngle*Math.PI/180)*radius;
if(textAngle>90&&textAngle<270){
ctx.textAlign='end'
}
ctx.fillText(text,x,y)
ctx.fill();
temAngle+=angle;
}
</script>
</body>
</html>
绘制图片
- context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height)
- imgDOM对象||也可以是画布,也就是把一个画布整体的渲染到另外一个画布上
- sx,sy裁剪框左上角X和Y坐标
- swidth,sheight裁剪宽高
- x,y图片坐标
- width,height图片宽高
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Document</title>
<style>
canvas{
margin: 0 auto;
border: 1px solid #000;
display: block;
}
</style>
</head>
<body>
<canvas width="500" height="500"></canvas>
<script>
// 绘制帧动画
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const img=new Image();
img.src='./imgs/ying.jpeg';
img.onload=function (){
let frameIndex=0;
let dirIndex=0;
setInterval(()=>{
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(
img,
frameIndex*130,
dirIndex*164,
130,
164,
100,
100,
130*2,
164*2
)
frameIndex++;
frameIndex%=3;
console.log(frameIndex)
if(frameIndex===2){
dirIndex++
}
dirIndex%=2;
},1000/4)
}
</script>
</body>
</html>
阴影与线性渐变和圆形渐变
阴影
- shadowColor : 设置或返回用于阴影的颜色
- shadowBlur : 设置或返回用于阴影的模糊级别,大于 1 的正整数,数值越高,模糊程度越大
- shadowOffsetX: 设置或返回阴影距形状的水平距离
- shadowOffsetY: 设置或返回阴影距形状的垂直距离
线性渐变
- ctx.createLinearGradient(x0,y0,x1,y1);
- x0,y0 起始坐标
- x1,y1 结束坐标
圆形渐变
- context.createRadialGradient(x0,y0,r0,x1,y1,r1)
- x0: 渐变的开始圆的 x 坐标
- y0: 渐变的开始圆的 y 坐标
- r0: 开始圆的半径
- x1: 渐变的结束圆的 x 坐标
- y1: 渐变的结束圆的 y 坐标
- r1: 结束圆的半径
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Document</title>
<style>
canvas{
margin: 0 auto;
border: 1px solid #000;
display: block;
}
</style>
</head>
<body>
<canvas width="500" height="500"></canvas>
<script>
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle='rgba(255,0,0,0.9)';
ctx.shadowColor='teal';
ctx.shadowBlur=10;
ctx.shadowOffsetX=10;
ctx.shadowOffsetY=10;
// 创建线性渐变
const grd=ctx.createLinearGradient(0,0,170,0);
grd.addColorStop(0,'black'); // 添加渐变颜色
grd.addColorStop(0.5,'red')
grd.addColorStop(1,'white');
ctx.fillStyle=grd;
ctx.fillRect(0,0,300,300)
</script>
</body>
</html>
位移画布
移动
ctx.translate(x,y) 方法重新映射画布上的 (0,0) 位置
旋转
context.rotate(angle); 方法旋转当前的绘图
缩放
context.scale(scalewidth,scaleheight)
保存
ctx.save() 保存当前环境的状态
可以把当前绘制环境进行保存到缓存中。
重置
ctx.restore() 返回之前保存过的路径状态和属性
获取最近缓存的 ctx;需要和save()配合使用
将图片输出为base64格式
- canvas.toDataURL(type, encoderOptions)
- type,设置输出的类型,比如 image/png image/jpeg
- encoderOptions: 0-1 之间的数字,用于标识输出图片的质量,1 表示无损压缩,类型为: image/jpeg 或者 image/webp 才起作用
cnavas对图片裁剪
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>裁剪图片</title>
<style>
*{
margin: 0;
padding: 0;
}
.wrap{
width: 800px;
margin: 0 auto;
}
.canvasBox{
width: 600px;
height: 600px;
background-color: #eee;
position: relative;
}
.mark{
width: 50%;
height: 50%;
background-color: rgba(0,0,0,0.5);
position: absolute;
top: 150px;
left: 150px;
/*display: none;*/
}
ul{
display: flex;
width: 600px;
justify-content: space-between;
}
li{
list-style: none;
}
</style>
</head>
<body>
<div class="wrap">
<div class="canvasBox">
<canvas></canvas>
<div class="mark"></div>
</div>
<ul>
<li>
<label for="file">选择图片</label>
<input
id="file"
type="file"
accept="image/*"
style="display: none"
/>
</li>
<li onclick="scale(1)">放大</li>
<li onclick="scale(0)">缩小</li>
<li onclick="save()">保存</li>
</ul>
<img src="" id="img"/>
</div>
<script>
/**
* 准备数据
* 1. 画布大小,遮罩大小和位置,上传大小和位置
*/
let img;
let imgW;
let imgH;
let imgOriginW;
let imgOriginH;
let startX;
let startY;
const canvasW=600;
const canvasH=600;
const fileNode=document.querySelector('#file');
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const canvasBoxNode=document.querySelector('.canvasBox');
const imgNode=document.querySelector('#img');
// 拿到file文件
fileNode.onchange=function (e){
const file=e.target.files[0];
if(!file) return;
canvas.width=canvasW;
canvas.height=canvasH;
// 文件读取
const fileExample=new FileReader();
fileExample.readAsDataURL(file);
fileExample.onload=(e)=>{
img=new Image();
img.src=e.target.result;
img.onload=()=>{
imgW=img.width;
imgH=img.height;
imgOriginW=100;
imgOriginH=100;
drawImage()
}
}
fileNode.value='';
}
// 绘制canvas
function drawImage(){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(img,imgOriginW,imgOriginH,imgW,imgH)
}
// 缩放
function scale(flag){
if(!img) return;
let n=imgW/imgH;
const n1=20;
const n2=n1/n;
if(flag){
imgW+=n1;
imgH+=n2;
}else {
imgW-=n1;
imgH-=n2;
}
drawImage()
}
// 刚移入记录位置
canvasBoxNode.ontouchstart=function (e){
const point=e.changedTouches[0];
startX=point.clientX;
startY=point.clientY;
}
// 移动记录位置
canvasBoxNode.ontouchmove=function (e){
if(!img) return;
const point=e.changedTouches[0];
const x=point.clientX-startX;
const y=point.clientX-startY;
console.log(x,y)
if(Math.abs(x)<20||Math.abs(y)<20) return;
imgOriginW+=x;
imgOriginH+=y;
drawImage();
startX=point.clientX;
startY=point.clientY;
}
// 保存遮罩内图片
function save(){
if(!img) return;
const imageData=ctx.getImageData(150,150,300,300);
const canvas2=document.createElement('canvas');
const canvas2Ctx=canvas2.getContext('2d');
canvas2.width=300;
canvas2.height=300;
canvas2Ctx.putImageData(imageData,0,0,0,0,300,300);
imgNode.src=canvas2.toDataURL('image/png')
console.log(canvas2.toDataURL('image/png'))
}
</script>
</body>
</html>