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、效果

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/298135.html
標籤:其他
