插件窝 干货文章 vue+css如何实现圆环渐变仪表盘

vue+css如何实现圆环渐变仪表盘

class div background transform 539    来源:    2024-10-30

整体效果

主要原理

渐变圆环

设置如下css代码可实现出环形渐变效果

.box{
    background:conic-gradient(from 0deg at 50% 50%,#eee 0deg,yelllow 180deg,red 360deg)
}
  • 效果图

添加圆角以及内部圆形居中遮挡即可实现圆环效果

  • 效果图

仪表盘效果

添加若干个环绕圆点旋转的元素,组成仪表盘,融合背景色将底色圆环覆盖掉,就得到了仪表盘效果。

1.div结构

<div class="scale">
    <div
         class="s-item"
         v-for="item in 72"
         :key="'item-' + item"
         >
    </div>
</div>

2.scss

.scale {
    border-radius: 50%;
    width: 100%;
    height: 100%;
    position: absolute;
    .s-item {
        position: absolute;
        width: 1px;
        height: 2px;
        background-color: #fff;
        left:50%;
        top: 0px;
        transform-origin: 0 24px;
    }
    @for $i from 0 through 72 {
        .s-item:nth-child(#{$i + 1}) {
            transform: rotate($i * 5deg);
        }
    }
}
  • 形式图

  • 效果图

全部代码

<template>
  <div
    class="gauge"
    :style="{
      width: diameter + 'px',
      height: diameter + 'px',
      background: bgColor,
    }"
  >
    <div class="cricle" ref="cricle">
      <!-- 色块圈 -->
      <div
        class="s-color"
        :style="
          follow
            ? {
                background: `conic-gradient(from 0deg at 50% 50%,${
                  rampColor[0]
                } 0deg,${rampColor[1]} ${value / 2}deg,${
                  rampColor[2]
                } ${value}deg,${defaultColor} ${value}deg)`,
              }
            : {
                background: `conic-gradient(from 0deg at 50% 50%,${rampColor[0]} 0deg,${rampColor[1]} 50%,${rampColor[2]}`,
              }
        "
      >
        <div
          class="follow"
          v-if="!follow"
          :style="{
            background: `conic-gradient(from 0deg at 50% 50%,transparent 0deg,transparent ${value}deg,${defaultColor} ${value}deg)`,
          }"
        ></div>
        <div
          class="mask"
          :style="{
            width: `calc(100% - ${dotHeight}px *2)`,
            height: `calc(100% - ${dotHeight}px *2)`,
            background: bgColor,
          }"
        ></div>
      </div>
      <!-- 拖动按钮 -->
      <div
        class="slider"
        :style="{
          transform: `rotate(${value}deg)`,
          width: sliderDiameter + 'px',
          height: `calc(50% - ${dotHeight / 2}px + ${sliderDiameter / 2}px)`,
          left: `calc(50% - ${sliderDiameter / 2}px)`,
          top: `calc((${sliderDiameter / 2}px - ${dotHeight / 2}px) *-1)`,
        }"
      >
        <div
          class="btn"
          ref="slider"
          :style="{
            width: sliderDiameter + 'px',
            height: sliderDiameter + 'px',
            background: sliderColor,
          }"
        ></div>
      </div>
      <!-- 仪表盘 -->
      <div class="bar scale">
        <div
          class="s-item"
          v-for="item in dotCount"
          :key="'item-' + item"
          :style="{
            'transform-origin': `2px calc(${diameter}px/2 + 1px)`,
            width: dotWidth + 'px',
            height: diameter / 2 + 'px',
            transform: `rotate(${(item * 360) / dotCount}deg)`,
            background: bgColor,
          }"
        ></div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: "gauge",
/** 组件参数  */
  props: {
    /** 默认值 */
    default: {
      type: Number,
      default: 0,
    },
    /** 直径 */
    diameter: {
      type: Number,
      default: 200,
    },
    /** 点间隔 */
    dotWidth: {
      type: Number,
      default: 4,
    },
    /** 点高度 */
    dotHeight: {
      type: Number,
      default: 8,
    },
    /** 滑块直径 */
    sliderDiameter: {
      type: Number,
      default: 20,
    },
    /** 滑块颜色 */
    sliderColor: {
      type: String,
      default: "red",
    },
    /** 点数量 */
    dotCount: {
      type: Number,
      default: 72,
    },
    /** 渐变色 */
    rampColor: {
      type: Array,
      default: function () {
        return ["#ddd", "#faba2a", "#f24c4f"];
      },
    },
    /** 环形默认颜色 */
    defaultColor: {
      type: String,
      default: "#ddd",
    },
    /** 背景颜色 */
    bgColor: {
      type: String,
      default: "#fff",
    },
    /** 渐变跟随 */
    follow: {
      type: Boolean,
      default: true,
    },
  },
  data() {
    return {
      value: 0,
    };
  },
  methods: {
    /** ## 组件方法 */
    /** 获取进度 */
    getValue(){
      return this.value/360;
    }
  },
  created() {
    this.value = this.default;
  },
  mounted() {
    /** 滑块滑动功能 */
    this.$refs.slider.onmousedown = (e) => {
      const cricle = this.$refs.cricle;
      /** 获取位置(坐标轴原点) */
      let client = cricle.getBoundingClientRect();
      let x0 = client.x + cricle.offsetWidth / 2;
      let y0 = client.y + cricle.offsetHeight / 2;
      /** 阻止默认事件 */
      let ev = e || window.event;
      ev.preventDefault ? ev.preventDefault() : (ev.returnValue = false);
      /** 鼠标移动 */
      document.onmousemove = (el) => {
        let move = el ? el : window.event;
        /** 鼠标位置 */
        let x = move.x - x0;
        let y = y0 - move.y;
        /** 计算角度 */
        let deg = (Math.atan(y / x) / 2 / Math.PI) * 360;
        if (x >= 0) {
          if (this.value >= 270) {
            /** 象限跳跃优化 */
            this.value = 360;
          } else {
            this.value = 90 - deg;
          }
        } else {
          if (this.value <= 90) {
            this.value = 0;
          } else {
            this.value = 270 - deg;
          }
        }
      };
      /** 鼠标松开 */
      document.onmouseup = () => {
        /** 取消订阅鼠标移动事件 */
        document.onmousemove = null;
        document.onmouseup = null;
      };
    };
  },
};
</script>
<style lang="scss" scoped>
.box {
  width: 100%;
  height: 100%;
  position: absolute;
}
.gauge {
  padding: 5px;
  //background-color: white;
  position: relative;
  .cricle {
    width: calc(100% - 10px);
    height: calc(100% - 10px);
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translateY(-50%) translate(-50%);
    .slider {
      //background-color: pink;
      width: 20px;
      height: calc(50% + 6px);
      position: absolute;
      z-index: 10;
      overflow: hidden;
      transform-origin: 50% 100%;
      top: -6px;
      transform: rotate(140deg);
      left: calc(50% - 10px);
      .btn {
        position: absolute;
        cursor: pointer;
        top: 0px;
        width: 20px;
        height: 20px;
        // background-color: red;
        border-radius: 50%;
      }
    }
    .s-color {
      z-index: 5; //5
      position: absolute;
      width: 100%;
      height: 100%;
      border-radius: 50%;
      .follow{
        @extend .box;
        // position: absolute;
        // width: calc(100% + 2px );
        // height: calc(100% + 2px);
        left:50%;
        top:50%;
        transform: translate(-50%,-50%);
        z-index: 6;
        border-radius: 50%;
      }
      .mask {
        content: "";
        display: block;
        position: absolute;
        //background-color: #fff;
        z-index: 9;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
        border-radius: 50%;
      }
    }
    .bar {
      transform: rotate(0deg);
      transform-origin: 0 100px;
    }
    .scale {
      border-radius: 50%;
      width: calc(100% + 2px);
      height: calc(100% + 2px);
      position: absolute;
      z-index: 9;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      .s-item {
        position: absolute;
        //background-color: #fff;
        left: calc(50% - 2px);
        top: 0px;
      }
      //   @for $i from 0 through 72 {
      //     .s-item:nth-child(#{$i + 1}) {
      //       transform: rotate($i * 5deg);
      //     }
      //   }
    }
  }
}
</style>

总结

以上为个人经验,希望对您有所帮助。