1 Star 0 Fork 0

pyf/canvas Graph

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
binzhuangtu.html 12.22 KB
一键复制 编辑 原始数据 按行查看 历史
pyf 提交于 2024-01-02 20:08 +08:00 . first commit
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>绘制饼状图</title>
<style lang="css">
canvas {
border: 1px solid #ccc;
display: block;
margin: 100px auto;
}
</style>
</head>
<body>
<canvas width="600" height="400" id="myCanvas"></canvas>
<script>
// const myCanvas = document.getElementById('myCanvas')
// const ctx = myCanvas.getContext('2d')
// 1、绘制饼状图
// 1、1 根据数据绘制一个饼图
// 1、2 绘制标题 从扇形的弧中心伸出一条线,在画一条横线,且横线上写上文字标题
// 1、3 在画布左上角绘制说明,画一个矩形和扇形颜色对应,旁边写文字说明
const PieChart = function(cnavasId, ctx) {
this.canvas = CanvasM = document.getElementById(cnavasId)
this.ctx = this.canvas.getContext('2d')
// 绘制饼图中心
this.W = this.ctx.canvas.width;
this.H = this.ctx.canvas.height;
// 圆心
this.x0 = this.W / 2 + 60;
this.y0 = this.H / 2;
// 半径
this.radius = 150;
// 伸出去的线的长度
this.outLine = 20;
// 说明的矩形大小
this.rectW = 30;
this.rectH = 16;
this.space = 20;
// 鼠标移动当前扇形半径
this.hoverRadius = 170
this.pieIndex = -1
}
// 初始化
PieChart.prototype.init = function(data, pointer, click) {
// 1、准备数据
this.drawPie(data, pointer, click)
}
// 画饼图
PieChart.prototype.drawPie = function(data, pointer, click) {
let that = this;
// 1、转化弧度
let angleList = this.transformAngle(data)
// 2、绘制饼图
let startAngle = 0;
angleList.forEach((item, index) => {
// 当前结束的弧度等于下一次的起始弧度
let endAngle = startAngle + item.angle
this.ctx.beginPath()
this.ctx.moveTo(that.x0, that.y0)
this.ctx.arc(that.x0, that.y0, that.radius, startAngle, endAngle);
// 伸出去线的颜色
// const outColor = this.ctx.fillStyle = that.getRandomColor()
const outColor = this.ctx.fillStyle = colorList[index]
this.ctx.fill()
// isPointInPath() 是判断所指定的点是否在所绘制的弧线上,是则返回true,不是则返回false
// 饼图事件:用于判断鼠标是否移入第 i 块图内
if (pointer && that.ctx.isPointInPath(pointer.x, pointer.y)) {
// 排除圆心坐标,鼠标移动到圆心时,不需要触发事件
if (pointer.x !== this.x0 && pointer.y !== this.y0) {
// 重新绘制当前扇形
// 1、扇形伸出点坐标和下划线终点坐标
this.ctx.beginPath();
this.ctx.moveTo(this.x0, this.y0);
this.ctx.arc(this.x0, this.y0, this.hoverRadius, startAngle, endAngle, false)
// 绘制阴影
this.ctx.shadowBlur = 10;
this.ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
this.ctx.fill()
// 有鼠标时绘制标题
this.drawMouseMoveTitle(startAngle, item.angle, outColor, item.title)
if (click) {
pieChart.onClick(item, index)
}
}
} else {
// 无鼠标时绘制标题
this.drawTitle(startAngle, item.angle, outColor, item.title)
}
// 绘制标题
// this.drawTitle(startAngle, item.angle, outColor, item.title)
// 绘制说明部分
this.drawDesc(index, item.title, pointer)
// 下一次开始弧度等于这一次结束弧度
startAngle = endAngle;
})
}
// 点击事件回调
PieChart.prototype.onClick = function(item, index) {
return function() {
return {...item,
index
};
}
}
// 鼠标移动到当前扇形放大效果
// 计算对应区块起始和结束对应的点
PieChart.prototype.drawMouseMoveTitle = function(startAngle, angle, outColor, outTitle) {
// 去掉继承父类的阴影
this.ctx.shadowBlur = 0;
// 斜边C的长度 = 鼠标放大半径 + 延长线
const hEdge = this.hoverRadius + this.outLine;
// X轴方向的直角边
const hEdgeX = Math.cos(startAngle + angle / 2) * hEdge;
// Y轴方向的直角边
const hEdgeY = Math.sin(startAngle + angle / 2) * hEdge;
// 计算伸出去点的坐标(hOutX, hOutY)
const hOutX = this.x0 + hEdgeX;
const hOutY = this.y0 + hEdgeY;
// 根据圆心和伸出去的点的坐标画线
this.ctx.beginPath();
this.ctx.moveTo(this.x0, this.y0);
this.ctx.lineTo(hOutX, hOutY);
this.ctx.strokeStyle = outColor;
// 绘制线
// this.ctx.stroke()
// 结束点坐标和文字字符串长度有关
const textWidth = this.ctx.measureText(outTitle).width;
// 判断文字下划线方向,hOutX 大于x0 向左,hOutX 大于x0向右
if (hOutX > this.x0) {
this.ctx.lineTo(hOutX + textWidth, hOutY)
this.ctx.textAlign = 'left'
} else {
this.ctx.lineTo(hOutX - textWidth, hOutY)
this.ctx.textAlign = 'right'
}
// 统一绘制线条
this.ctx.stroke()
// 文字对齐方式
this.ctx.textBaseline = 'bottom';
// 添加文本
this.ctx.fillText(outTitle, hOutX, hOutY)
}
// 绘制标题
PieChart.prototype.drawTitle = function(startAngle, angle, outColor, outTitle) {
// 去掉继承父类的阴影
this.ctx.shadowBlur = 0;
// 1、确定伸出去的线 通过圆心 通过伸出去的点 确定这个线
// 2、确定伸出去的点 需要确定伸出去的线的长度
// 3、固定伸出去的线的长度
// 4、计算这个点的坐标
// 5、确定圆弧中心点坐标,根据角度和斜边的长度
// 5.1 计算【每个扇形弧度中心点=当前扇形的起始弧度startAngle + 对应弧度的一半(angle/2: 当前扇形弧度的一半)】
// 5.2 斜边长度edge=半径+伸出去的长度,伸出去点的坐标(outX, outY)
const edge = this.radius + this.outLine
// 5.3 outX = x0 + cos(angle) * (r + outLine)
// 5.3 outY = y0 + sin(angle) * (r + outLine)
// X轴方向的直角边
const edgeX = Math.cos(startAngle + angle / 2) * edge;
// Y轴方向的直角边
const edgeY = Math.sin(startAngle + angle / 2) * edge;
// 计算伸出去的点坐标
const outX = this.x0 + edgeX;
const outY = this.y0 + edgeY;
this.ctx.beginPath();
this.ctx.moveTo(this.x0, this.y0);
this.ctx.lineTo(outX, outY)
this.ctx.strokeStyle = outColor
// 根据坐标绘制线
// this.ctx.stroke();
// 画文字和下划线
// 线的方向怎么判断:伸出去的点在x0的左边,线的方向就是左边
// 线的方向怎么判断:伸出去的点在x0的右边 线的方向就是右边
// 设置文字大小字体,要放在测量之前才生效,否则横线会变短
this.ctx.font = 'bold 16px Microsoft YaHei';
// 结束点坐标和文字字符串长度有关
const textWidth = this.ctx.measureText(outTitle).width;
if (outX > this.x0) {
// 文字在右边
this.ctx.lineTo(outX + textWidth, outY)
this.ctx.textAlign = 'left'
} else {
// 文字在左边
this.ctx.textAlign = 'right'
this.ctx.lineTo(outX - textWidth, outY)
}
// 统一描边
this.ctx.stroke();
// 文字对齐方式
this.ctx.textBaseline = 'bottom';
// 显示文字
this.ctx.fillText(outTitle, outX, outY)
}
// 绘制描述面板内容
PieChart.prototype.getRandomColor = function() {
let r = Math.floor(Math.random() * 256);
let g = Math.floor(Math.random() * 256);
let b = Math.floor(Math.random() * 256);
let rgb = 'rgb(' + r + ',' + g + ',' + b + ')';
return rgb;
}
// 绘制描述面板内容
PieChart.prototype.drawDesc = function(index, descTitle, pointer) {
// 去掉继承父类的阴影
this.ctx.shadowBlur = 0;
// 绘制说明
// 矩形大小
// 每一个矩形外边距
this.ctx.fillRect(this.space, this.space + index * (this.rectH + 10), this.rectW, this.rectH);
this.ctx.font = '16px Microsoft YaHei';
this.ctx.beginPath()
this.ctx.textAlign = 'left';
this.ctx.textBaseline = 'top';
// 绘制文字
this.ctx.fillText(descTitle, this.space + this.rectW + 10, this.space + index * (this.rectH + 10))
}
// 转化弧度,返回的数据应该包含弧度
PieChart.prototype.transformAngle = function(data) {
let total = 0
data.forEach(el => total += el.num)
// 创建弧度属性,添加到每个对象内
data.forEach(item => {
const angle = item.num / total * Math.PI * 2;
item['angle'] = angle;
})
return data;
}
// 数据准备
const data = [{
title: '技术部',
num: 34
}, {
title: '工程部',
num: 13
}, {
title: '保洁部',
num: 20
}, {
title: '销售部-文字',
num: 42
}, ]
// 自定义颜色
const colorList = ['#ee7752', '#e73c7e', '#23a6d5', '#23d5ab']
// 初始化饼图,饼图构造函数this,无法在外部访问,因为没有内部赋值
const pieChart = new PieChart('myCanvas', data)
pieChart.init(data)
// 监听鼠标在饼图上移动坐标
pieChart.canvas.addEventListener('mousemove', function(event) {
const x = event.offsetX;
const y = event.offsetY;
// 清空画布
pieChart.ctx.clearRect(0, 0, 600, 400)
// 重绘饼状图,将鼠标坐标进行传参
pieChart.init(data, {
x,
y
})
})
// 监听鼠标点击扇形事件
pieChart.canvas.addEventListener('click', function(event) {
// 获取点击坐标
const x = event.offsetX;
const y = event.offsetY;
// console.log(x, y);
// 清空画布
pieChart.ctx.clearRect(0, 0, 600, 400)
// 重绘饼状图,将鼠标坐标进行传参
pieChart.init(data, {
x,
y
}, 'click')
// 事件回调函数
pieChart.onClick = function(data, index) {
pieChart.pieIndex = index
console.log(data, index);
}
})
</script>
</body>
</html> 作者:布依前端 https://www.bilibili.com/read/cv23455123/ 出处:bilibili
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/pyfGo/canvas-graph.git
[email protected]:pyfGo/canvas-graph.git
pyfGo
canvas-graph
canvas Graph
master

搜索帮助