</JavaScript>环形进度条插件(v2)

之前写的 cpbBuilder.js 完全是觉得好玩儿,没有实际使用,上个星期自己在项目上使用了一些,觉得有些东西用起来并不是很舒坦,主要就是每个进度条的设置,要统一在 JS 中去设置,这样在修改的时候不是很直观,不过好处是可以使用 JS 配合 JSON,第二个版本在 canvas 标签中使用了 data-* 属性,也就是图个方便,至于具体使用哪个版本可以自己根据需要选择,代码是完全开源的,可以随便用,随便改。

王世杰:纯JS环形进度条生成插件

插件代码:

// cpbBuilder.js
// 环形进度条生成插件
;
(function (global, undefined) {
    "use strict" // 使用js严格模式检查,使语法更规范
    var _global;
    // 定义一些默认参数
    // 参数优先级顺序为 canvas 元素 [data-*] 属性 > 插件实例化参数 > 插件默认参数
    var _options = {
        // true 以百分比显示 || false 以原始值显示
        showPercent: true,
        // 显示值前缀
        prefix: '',
        // 显示值后缀,如果以原始值显示可以为数字添加单位符号后缀
        suffix: '',
        // 数字格式化选项,默认保留小数点后2位
        fiexd: 2,
        // 计算百分比是的分母
        total: 100,
        // 动画选项,使用每帧增加的百分比调整动画速度,默认 2.5 即每帧增加 2.5%,设置为 0 或 false 关闭动画
        animation: 2.5,
        // 旋转,只旋转进度条,文本不旋转
        rotate: 0,
        // 反向,顺时针还是逆时针
        reverse: false,
        // 样式,不可通过 canvas 元素 [data-*] 属性设置
        style: {
            // 线宽
            circleWidth: 10,
            // 进度条颜色
            circleColor: "#10aeff",
            // 进度条背景颜色,设置为 false 时不显示
            circleBackground: '#eee',
            // 值文本颜色
            valueColor: "#777",
            // 标签文本颜色
            lableColor: "#777",
            // 字体,不能设置文字大小
            font: "'Helvetica Neue', Helvetica, Arial, 'PingFang SC', 'Hiragino Sans GB', 'Heiti SC', 'Microsoft YaHei', 'WenQuanYi Micro Hei', sans-serif",
            // 进度条渐变设置,设置渐变以后会使 circleColor 选项失效
            gradient: false
        }
    }
    // api 实例
    var apiInstance;
    // 定义 api
    var _plugin_api = function (options) {
        // api 实例化,可以在 api 以外的函数中调用 api
        apiInstance = this;
        // 合并默认参数
        this.options = mergeObj(options, _options)

        // 绘制函数
        this.draw = function (canvas) {
            // 如果输入参数是字符串,则默认为 canvasID 使用 ID 选择器获取画布
            if (typeof canvas == "string") {
                // 获取画布
                canvas = document.getElementById(canvas);
            }
            // 获取 context
            var context = canvas.getContext("2d");
            let animation = getData('animation', context)
            if (!animation) {
                // 静态绘制
                drawArcBg(context); // 绘制进度条背景
                drawLabel(context); // 绘制标签
                drawArc(context); // 绘制进度条
                drawText(context);
            } else {
                // 使用动画绘制
                // 设置自增变量,百分比
                var increment = 0;
                (function drawAnimation() {
                    window.requestAnimationFrame(drawAnimation, canvas);
                    context.clearRect(0, 0, canvas.width, canvas.height);
                    drawArcBg(context); // 绘制进度条背景
                    drawLabel(context); // 绘制标签
                    drawArc(context, increment);
                    // 绘制值文本时需要将自增变量百分比逆向转换为绝对值
                    drawText(context, getAbsolute(increment, context));
                    if (increment < getPercent(canvas.dataset.value, context)) {
                        increment += getData('animation', context);
                    } else {
                        increment = getPercent(canvas.dataset.value, context);
                    }
                })();
            }
            return this; //返回当前方法
        }
    }
    // 绘制环行条
    function drawArc(context, value = undefined) {
        // 设置图形中心
        var center = getCenter(context);
        // 设置环形半径 
        var radius = getRadius(context);
        // 设置开始角度
        var startAngle = (-0.5 + getData('rotate', context) / 180) * Math.PI;
        if (value === undefined) {
            value = getData('value', context)
        }
        // 设置结束角度
        var endAngle = (getPercent(value, context) / 50 - 0.5 + getData('rotate', context) / 180) * Math.PI;
        context.save();
        context.beginPath();
        // 创建圆
        context.arc(center, center, radius, startAngle, endAngle, getData('reverse', context));
        // 设置线宽
        context.lineWidth = apiInstance.options.style.circleWidth;
        // 使用渐变
        if (!apiInstance.options.style.gradient) {
            context.strokeStyle = apiInstance.options.style.circleColor;
        } else {
            var gradientDirection = {
                x1: 0,
                y1: 0,
                x2: radius * 2,
                y2: 0
            }
            switch (apiInstance.options.style.gradient.direction) {
                case 1:
                    gradientDirection.x2 = 0;
                    gradientDirection.y2 = radius * 2;
                    break;
                case 2:
                    gradientDirection.y2 = radius * 2;
                    break;
                case 3:
                    gradientDirection.y1 = radius * 2;
                    break;
            }
            var gradient = context.createLinearGradient(gradientDirection.x1, gradientDirection.y1, gradientDirection.x2, gradientDirection.y2);
            var clolor = apiInstance.options.style.gradient.color;
            for (var index in clolor) {
                gradient.addColorStop(index, clolor[index]);
            }
            context.strokeStyle = gradient;
        }
        context.stroke();
        context.restore();
        return apiInstance
    }
    // 绘制环形背景
    function drawArcBg(context) {
        if (apiInstance.options.style.circleBackground) {
            // 设置图形中心
            var center = getCenter(context);
            // 设置环形半径 
            var radius = getRadius(context);
            context.beginPath();
            // 创建圆
            context.arc(center, center, radius, 0, 2 * Math.PI);
            // 设置线宽
            context.lineWidth = apiInstance.options.style.circleWidth;
            context.strokeStyle = apiInstance.options.style.circleBackground;
            context.stroke();
        }
        return apiInstance
    }
    // 绘制值文本
    function drawText(context, value = undefined) {
        // 设置图形中心
        var center = getCenter(context);
        // 设置环形半径 
        var radius = getRadius(context);
        if (value === undefined) {
            value = getData('value', context)
        }
        // 设置值显示文本
        var valStr;
        let showPercent = getData('showPercent', context)
        if (showPercent) {
            valStr = getData('prefix', context) + String(Number(value).toFixed(getData('fixed', context))) + '%'
        } else {
            valStr = getData('prefix', context) + String(Number(value).toFixed(getData('fixed', context))) + getData('suffix', context);
        }
        context.save();
        context.font = radius / 2.25 + "px " + apiInstance.options.style.font;
        context.textAlign = "center";
        context.textBaseline = "middle";
        context.fillStyle = apiInstance.options.style.valueColor;
        context.fillText(valStr, center, center, radius * 1.75);
        context.restore();
        return apiInstance
    }
    // 绘制标签
    function drawLabel(context) {
        // 设置图形中心
        let center = getCenter(context)
        // 设置环形半径 
        let radius = getRadius(context)
        let label = context.canvas.dataset.label
        // 显示标签文本
        if (label) {
            context.font = radius / 3.5 + "px " + apiInstance.options.style.font;
            context.textAlign = "center";
            context.textBaseline = "middle";
            context.fillStyle = apiInstance.options.style.lableColor;
            context.fillText(label, center, center + radius / 2.5, radius);
        }
        return apiInstance
    }
    // 获取 canvas 半径
    function getRadius(context) {
        return (context.canvas.width - apiInstance.options.style.circleWidth) / 2;
    }
    // 获取 canvas 中心坐标
    function getCenter(context) {
        return context.canvas.width / 2;
    }
    // 获取百分比值
    function getPercent(value, context) {
        let total = getData('total', context)
        return value / total * 100;
    }
    // 获取绝对值
    function getAbsolute(percent, context) {
        let total = getData('total', context)
        return percent * total / 100;
    }
    // 
    function getData(dataName, context) {
        let data = context.canvas.dataset[dataName]
        if (typeof data === "undefined") {
            data = apiInstance.options[dataName]
        }
        return data
    }
    // 合并对象,支持无限深度,用来合并 options
    function mergeObj(obj, original) {
        for (var item in obj) {
            // 如果新的值和原始值类型都是 object,则使用 mergeObj 进行合并
            if (typeof obj[item] === 'object' && typeof original[item] === 'object') {
                mergeObj(obj[item], original[item])
            } else {
                original[item] = obj[item]
            }
        }
        return original
    }

    // 将插件对象暴露给全局对象
    _global = (function () {
        return this || (0, eval)('this');
    }());
    if (typeof module !== "undefined" && module.exports) {
        module.exports = _plugin_api;
    } else if (typeof define === "function" && define.amd) {
        define(function () {
            return _plugin_api;
        });
    } else {
        // 最后的插件名称在这里定义
        var _plugin_name = 'cpbBuilder';
        !(_plugin_name in _global) && (_global[_plugin_name] = _plugin_api);
    }
}());// cpbBuilder.js

使用:

<!-- 创建 canvas 元素 -->
<canvas id="myCanvas" width="150" data-value="85" data-prefix="&ge;" data-label="指标名称">
    您的浏览器不支持 HTML5 canvas 标签。
</canvas>

插件实例化和设置:

// usage
// 实例化 cpbBuilder.js 插件
var progressBar = new cpbBuilder({
    // true 以百分比显示 || false 以原始值显示
    showPercent: true,
    // 显示值前缀
    prefix: '',
    // 显示值后缀,如果以原始值显示可以为数字添加单位符号后缀
    suffix: '',
    // 数字格式化选项,默认保留小数点后2位
    fiexd: 2,
    // 计算百分比是的分母
    total: 100,
    // 动画选项,使用每帧增加的百分比调整动画速度,默认 2.5 即每帧增加 2.5%,设置为 0 或 false 关闭动画
    animation: 2.5,
    // 旋转,只旋转进度条,文本不旋转
    rotate: 0,
    // 反向,顺时针还是逆时针
    reverse: false,
    // 样式,不可通过 canvas 元素 [data-*] 属性设置
    style: {
        // 线宽
        circleWidth: 5,
        // 进度条颜色
        circleColor: "#10aeff",
        // 进度条背景颜色,设置为 false 时不显示
        circleBackground: '#eee',
        // 值文本颜色
        valueColor: "#777",
        // 标签文本颜色
        lableColor: "#777",
        // 字体,不能设置文字大小
        font: "'Helvetica Neue', Helvetica, Arial, 'PingFang SC', 'Hiragino Sans GB', 'Heiti SC', 'Microsoft YaHei', 'WenQuanYi Micro Hei', sans-serif",
        // 进度条渐变设置,设置渐变以后会使 circleColor 选项失效
        gradient: {
            // 渐变方向,默认左右;1 - 上下;2 - 左上右下;3 - 左下右上
            direction: 2,
            // 渐变颜色,设置位置 0 至 1 的不同颜色节点
            color: {
                "0": "#10aeff",
                "1": "#6ad2a5"
            }
        }
    }
});

插件运行:

// 因为参数是在元素 data-* 属性中设置的,因此可以遍历元素运行
var canvas = document.getElementsByTagName("canvas")
for (item in canvas) {
    if (typeof canvas[item] === "object") {
        // 调用 draw() 方法绘制,每个进度条都将使用相同的配置
        progressBar.draw(canvas[item]);
    }
}

v2-3623b3fdfb91d72f9d57d0d0231fee8d_b.jpg

更多用法请自由发挥!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值