先绘制饼状图,代码:
<!DOCTYPE html>
<html>
<head>
<title></title>
<script>
function d2a(n){//弧度转角度
return n * Math.PI / 180;
}
window.onload = function(){
let oSvg = document.getElementById('svg')
let cx = 400,cy = 300,r = 200;//圆心半径
let data = [800,600,1200,3500,2800,500];
let sum = 0;//数据总和
data.forEach(item => sum += item);
let now = 0;//扇形在弧上的起点
data.forEach(item => {
let ang = 360 * item / sum;
//随机分配颜色
//通过rgba随机改变三原色0-255
//let color = `rgba(${Math.floor(Math.random() * 256)},${Math.floor(Math.random() * 256)},${Math.floor(Math.random() * 256)})`
//通过颜色十六进制,#ffffff = 16777216
let color = Math.floor(Math.random() * 16777216).toString(16);//转成16进制
while(color.length < 6){//如果颜色位数不足六位,补上0
color = '0' + color;
}
pie(now,now + ang,'#' + color);
now += ang;//下一个扇形在弧上的起点
})
pie(10,30,'red')
function pie(ang1,ang2,color){//用两个角度确定扇形
//svg不属于html标签,需要用createElementNS配合域来添加
let oP = document.createElementNS('http://www.w3.org/2000/svg','path');
oP.style.fill = color;
oSvg.appendChild(oP);
let arr = []
function getPoint(ang){//获取扇形在弧上的点
return {
x:cx + Math.sin(d2a(ang)) * r,
y:cy - Math.cos(d2a(ang)) * r
}
}
//绘制扇形的第一条弦
let {x : x1, y : y1} = getPoint(ang1);//获取扇形在弧上的起点
arr.push(`M ${cx} ${cy} L ${x1} ${y1}`)//M => moveto,L => lineto
//绘制弧,使用A,A = elliptical Arc
/* A rx ry x-axis-rotation large-arc-flag sweep-flag x y
* rx ry 是椭圆的两个半轴的长度。
* x-axis-rotation 是椭圆相对于坐标系的旋转角度,角度数而非弧度数。
* large-arc-flag 是标记绘制大弧(1)还是小弧(0)部分。
* sweep-flag 是标记向顺时针(1)还是逆时针(0)方向绘制。
* x y 是圆弧终点的坐标
*/
let {x : x2, y : y2} = getPoint(ang2);//获取扇形在弧上的终点
arr.push(`A ${r} ${r} 0 ${ang2 - ang1 >= 180 ? 1 : 0} 1 ${x2} ${y2}`)
//连接圆心和终点
arr.push('Z')
oP.setAttribute('d',arr.join(' '))
}
}
</script>
</head>
<body>
<svg width="800" height="600" id="svg"></svg>
</body>
</html>
效果:
修改pie(),实现鼠标移入对应扇形凸出效果
function pie(ang1,ang2,color){//用两个角度确定扇形
//svg不属于html标签,需要用createElementNS配合域来添加
let oP = document.createElementNS('http://www.w3.org/2000/svg','path');
oP.style.fill = color;
oSvg.appendChild(oP);
calcD(r)
function calcD(r){//画扇形函数
let arr = []
function getPoint(ang){//获取扇形在弧上的点
return {
x:cx + Math.sin(d2a(ang)) * r,
y:cy - Math.cos(d2a(ang)) * r
}
}
//绘制扇形的第一条弦
let {x : x1, y : y1} = getPoint(ang1);//获取扇形在弧上的起点
arr.push(`M ${cx} ${cy} L ${x1} ${y1}`)//M => moveto,L => lineto
//绘制弧,使用A,A = elliptical Arc
/* A rx ry x-axis-rotation large-arc-flag sweep-flag x y
* rx ry 是椭圆的两个半轴的长度。
* x-axis-rotation 是椭圆相对于坐标系的旋转角度,角度数而非弧度数。
* large-arc-flag 是标记绘制大弧(1)还是小弧(0)部分。
* sweep-flag 是标记向顺时针(1)还是逆时针(0)方向绘制。
* x y 是圆弧终点的坐标
*/
let {x : x2, y : y2} = getPoint(ang2);//获取扇形在弧上的终点
arr.push(`A ${r} ${r} 0 ${ang2 - ang1 >= 180 ? 1 : 0} 1 ${x2} ${y2}`)
//连接圆心和终点
arr.push('Z')
oP.setAttribute('d',arr.join(' '))
}
let curR = r;//初始半径长度
let fnNext = null;//动画内容函数
let size = 40;//需走的总步数
function move(end){//行动函数
let start = curR;//起点
let dis = end - start;//起点到终点的距离
let count = 0;//已走的步数
fnNext = function(){
count++;
let a = 1 - count / size;//可以视为速度
curR = start + dis * (1 - a * a * a);//当前半径长度
calcD(curR);//绘制图形
if(count >= size){//走完就停止
fnNext = null;
}
}
}
//鼠标移入
oP.onmouseover = function(){
move(r * 1.25);
}
//鼠标离开
oP.onmouseout = function(){
move(r);
}
next()
//动画执行函数
function next(){
fnNext && fnNext();
requestAnimationFrame(next);
}
}
效果: