美文网首页
Canvas绘制带标签导引线的环形图

Canvas绘制带标签导引线的环形图

作者: Odeng | 来源:发表于2019-07-25 10:48 被阅读0次

    运行效果

    line-pie.png

    实例代码

    <!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>CirclePie</title>
        <style>
            html {
                background-color: black;
            }
    
            .pie {
                width: 300px;
                height: 300px;
                border: 1px solid red;
                margin: 100px auto;
            }
        </style>
    </head>
    
    <body>
        <div class="pie"></div>
    </body>
    <script>
        function CirclePie(options) {
            this.x = options.x || 0;
            this.y = options.y || 0;
            this.r = options.r || 50;
            this.width = 100;
            this.height = 100;
            this.ctx = null;
            //初始鼠标位置
            this.pos = {
                x: 0,
                y: 0
            };
        }
    
        CirclePie.prototype.init = function (dom) {
            if (!dom) {
                console.log('canvas挂载节点不存在');
                return;
            }
            var canvas = document.createElement("canvas");
            this.width = dom.offsetWidth;
            this.height = dom.offsetHeight;
            canvas.width = this.width;
            canvas.height = this.height;
            this.ctx = canvas.getContext("2d");
            //添加canvas节点
            dom.appendChild(canvas);
            //定点圆心
            this.ctx.translate(this.width / 2, this.height / 2);
            var _this = this;
    
            //绑定事件
            canvas.addEventListener('mousemove', function (event) {
                _this.pos = _this.getEventPosition(event);
                _this.draw(_this.options);
            }); 
        };
    
        CirclePie.prototype.getEventPosition = function (event) {
            var x, y;
            if (event.layerX || event.layerX == 0) {
                x = event.layerX;
                y = event.layerY;
            } else if (event.offsetX || event.offsetX == 0) { // Opera
                x = event.offsetX;
                y = event.offsetY;
            }
            return {
                x: x,
                y: y
            };
        }
    
        CirclePie.prototype.ring = function (x, y, r, start, end, color) {
            this.ctx.save();
            this.ctx.beginPath();
            this.ctx.strokeStyle = color;
            this.ctx.lineWidth = 10;
            this.ctx.arc(x, y, r, start, end);
            
            // this.ctx.closePath();
            if (this.ctx.isPointInPath(this.pos.x, this.pos.y)) {
                this.ctx.stroke();
                this.ctx.beginPath();
                if (this.pos.x > this.width / 2) {
                    x = x + 10 * Math.sin(end - start);
                } else {
                    x = x - 10 * Math.sin(end - start);
                }
                if (this.pos.y > this.height / 2) {
                    y = y + 10 * Math.cos(end - start);
                } else {
                    y = y - 10 * Math.cos(end - start);
                }
                this.ctx.arc(x, y, r, start, end);
                this.ctx.stroke();
            } else {
                this.ctx.stroke();
            }
             this.ctx.closePath();
            this.ctx.restore();
        };
        CirclePie.prototype.drawGuidLine = function (
            x0,
            y0,
            radius,
            startAngle,
            angle,
            color,
            text
        ) {
            this.ctx.beginPath();
            this.ctx.save();
            let out = 10;
            let cAngle = (startAngle + angle) / 2;
            let x = x0 + (radius + 2) * Math.cos(cAngle);
            let y = y0 + (radius + 2) * Math.sin(cAngle);
            let x2 = x0 + (radius + 15) * Math.cos(cAngle);
            let y2 = y0 + (radius + 15) * Math.sin(cAngle);
            this.ctx.moveTo(x, y);
            this.ctx.strokeStyle = color;
            this.ctx.lineTo(x2, y2);
            this.ctx.textBaseline = "middle";
            this.ctx.textAlign = "center";
            //默认字体大小
            this.ctx.font = "14px Microsoft Yahei";
            this.ctx.fillStyle = color;
            if (x2 > x) {
                x2 = x2 + 20;
                this.ctx.lineTo(x2, y2);
                this.ctx.fillText(text, x2 + 20, y2);
            } else {
                x2 = x2 - 20;
                this.ctx.lineTo(x2, y2);
                this.ctx.fillText(text, x2 - 20, y2);
            }
            if (this.ctx.isPointInPath(this.pos.x, this.pos.y)) {
                alert('line in path');
            } 
            this.ctx.stroke();
            // this.ctx.beginPath();
            this.ctx.restore();
        };
    
        //绘制文本
        CirclePie.prototype.drawText = function (x, y, color, fontSize, text) {
            this.ctx.save();
            this.ctx.fillStyle = color;
            this.ctx.textBaseline = "middle";
            this.ctx.textAlign = "center";
            this.ctx.font = `${fontSize}px Microsoft Yahei`;
            if (this.ctx.isPointInPath(this.pos.x, this.pos.y)) {
                alert('text in path');
            } 
            this.ctx.fillText(text, x, y);
            this.ctx.restore();
        };
        CirclePie.prototype.clearLable = function () {
            this.ctx.clearRect(-50, -25, 100, 100);
        };
    
        CirclePie.prototype.clear = function () {
            this.ctx.clearRect(
                -this.width / 2,
                -this.height / 2,
                this.width,
                this.height
            );
        };
    
        CirclePie.prototype.draw = function (options, offsetX, offsetY, index) {
            //清空上一次绘制
            this.clear();
            var data = options.data;
            var sum = 0;
            var map = {};
            //求总数
            data.forEach((element, index) => {
                sum += element.value;
                map[element.name] = element.value;
            });
    
            let i = 0;
            let startAngle = (Math.PI / 180) * 0;
            let endAngle = 0;
            let offset = 0;
    
            // this.ctx.beginPath();
    
    
            for (let key in map) {
                offset = (((map[key] / sum) * Math.PI) / 180) * 360;
                endAngle = startAngle + offset;
                //画扇区
                this.ring(0, 0, this.r, startAngle, endAngle, options.colors[i]);
                
                //话导引线
                this.drawGuidLine(0, 0, this.r, startAngle, endAngle, options.colors[i], key);
    
                startAngle = endAngle;
                i++;
            }
            this.drawText(0, -15, "rgba(255, 255, 255, 1)", 12, options.name);
            this.drawText(0, 15, "rgba(255, 255, 255, 1)", 30, sum);
        };
    
        CirclePie.prototype.render = function (options) {
    
            this.options = options;
    
            //中间名称
            this.name = options.name;
            //颜色值
            this.colors = options.colors;
            //初始化数据
            if (!this.data) {
                this.data = JSON.parse(JSON.stringify(options.data));
                this.data.forEach((item, index) => {
                    item.value = 0;
                });
            }
    
            let _this = this;
    
            this.timer = window.requestAnimationFrame(function () {
                let data = [];
                let j = 0;
                for (let i = 0; i < _this.data.length; i++) {
                    if (_this.data[i].value > options.data[i].value) {
                        _this.data[i].value -= 1;
                    } else if (_this.data[i].value == options.data[i].value) {
                        j++;
                        if (j >= _this.data.length) {
                            window.cancelAnimationFrame(_this.timer);
                            return;
                        }
                    } else {
                        _this.data[i].value += 1;
                    }
                }
                _this.draw({
                    name: options.name,
                    colors: options.colors,
                    data: _this.data
                });
                _this.render(options);
            });
        };
    
        window.onload = function () {
    
            var colors = ["rgba(239, 58, 113, 1)", "rgba(95, 222, 145, 1)"];
            var circlePie = new CirclePie({
                x: 0,
                y: 0,
                r: 50
            });
            circlePie.init(document.getElementsByClassName('pie')[0]);
            circlePie.render({
                name: "名称",
                colors: [
                    "red",
                    "green",
                    "skyblue"
                ],
                data: [
                    {
                        name: "扇区1",
                        value: 20//Math.floor(Math.random() * 100)
                    },
                    {
                        name: "扇区2",
                        value: 30//Math.floor(Math.random() * 100)
                    },
                    {
                        name: "扇区3",
                        value: 50//Math.floor(Math.random() * 100)
                    }
                ]
            });
        }
    </script>
    
    </html>
    

    相关文章

      网友评论

          本文标题:Canvas绘制带标签导引线的环形图

          本文链接:https://www.haomeiwen.com/subject/jdmcrctx.html