canvas一些基础的概念在直线绘制中已经提过。下面主要提曲线和文本绘制,以完成一个完整饼状图的绘制。
(一)曲线的绘制
- 弧度概念
- 1 弧度 = 1 r(半径)
- 圆的周长=2πr
- 原的总弧度=2π
- 确定圆心
- 确定半径
- 确定圆弧的起始位置和结束位置 (以弧度为单位,0====水平右边)
- 确定圆弧的绘制方向 默认false 顺时针
- arc() 圆弧绘制函数
- x 圆心横坐标
- y 圆心纵坐标
- r 半径
- startAngle 开始角度
- endAngle 结束角度
- anticlockwise 是否逆时针方向绘制(默认false表示顺时针;true表示逆时针)
(二)文本的绘制
- ctx.font = ‘20px 微软雅黑’ 设置字体
- strokeText(text,x,y,maxWidth) 绘制空心字体
- text 要绘制的文本
- x,y 文本绘制的坐标(文本左下角)
- maxWidth 设置文本最大宽度,可选参数
- fillText(text,x,y,maxWidth) 绘制实心字体
- text 要绘制的文本
- x,y 文本绘制的坐标(文本左下角)
- maxWidth 设置文本最大宽度,可选参数
- ctx.textAlign 文本水平对齐方式,相对绘制坐标
- left
- center
- right
- start 默认
- end
- ctx.direction属性css(rtl ltr) start和end于此相关
- 如果是ltr,start和left表现一致
- 如果是rtl,start和right表现一致
- ctx.textBaseline 设置基线(垂直对齐方式 )
- top 文本的基线处于文本的正上方,并且有一段距离
- middle 文本的基线处于文本的正中间
- bottom 文本的基线处于文本的证下方,并且有一段距离
- hanging 文本的基线处于文本的正上方,并且和文本粘合
- alphabetic 默认值,基线处于文本的下方,并且穿过文字
- ideographic 和bottom相似,但是不一样
- measureText() 获取文本宽度 拿到的是一个带有width的对象 obj.width
(三)实例
- 1.体验曲线的绘制
- 线是由点构成的
- 曲线可以由数学公式绘制出来
//线是由点构成的
//曲线可以由数学公式画出来
//公式1: y=6x;
//公式2: y=(x+2)^2;
//公式3: y=(x/2-100)^2;
//公式4: y=sin(x);
let myCanvas=document.querySelector('canvas');
let ctx=myCanvas.getContext('2d');
for(let i=1;i<600;i++){
let x=i;
// let y=6*x;
// let y=Math.pow(x+2,2);
// let y=Math.pow(x/2-100,2);
let y=40*Math.sin(x/2)+200;
ctx.lineTo(x,y);
ctx.strokeStyle="#479B1E";
ctx.stroke();
}
- 2.使用圆弧绘制函数绘制圆弧
//绘制一个圆心在画布中心 半径为150 右下角半边的圆弧
let myCanvas=document.querySelector('canvas');
let ctx=myCanvas.getContext('2d');
//获取画布的宽高以此来确认圆心
let canvasWidth=ctx.canvas.width;
let canvasHeight=ctx.canvas.height;
//使用函数来绘制圆弧 参数: x,y,r,startAngle,endAngle,direction(圆心,半径,开始弧度,结束弧度,方向)
//四分之一个圆的弧度===2π/4===π/2
//函数只是绘制路径
// ctx.arc(canvasWidth/2,canvasHeight/2,150,0,Math.PI/2);
//绘制右上角的半边圆弧
// ctx.arc(canvasWidth/2,canvasHeight/2,150,0,Math.PI*3/2,true);
//绘制下半边圆弧
// ctx.arc(canvasWidth/2,canvasHeight/2,150,0,Math.PI);
//绘制只缺左下角的圆弧
// ctx.arc(canvasWidth/2,canvasHeight/2,150,Math.PI/2,Math.PI,true);
ctx.arc(canvasWidth/2,canvasHeight/2,150,Math.PI/2,-Math.PI/4,false);
ctx.strokeStyle='#479B1E';
ctx.stroke();
- 3.绘制扇形
- 要确定圆心
- 使用程序自动闭合
let myCanvas=document.querySelector('canvas');
let ctx= myCanvas.getContext('2d');
//在中心位置 绘制一个半径为200的 位于右上角的 扇形
//获取宽高
let canvasWidth= ctx.canvas.width;
let canvasHeight= ctx.canvas.height;
//首先要定起始位置
ctx.moveTo(canvasWidth/2,canvasHeight/2);
//绘制弧度
ctx.arc(canvasWidth/2,canvasHeight/2,200,0,-Math.PI/2,true);
//使用程序自动闭合
ctx.closePath();
ctx.stroke();
ctx.fill();
- 4.绘制n等份的颜色随机的圆
let myCanvas = document.querySelector('canvas');
let ctx = myCanvas.getContext('2d');
//设置n等分
let num = 6;
//那么每一等份的弧度为
let radian = 2 * Math.PI / num;
//获取canvas宽高
let canvasWidth = ctx.canvas.width;
let canvasHeight = ctx.canvas.height;
//设置随机颜色
function getColor() {
let r = Math.floor(Math.random() * 256);
let g = Math.floor(Math.random() * 256);
let b = Math.floor(Math.random() * 256);
return "rgb(" + r + "," + g + "," + b + ")";
}
//遍历等分
for (let i = 0; i < num; i++) {
let startRadian = i * radian;
let endRadian = (i + 1) * radian;
ctx.beginPath();
ctx.moveTo(canvasWidth / 2, canvasHeight / 2);
ctx.arc(canvasWidth / 2, canvasHeight / 2, 200, startRadian, endRadian);
ctx.fillStyle = getColor();
ctx.fill();
}
- 5.根据数据绘制饼图–统计每个年龄层人数
let myCanvas = document.querySelector('canvas');
let ctx = myCanvas.getContext('2d');
//根据数据来绘制饼图
//统计每个年龄层的人数
/*
* 15~20 5
* 20~25 10
* 25~30 20
* 30~35 16
* 35~40 25
* */
//1.设置数据
let data = [5, 10, 20, 16, 25];
//2.获取弧度list 每个弧度= 总弧度 * (每个数组/总值)
let total = data.reduce((previousValue, currentValue, currentIndex, array) => {
// console.log(previousValue +"=========="+currentValue)
return previousValue + currentValue;
});
//获取canvas宽高
let canvasWidth = ctx.canvas.width;
let canvasHeight = ctx.canvas.height;
//获取随机颜色
function getColor() {
let r = Math.floor(Math.random() * 256);
let g = Math.floor(Math.random() * 256);
let b = Math.floor(Math.random() * 256);
return "rgb(" + r + "," + g + "," + b + ")";
}
//3.遍历 绘制每个扇形
let startRadian = 0;
data.forEach((value, index) => {
ctx.beginPath();
ctx.moveTo(canvasWidth / 2, canvasHeight / 2);
if (index === 0) {
ctx.arc(canvasWidth / 2, canvasHeight / 2, 200, startRadian, 2 * Math.PI * (value / total));
} else {
ctx.arc(canvasWidth / 2, canvasHeight / 2, 200, startRadian, startRadian + 2 * Math.PI * (value / total));
}
startRadian = startRadian + 2 * Math.PI * (value / total);
ctx.fillStyle = getColor();
ctx.fill();
})
- 6.在画布中心绘制文本带下划线
let myCanvas=document.querySelector('canvas');
let ctx=myCanvas.getContext('2d');
//在画布中心绘制文本+下划线
//获取画布宽高
let canvasWidth=ctx.canvas.width;
let canvasHeight=ctx.canvas.height;
//设置文本内容
let str="春有百花秋有月,夏有凉风冬有雪。";
//设置文本大小及文本字体
ctx.font="40px 全新硬笔行书简";
//设置文本水平对齐方式 以起始坐标为基准 即下面text中的x,y为基准 默认与基线对齐 起始位置在左下角 (矩形绘制起始位置是在左上角)
//left (默认) center right start end
ctx.textAlign="center";
//设置文本垂直对齐方式 top middle bottom
ctx.textBaseline="middle";
//绘制空心字体
// ctx.strokeText(str,canvasWidth/2,canvasHeight/2);
ctx.fillStyle="#1AC396";
//绘制实心字体
ctx.fillText(str,canvasWidth/2,canvasHeight/2);
//绘制下划线
//要知道文本的宽度 这样才能确定下划线的开始x和结束x
ctx.beginPath();
//测量文本宽度 measureText 拿到的是一个包含宽度的对象
let textWidth=ctx.measureText(str).width;
ctx.moveTo(canvasWidth/2-textWidth/2,canvasHeight/2+20);
ctx.lineTo(canvasWidth/2+textWidth/2,canvasHeight/2+20);
ctx.strokeStyle="#9CC31A";
ctx.stroke();
- 7.绘制完整饼状图
- 绘制饼图
- 绘制标题
- 绘制图例
//通过面向对象的方式
//绘制饼状图
//1.创建构造函数
//2.设置数据
//3.根据数据绘制饼图
//4.绘制饼图中每一块扇形的标题
//5.绘制legend 图例
//饼状图的构造函数
let PieChart=function (ctx) {
//上下文
this.ctx=ctx || document.querySelector('canvas').getContext('2d');
//画布的宽
this.canvasWidth=this.ctx.canvas.width;
//画布的高
this.canvasHeight=this.ctx.canvas.height;
//饼状图的圆心 x0
this.x0=this.canvasWidth/2+20;
//饼状图的圆心 y0
this.y0=this.canvasHeight/2;
//饼状图的半径r
this.radius=150;
//标题超出的长度
this.exceedLength=20;
//图例距离边框的间距
this.space=20;
//图例之间的间距
this.legendSpace=10;
//图例矩形部分的宽
this.rectW=40;
//图例矩形部分的高
this.rectH=20;
};
//方法初始化
PieChart.prototype.init=function(data){
let total= this.getTotalData(data);
//绘制饼状图
this.drawPie(data,total);
};
//绘制饼状图
PieChart.prototype.drawPie=function(data,total){
let startRadian=0;
data.forEach((item,index) =>{
this.ctx.beginPath();
this.ctx.moveTo(this.x0,this.y0);
//结束弧度
let temp=2*Math.PI*(item.value/total);
let endRadian= startRadian+temp;
//绘制扇形
this.ctx.arc(this.x0,this.y0,this.radius,startRadian,endRadian);
//自动闭合
this.ctx.closePath();
//设置填充颜色
let curColor=this.getColor();
this.ctx.fillStyle=curColor;
//填充
this.ctx.fill();
//绘制标题
this.drawTitle(startRadian,temp,curColor,item.name);
//绘制图例
this.drawLegend(item,curColor,index);
//下一个开始弧度
startRadian=endRadian;
})
};
//绘制标题
PieChart.prototype.drawTitle=function(startRadian,radian,color,text){
//绘制标题步骤
//- 获取弧度
//-获取斜边边长
//- 获取标题引导线的x y
//- 绘制引导线
//- 绘制下划线
//- 绘制文本
//获取弧度 =上一个弧度+现在弧度的一半
let currentRadian=startRadian+radian/2;
//获取斜边长
let bevel=this.radius+this.exceedLength;
//根据cos获取距离圆心x的横轴距离
let tX=Math.cos(currentRadian)*bevel;
//根据sin获取距离圆心y的垂直距离
let tY=Math.sin(currentRadian)*bevel;
this.ctx.beginPath();
this.ctx.moveTo(this.x0,this.y0);
//引导线的x=x0+tX y=y0+tY;
let lX=this.x0+tX;
let lY=this.y0+tY;
this.ctx.lineTo(lX,lY);
this.ctx.strokeStyle=color;
// this.ctx.stroke();
//绘制下划线 下划线的长度根据标题内容大小设置
this.ctx.font="14px Microsoft YaHei";
let txtW=this.ctx.measureText(text).width;
//判断当前超出的点的x位置是在x的左侧还是右侧,左侧- 右侧+
this.ctx.textBaseline="bottom";
if(lX>this.x0){
//画线
this.ctx.lineTo(lX+txtW,lY);
this.ctx.textAlign="left";
//绘制文字
this.ctx.fillText(text,lX,lY);
}else {
this.ctx.lineTo(lX-txtW,lY);
//在左侧还需设置字体对齐方式
this.ctx.textAlign="right";
//绘制文字
this.ctx.fillText(text,lX,lY);
}
this.ctx.stroke();
};
//绘制图例
PieChart.prototype.drawLegend=function(item,color,index){
//图例绘制
//矩形绘制 文字绘制
let rectY=(index+1)*this.legendSpace+index*this.rectH;
//矩形绘制
// this.ctx.rect(this.space,rectY,this.rectW,this.rectH);
this.ctx.fillRect(this.space,rectY,this.rectW,this.rectH);
//文字绘制
this.ctx.textAlign="left";
this.ctx.textBaseline="middle";
this.ctx.fillText(item.name,this.space+this.rectW+this.legendSpace,rectY+this.rectH/2);
};
//获取随机颜色
PieChart.prototype.getColor=function(){
let r=Math.floor(Math.random()*256);
let g=Math.floor(Math.random()*256);
let b=Math.floor(Math.random()*256);
return "rgb("+r+","+g+","+b+")";
};
//获取数值总值
PieChart.prototype.getTotalData=function(data){
return data.reduce((preValue,currentValue)=>{
return preValue+currentValue.value
},0)
};
//数据值 包含数值,数值名
let data=[
{name:"10~15岁",value:25},
{name:"15~20岁",value:15},
{name:"20~25岁",value:20},
{name:"25~30岁",value:35},
{name:"30~35岁",value:30},
{name:"35~40岁",value:45}
];
//初始化
let pieChart= new PieChart();
pieChart.init(data);