canvas+js实现动态饼图效果
Posted Katle
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了canvas+js实现动态饼图效果相关的知识,希望对你有一定的参考价值。
canvas+js实现动态饼图效果
参考了一些网上的例子,利用canvas+js实现动态饼图效果demo,包括鼠标移入图块颜色变化以及带有tooltip提示。实现逻辑可分为三个步骤:
1、首先利用canvas画布原理画饼图。
2、添加鼠标移入监听事件,获取鼠标当前坐标。
3、判断当前鼠标坐标是否在所画饼图扇形区域,改变饼图颜色和显示tooltip提示。
1、画饼图
利用canvas实现一个饼图效果,我们首先需要知道canvas画布画图原理以及相关需要的API。
W3school这样介绍canvas,还不太了解的朋友可以先去熟悉熟悉。
什么是 Canvas?
html5 的 canvas 元素使用 javascript 在网页上绘制图像。
画布是一个矩形区域,您可以控制其每一像素。
canvas 拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。
创建 Canvas 元素
向 HTML5 页面添加 canvas 元素。
规定元素的 id、宽度和高度:
<canvas id="myCanvas" width="200" height="100"></canvas>
通过上面的介绍,即画图的html部分我们已经了解,只需要定义一个带有宽高及id的canvas即可,接下来便是通过js来绘制图形,首先我们需要利用id来寻找canvas元素。
let el = document.getElementById("myCanvas");
然后创建画布的绘制对象:
let cxt = el.getContext("2d");
接下来,即可利用canvas的相关API来绘制我们需要的图像,canvas的API可参考《HTML Canvas 参考手册》。本次画饼图设计需要的API整理如下:
ctx.beginPath(); // 起始一条路径,或重置当前路径
ctx.fillStyle; // 设置或返回用于填充绘画的颜色、渐变或模式 ctx.moveTo(x, y); // 把路径移动到画布中的指定点,不创建线条
ctx.arc(x, y, radius, sAngle, eAngle, false); // 创建弧/曲线(用于创建圆形或部分圆)
ctx.fill(); // 填充当前绘图(路径)
以上只列出了几个化饼图的关键API,其他包括饼图具体实现、小图标以及文字的相关API详细参见下面的代码。
2、添加鼠标监听
利用js的addEventListener,添加所绘画布鼠标监听,获取当前鼠标移入的坐标。
// 鼠标位置监听
getPosition(element) {
let mouseTimer = null;
element.addEventListener('mousemove', (e) => {
e = e || window.event;
if ( e.ofSfsetX || e.offsetX==0 ) {
this.mousePosition.x = e.offsetX;
this.mousePosition.y = e.offsetY;
} else if ( e.layerX || e.layerX==0 ){
this.mousePosition.x = e.layerX;
this.mousePosition.y = e.layerY;
}
// 监听tooltip显示
let el = document.getElementById('self-tooltip');
el.style.visibility = 'hidden';
clearTimeout(mouseTimer);
mouseTimer = setTimeout(() => {
this.drawPie(element, this.pieData);
}, 50)
});
},
3、鼠标移入,改变饼图样式
在绘制饼图方法中调用getPosition(element)方法,利用ctx.isPointInPath()API判断当前鼠标坐标是否在所绘制扇形中,以改变相关样式和显示tooltip提示。
// 监听鼠标是否移动到绘制扇形处
if (ctx.isPointInPath(this.mousePosition.x, this.mousePosition.y)) {
// 鼠标移上改变颜色
ctx.fillStyle = '#F56C6C';
// tooltip显示
let el = document.getElementById('self-tooltip');
el.innerHTML = data.labels[index] + ":" + (percent * 100).toFixed(2) + "%";
el.style.left = `${this.mousePosition.x + 15}px`;
el.style.top = `${this.mousePosition.y + 15}px`;
el.style.visibility = 'visible';
}
4、详细代码如下
<template>
<div class="box-style">
<canvas :id="canvasId" :width="width" :height="height" style="border: 1px solid #909399"></canvas>
<span id="self-tooltip" class="tooltip-text"></span>
</div>
</template>
<script>
export default {
props: {
pieData: {
type: Object,
default: function() {
return {
colors: ['#AFB4DB', '#91CC75', '#FFC333', '#FFC0CB', '#73C0DE'], // 颜色
labels: ['周一', '周二', '周三', '周四', '周五'], //标签
values: [10, 20, 30 , 40, 50], //值
radius: 100 //圆半径
};
}
},
width: {
type: Number,
default: function() {
return 400;
}
},
height: {
type: Number,
default: function() {
return 400;
}
},
canvasId: {
type: String,
default: function() {
return 'pie';
}
}
},
data() {
return {
// 鼠标移动是坐标
mousePosition: {}
}
},
mounted() {
let pieElement = document.getElementById(this.canvasId);
this.drawPie(pieElement, this.pieData);
this.getPosition(pieElement);
},
methods: {
// 画饼状图
drawPie(element, data) {
// 在画布上初始化绘图环境
let ctx = element.getContext('2d');
let drawData = data.values; // 画图数据
let sum = this.getSum(drawData); //获取绘制数据的总和
let sAngle = 0; // 扇形开始的角度
let eAngle; // 结束的角度
let x = element.width / 2;
let y = element.height / 2; //圆心坐标
let radius = data.radius; // 圆半径
let xMarker = 20; // 标记坐标
let yMarker = 20; // 标记坐标
drawData.forEach((value, index) => {
// 绘制饼图
let percent = value / sum; // 计算每个数据的占比,根据占比求扇形的弧度,即每个扇形结束的角度
eAngle = sAngle + Math.PI * 2 * (percent);
ctx.beginPath(); //新路径
ctx.fillStyle = data.colors[index];
ctx.moveTo(x, y);
ctx.arc(x, y, radius, sAngle, eAngle, false);
// 监听鼠标是否移动到绘制扇形处
if (ctx.isPointInPath(this.mousePosition.x, this.mousePosition.y)) {
// 鼠标移上改变颜色
ctx.fillStyle = '#F56C6C';
// tooltip显示
let el = document.getElementById('self-tooltip');
el.innerHTML = data.labels[index] + ":" + (percent * 100).toFixed(2) + "%";
el.style.left = `${this.mousePosition.x + 15}px`;
el.style.top = `${this.mousePosition.y + 15}px`;
el.style.visibility = 'visible';
}
// 绘制左侧标记
ctx.moveTo(xMarker, yMarker);
// 绘制矩形
ctx.fillRect(xMarker, yMarker, 30, 10);
// 绘制文字
ctx.font = 'blod 14px';
ctx.txtalgin = 'center';
ctx.textBaseline = 'top';
ctx.fillText(data.labels[index] + ":" + (percent * 100).toFixed(2) + "%", xMarker + 35, yMarker);
// 填充以上绘制
ctx.fill();
// 绘制下一个扇形时初始值改变
sAngle = eAngle;
yMarker += 20;
});
},
// 求和
getSum(data) {
let sum = 0;
data.map(value => {
sum += value;
});
return sum;
},
// 鼠标位置监听
getPosition(element) {
let mouseTimer = null;
element.addEventListener('mousemove', (e) => {
e = e || window.event;
if ( e.ofSfsetX || e.offsetX==0 ) {
this.mousePosition.x = e.offsetX;
this.mousePosition.y = e.offsetY;
} else if ( e.layerX || e.layerX==0 ){
this.mousePosition.x = e.layerX;
this.mousePosition.y = e.layerY;
}
// 监听tooltip显示
let el = document.getElementById('self-tooltip');
el.style.visibility = 'hidden';
clearTimeout(mouseTimer);
mouseTimer = setTimeout(() => {
this.drawPie(element, this.pieData);
}, 50)
});
},
}
}
</script>
<style lang="less" scoped>
.box-style {
position: relative;
display: inline-block;
width: 100%;
height: 100%;
// 画布
canvas {
cursor: pointer;
}
}
// 提示工具
.tooltip-text {
position: absolute;
width: 150px;
z-index: 10;
background-color: #909399;
color: #fff;
border-radius: 5px;
padding: 5px;
left: 0;
top: 0;
visibility: hidden;
}
</style>>
5、效果
以上是关于canvas+js实现动态饼图效果的主要内容,如果未能解决你的问题,请参考以下文章
第五周可执行代码 以及 Canvas 制作个人PSP分类饼图