js刻度形进度条控件实现

1.效果如下:

2.首先此控件采用了vue框架(懒得写原生js)和svg实现。不bb,上代码。使用方法如下。

   <EnvProgress
                            :radius="radius"
                            :value="currentShowItem.value"
                            :maxValue="currentShowItem.maxValue"
                            :minValue="currentShowItem.minValue"
                         style="left: 50%;top: 50%;width: 240px;height: 240px;position: absolute;transform: translateX(-50%) translateY(-50%)">

                    </EnvProgress>

3.准备好dom

<div id="main">
    <svg style="position: absolute;left: 100px;top: 100px;width: 200px;height: 200px" id="svg" width="100%" height="100%" version="1.1"
         xmlns="http://www.w3.org/2000/svg">
        <circle cx="100" cy="50" r="40"
                fill="red"/>
        <text x="0" :x="maxValue.x" :y="maxValue.y" :fill="maxValue.style.fill">{{maxValue.value}}</text>
        <text x="0" :x="minValue.x" :y="minValue.y" :fill="minValue.style.fill">{{minValue.value}}</text>
        <line v-for="item in lines" :x1="item.x1" :y1="item.y1" :x2="item.x2" :y2="item.y2"
              v-bind:style="{stroke:item.color}"
              style="stroke-width:2"/>

    </svg>
</div>

2.引入vue

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>

3.定义vue组件属性如下

 props: {
            //半径
            radius: {
                type: Number,
                default: 140
            },
            //最大值
            maxValue: {
                type: Number,
                default: 100
            },
            //最小值
            minValue: {
                type: Number,
                default: 0
            },
            //当前值
            value: {
                type: Number,
                default: 50
            },
            //最大最小值的文本颜色
            labelColor: {
                type: String,
                default: "#535353"
            },
            //刻度条颜色
            activeColor: {
                type: String,
                default: "#dace01"
            },
            //刻度条底色
            defaultColor: {
                type: String,
                default: "#666974"
            },
            //起始角度。采用角度制,未采用弧度制。
            startAngle: {
                type: Number,
                default: 135
            },
            //终止角度
            endAngle: {
                type: Number,
                default: 405
            },
            //间隔角度
            step: {
                type: Number,
                default: 5
            },
            //刻度的长度
            strokeWidth: {
                type: Number,
                default: 20
            }
        },

4.data属性:

     data() {
            return {
                //刻度线
                lines: [],
                //当前value对应的角度
                currentAngle: 0,
                //最大最小值的坐标
                label: {
                    x1: 0,
                    y1: 0,
                    x2: 0,
                    y2: 0
                },
                timer: null,
                isAnimating: false,
                i: 0,
                //上面的圆形指示器
                indicator: {
                    x: 0,
                    y: 0,
                    radius: 5
                }
            }
        },

5.在组件mounted钩子下初始化。

  init() {
                //起始角度
                let startAngle = this.startAngle;
                //结束角度
                let endAngle = this.endAngle;
                //步长
                let step = this.step;
                //半径
                let r1 = this.radius;
                //半径2
                let r2 = r1 - this.strokeWidth;


                //进度
                let progress = this.value / (this.maxValue - this.minValue);
                let range = endAngle - startAngle;
                let currentAngle = range * progress + startAngle;
                this.currentAngle = currentAngle;
                if (currentAngle < startAngle) {
                    currentAngle = startAngle
                }
                if (currentAngle > endAngle) {
                    currentAngle = endAngle
                }

                //指示器的x,y坐标 根据圆上的点的坐标公式
                // x=centerX + radius*cos(angle) y =centerX + radius*sin(angle) 
                this.indicator.x = (r1 + (r1 - 5) * Math.cos(currentAngle * Math.PI / 180));
                this.indicator.y = (r1 + (r1 - 5) * Math.sin(currentAngle * Math.PI / 180));

                //遍历角度,算出每条刻度线的起始坐标和终止坐标。
                for (let i = startAngle; i <= endAngle; i += step) {
                    let color = this.defaultColor;

                    if (i <= currentAngle) {
                        color = this.activeColor;
                    } else {
                        color = this.defaultColor;
                    }
                    let x = (r1 + (r1 - 11) * Math.cos(i * Math.PI / 180));
                    let y = (r1 + (r1 - 11) * Math.sin(i * Math.PI / 180));

                    let x2 = (r1 + (r2 - 11) * Math.cos(i * Math.PI / 180));
                    let y2 = (r1 + (r2 - 11) * Math.sin(i * Math.PI / 180));

                    this.lines.push({
                        angle: i,
                        x1: x,
                        y1: y,
                        x2: x2,
                        y2: y2,
                        color: color
                    })
                }
                this.label.x1 = (r1 + r1 * Math.cos(startAngle * Math.PI / 180));
                this.label.y1 = (r1 + r1 * Math.sin(startAngle * Math.PI / 180)) + 20;

                this.label.x2 = (r1 + r1 * Math.cos(endAngle * Math.PI / 180)) - 20;
                this.label.y2 = (r1 + r1 * Math.sin(endAngle * Math.PI / 180)) + 20;


            }

6.动画效果。观察value值变化的时候执行。

   startAnimate() {


                this.isAnimating = true;
                this.lines.forEach(v => {
                    v.color = this.defaultColor;
                });
                let self = this;
                self.i = 0;

                this.indicator.x = (this.radius + (self.radius - 5) * Math.cos(this.startAngle * Math.PI / 180));
                this.indicator.y = (this.radius + (self.radius - 5) * Math.sin(this.startAngle * Math.PI / 180));


                self.lines[self.i].color = self.activeColor;
                if(this.timer){
                    clearInterval(this.timer);
                }
                this.timer = setInterval(function () {

                    self.i++;
                    if (self.lines.length == self.i) {
                        self.isAnimating = false;
                        self.i = 0;
                        self.clearIn();
                        return
                    } else {
                        if (self.lines[self.i].angle <= self.currentAngle) {
                            self.indicator.x = (self.radius + (self.radius - 5) * Math.cos(self.lines[self.i].angle * Math.PI / 180));
                            self.indicator.y = (self.radius + (self.radius - 5) * Math.sin(self.lines[self.i].angle * Math.PI / 180));
                            self.lines[self.i].color = self.activeColor
                        } else {
                            self.lines[self.i].color = self.defaultColor
                        }
                    }

                }, 100)
            },

7.最后完整版代码如下。

<template>
    <svg style="" v-bind:style="{width:radius*2+'px',height:radius*2+'px'}" id="svg" width="100%" height="100%"
         version="1.1"
         xmlns="http://www.w3.org/2000/svg">
        <circle :cx="indicator.x" :cy="indicator.y" :r="indicator.radius"
                :fill="activeColor"/>
        <text x="0" :x="label.x1" :y="label.y1" :fill="labelColor">{{minValue}}</text>
        <text x="0" :x="label.x2" :y="label.y2" :fill="labelColor">{{maxValue}}</text>
        <line v-for="item in lines" :x1="item.x1" :y1="item.y1" :x2="item.x2" :y2="item.y2"
              v-bind:style="{stroke:item.color}"
              style="stroke-width:2"/>

    </svg>
</template>

<script>

    export default {
        name: 'EnvProgress',
        props: {
            radius: {
                type: Number,
                default: 140
            },
            maxValue: {
                type: Number,
                default: 100
            },
            minValue: {
                type: Number,
                default: 0
            },
            value: {
                type: Number,
                default: 50
            },
            labelColor: {
                type: String,
                default: "#535353"
            },
            activeColor: {
                type: String,
                default: "#dace01"
            },
            defaultColor: {
                type: String,
                default: "#666974"
            },
            startAngle: {
                type: Number,
                default: 135
            },
            endAngle: {
                type: Number,
                default: 405
            },
            step: {
                type: Number,
                default: 5
            },
            strokeWidth: {
                type: Number,
                default: 20
            }
        },
        watch: {
            value(newValue) {
                console.log("newValue=" + newValue);

                newValue = parseInt(newValue);
                let progress = newValue / (this.maxValue - this.minValue);
                let range = this.endAngle - this.startAngle;
                let currentAngle = range * progress + this.startAngle;
                this.currentAngle = currentAngle;
                if (currentAngle < this.startAngle) {
                    currentAngle = this.startAngle
                }
                if (currentAngle > this.endAngle) {
                    currentAngle = this.endAngle
                }
//                console.log('value 变化了.oldvalue='+oldValue+",newValue="+newValue);
                this.startAnimate();
            }
        },
        methods: {


            startAnimate() {


                this.isAnimating = true;
                this.lines.forEach(v => {
                    v.color = this.defaultColor;
                });
                let self = this;
                self.i = 0;

                this.indicator.x = (this.radius + (self.radius - 5) * Math.cos(this.startAngle * Math.PI / 180));
                this.indicator.y = (this.radius + (self.radius - 5) * Math.sin(this.startAngle * Math.PI / 180));


                self.lines[self.i].color = self.activeColor;
                if(this.timer){
                    clearInterval(this.timer);
                }
                this.timer = setInterval(function () {

                    self.i++;
                    if (self.lines.length == self.i) {
                        self.isAnimating = false;
                        self.i = 0;
                        self.clearIn();
                        return
                    } else {
                        if (self.lines[self.i].angle <= self.currentAngle) {
                            self.indicator.x = (self.radius + (self.radius - 5) * Math.cos(self.lines[self.i].angle * Math.PI / 180));
                            self.indicator.y = (self.radius + (self.radius - 5) * Math.sin(self.lines[self.i].angle * Math.PI / 180));
                            self.lines[self.i].color = self.activeColor
                        } else {
                            self.lines[self.i].color = self.defaultColor
                        }
                    }

                }, 100)
            },
            clearIn() {
                console.log("clear");
                clearInterval(this.timer);
            },
            init() {
                //起始角度
                let startAngle = this.startAngle;
                //结束角度
                let endAngle = this.endAngle;
                //步长
                let step = this.step;
                //半径
                let r1 = this.radius;
                //半径2
                let r2 = r1 - this.strokeWidth;


                //进度
                let progress = this.value / (this.maxValue - this.minValue);
                let range = endAngle - startAngle;
                let currentAngle = range * progress + startAngle;
                this.currentAngle = currentAngle;
                if (currentAngle < startAngle) {
                    currentAngle = startAngle
                }
                if (currentAngle > endAngle) {
                    currentAngle = endAngle
                }

                this.indicator.x = (r1 + (r1 - 5) * Math.cos(currentAngle * Math.PI / 180));
                this.indicator.y = (r1 + (r1 - 5) * Math.sin(currentAngle * Math.PI / 180));

                for (let i = startAngle; i <= endAngle; i += step) {
                    let color = this.defaultColor;

                    if (i <= currentAngle) {
                        color = this.activeColor;
                    } else {
                        color = this.defaultColor;
                    }
                    let x = (r1 + (r1 - 11) * Math.cos(i * Math.PI / 180));
                    let y = (r1 + (r1 - 11) * Math.sin(i * Math.PI / 180));

                    let x2 = (r1 + (r2 - 11) * Math.cos(i * Math.PI / 180));
                    let y2 = (r1 + (r2 - 11) * Math.sin(i * Math.PI / 180));

                    this.lines.push({
                        angle: i,
                        x1: x,
                        y1: y,
                        x2: x2,
                        y2: y2,
                        color: color
                    })
                }
                this.label.x1 = (r1 + r1 * Math.cos(startAngle * Math.PI / 180));
                this.label.y1 = (r1 + r1 * Math.sin(startAngle * Math.PI / 180)) + 20;

                this.label.x2 = (r1 + r1 * Math.cos(endAngle * Math.PI / 180)) - 20;
                this.label.y2 = (r1 + r1 * Math.sin(endAngle * Math.PI / 180)) + 20;


            }
        },
        data() {
            return {
                lines: [],
                currentAngle: 0,
                label: {
                    x1: 0,
                    y1: 0,
                    x2: 0,
                    y2: 0
                },
                timer: null,
                isAnimating: false,
                i: 0,
                indicator: {
                    x: 0,
                    y: 0,
                    radius: 5
                }
            }
        },
        mounted() {
            this.init();
        },
        beforeDestroy() {
            if (this.timer) {
                clearInterval(this.timer);
            }
        }
    }
</script>

<style scoped lang="less">

</style>

猜你喜欢

转载自blog.csdn.net/u012282382/article/details/83687959