svg的path创建动态饼图原理

8次阅读

共计 2390 个字符,预计需要花费 6 分钟才能阅读完成。


一共分为三个步骤:

  • 画扇形
  • 用扇形画圆
  • 做动画

下面分三个步骤详细讲解

画扇形

画扇形再前面的文章中以及提及到了,还有用到的画弧命令也在前面的文章里,不清楚的可以去看我前面的文章:svg 的 path 应用,这里再简单提及一下

  • 首先需要从圆心画一条直线到一个坐标,直线长半径

    • 这一步我们需要用到一个求坐标的函数,首先要解决的是如何求坐标。求坐标我们根据角度来求,已知半径圆心坐标,给出角度,求出坐标
                    function d2a(ang) {// 角度转弧度
                        return ang * Math.PI / 180;
                    }
                    
                    function point(ang) {// 根据角度求坐标
                        return {x: cx + r * Math.sin(d2a(ang)),
                            y: cy - r * Math.cos(d2a(ang))
                        }
                    }
  • 然后再从到达的那个坐标我们称作起始点画一条弧到另一个坐标我们称作终止点
  • 最后闭合路径就得到一个扇形

用扇形画圆

画出扇形之后我们根据有多少个数据就画多少个弧,最终画出一个圆。
我们把数据求和得到一个总数据,再把每一个数据 *360/ 总数据 = 这个数据占的角度,这个角度就可以传给上面的 point 函数求出坐标最后画出扇形,然后循环每一个数据画出对应角度的扇形在,最终画出一个圆

做动画

做动画的原理就是假设我们改变扇形半径 r 的长度,鼠标移入 r 变长,移出 r 变短。只是再变化的过程中我们不是一步到位,而是分很多步完成,例如从 150 变到 200,中间距离 50,我们一次走 1,不停的走不停的走,走到之后停止,就会有动画的效果了

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script>
        window.onload = function () {let oSvg = document.getElementById('s1');

            let cx = 400, cy = 300, r = 200,  sum = 0;// 初始圆心半径
            let data = [140, 100, 110, 90, 170,125];// 要画出扇形的数据
            data.forEach(item => {// 求数据总和
                sum += item; 
            })
            let now = 0;
            data.forEach(item => {// 画圆
                let ang = 360 * item / sum;
                pie(now, now + ang);
                now += ang;
            })
            function pie(ang1, ang2) {let oPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
                oPath.setAttribute('stroke', 'white');
                oPath.setAttribute('fill', `rgb(${Math.floor(Math.random() * 256)},${Math.floor(Math.random() * 256)},${Math.floor(Math.random() * 256)})`);
                oPath.setAttribute('stroke-width', 2);

                function calcD(r) {function d2a(ang) {// 角度转弧度
                        return ang * Math.PI / 180;
                    }
                    function point(ang) {// 根据角度求坐标
                        return {x: cx + r * Math.sin(d2a(ang)),
                            y: cy - r * Math.cos(d2a(ang))
                        }
                    }
                    // 画扇形的三个步骤                   
                    let arr = [];
                    // 第一步
                    let {x: x1, y: y1} = point(ang1);
                    arr.push(`M ${cx} ${cy} L ${x1} ${y1}`);// 画第一条线
                    // 第二步
                    let {x: x2, y: y2} = point(ang2);
                    arr.push(`A ${r} ${r} 0 ${ang2 - ang1 > 180 ? 1 : 0} 1 ${x2} ${y2}`);// 画弧
                    // 第三步
                    arr.push('Z');// 闭合

                    oPath.setAttribute('d', arr.join(' '));// 拼接字符串,执行绘画命令
                    oSvg.appendChild(oPath);// 添加到 svg 中
                }
                // 动画
                calcD(r);
                let fnNext=null;
                let curR=r;
                let size=40;

                function move(end){
                    let start=curR// 起始点
                    let dis=end-start;// 要走多远
                    let count=0;// 计步
                    
                    fnNext=function(){
                        let a=1-count/size;// 比率
                        count++;// 计步走
                        curR=start+dis*(1-a*a*a);// 当前走到哪里
                        calcD(curR);// 计算并画出来
                        if(count>=size){// 如果走到了就停止动画
                            fnNext=null;
                        }
                    }
                }
                next();// 不停的走
                function next(){fnNext&&fnNext();// 如果走到了 fnNext 函数就置空,不走了,否则继续
                    requestAnimationFrame(next);
                }

                oPath.onmouseover = function () {move(r * 1.2);// 鼠标移入扇形区域变大
                }
                oPath.onmouseout = function () {move(r);// 鼠标移出扇形区域恢复
                }
            }
        }
    </script>
</head>
<body>
    <svg id="s1" width=800 height=600></svg>
</body>
</html>

正文完
 0