linear-gradient实现圆环进度条

时间:2021-1-8 作者:admin

前言

最近项目有用到圆环进度条,所以也找了一些文章,看看是如何实现的。市面上的文章讲述的实现方式基本都是两个长方形旋转和canvas这两种方式,大家如果有兴趣可以自己去找找,百度一搜一大把?。

然而我做的项目是小程序(提供demo是vue版本),canvas层级非常高,虽然官方提供了同层canvas,但是同层canvas在官方社区上反馈bug非常多,而且感觉比较重,所以就没考虑。两个长方形旋转的方式我之前也试过,真机上两个长方形交接处会存在差不多1px的间距,一直无法修复,也没法和设计说只能这样是不是。

然后无意之中看到了这样一个demo,感觉打开了新世界,原来圆环进度条还能这样实现啊!!

linear-gradient实现圆环

先看下先看下我们要实现的效果

上面的图可以拆分为4个dom结构,或许这样看你更清晰

首先我们先实现最底部深红色的圆

<div class="progress-radial"></div>
.progress-radial {
    position: relative;
    width: 120rpx;
    height: 120rpx;
    border-radius: 50%;
    background-image: linear-gradient(90deg, #C40006 %, #FECC1C);
    line-height: normal;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1;
}

这时我们会得到这样一个圆

background-image: linear-gradient(90deg, #C40006 50%, #FECC1C 50%, #FECC1C);

这时我们就可以得到一个两种颜色对半分并且没有过渡色的圆

<div class="progress-radial">
    <div class="progress-text"></div>
</div>
.progress-radial {
    position: relative;
    width: 120rpx;
    height: 120rpx;
    border-radius: 50%;
    background-image: linear-gradient(90deg, #C40006 50%, #FECC1C 50%, #FECC1C);
    line-height: normal;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1;
}

.progress-radial .progress-text {
    width: 88rpx;
    height: 88rpx;
    background: #ED1937;
    border-radius: 50%;
}

那我们该怎么让进度条动起来那

假设我们先动一下 linear-gradient 的 angle ,让 angle = 145deg 会得到下面这样的情况

答案肯定是可以的。我们将这个圆想象成上下两个部分,当进度小余50%的时候,上下两部分如下图所示
图一

.progress-radial {
    position: relative;
    width: 120px;
    height: 120px;
    border-radius: 50%;
    background-image: linear-gradient(90deg, #C40006 50%, transparent 50%, transparent) , linear-gradient(90deg, #FECC1C 50%, #C40006 50%, #C40006);
    line-height: normal;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1;
    margin: 0 auto
}

然后我们修改 图二 部分的 linear-gradient 的 angle 达到修改进度的效果,比如我们试试将的 angle 改为120deg,其实我们可以想像成把上面 图二 转动了30度后得到以下效果

.progress-radial {
    position: relative;
    width: 120px;
    height: 120px;
    border-radius: 50%;
    background-image: linear-gradient(90deg, #C40006 50%, transparent 50%, transparent) , linear-gradient(120deg, #FECC1C 50%, #C40006 50%, #C40006);
    line-height: normal;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1;
    margin: 0 auto
}

.progress-radial {
    position: relative;
    width: 120px;
    height: 120px;
    border-radius: 50%;
    background-image: linear-gradient(-90deg, #FECC1C 50%, transparent 50%, transparent) , linear-gradient(270deg, #FECC1C 50%, #C40006 50%, #C40006);
    line-height: normal;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1;
    margin: 0 auto
}

然后我们可以修改 图一 部分的 linear-gradient 的 angle 达到修改进度的效果,比如我们试试将的 angle 改为-60deg,可以想像把上面 图一 转动了30度后得到以下效果

<template>
  <div id="app">
    <div class="progress-radial" :style="point <= 50 ? 'background-image: linear-gradient(90deg, #C40006 50%, transparent 50%, transparent), linear-gradient('+deg+'deg, #FECC1C 50%, #C40006 50%, #C40006);' : 'background-image: linear-gradient('+deg+'deg, #FECC1C 50%, transparent 50%, transparent), linear-gradient(270deg, #FECC1C 50%, #C40006 50%, #C40006)'">
      <div class="progress-text"></div>
    </div>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      deg: 0,
      point: 0,
    }
  },
  mounted() {
    // 完成的数量,整体的数量
    let { finishCount, totalCount } = {
      finishCount: 1,
      totalCount: 3,
    };
    // 完成数量的百分比,保留2位小数
    let point = Math.floor((finishCount / totalCount) * 10000) / 100;
    let deg = 0;
    // 360度,按百分比每分3.6度,小于50%以90deg为起始,大于50%以-90deg为起始
    if (point <= 50) {
      deg = Math.round(90 + point * 3.6);
    } else {
      deg = Math.round(-90 + (point - 50) * 3.6);
    }
    this.deg = deg
    this.point = point
  },
};

这时我们就完成了修改 finishCount 修改进度的效果

transform-origin实现头尾圆弧

头尾圆弧也就上上面我们拆分的时候说的黑色圆,起始圆永远都位于最上方,所以只要定位好就可以,结束圆需要计算真实的进度,然后旋转到对于位置。
如何去转这个黑色的小圆,这里我们是用了 transform-origin ,也就是将这个黑色圆的旋转基点定到红色圆的圆心,然后用 rotate 就可以得到一个绕圆环边缘转的黑色小圆了,然后上面我们计算得到的 deg 大于50和小于50的的起始基点不一样,所以我们也需要对这个数值做一下处理。
最终代码和效果图如下

<template>
  <div id="app">
    <input v-model="finishCount" @change="draw" type="number" class="input" />
    <div
      class="progress-radial"
      :style="
        point <= 50
          ? 'background-image: linear-gradient(90deg, #C40006 50%, transparent 50%, transparent), linear-gradient(' +
            deg +
            'deg, #FECC1C 50%, #C40006 50%, #C40006);'
          : 'background-image: linear-gradient(' +
            deg +
            'deg, #FECC1C 50%, transparent 50%, transparent), linear-gradient(270deg, #FECC1C 50%, #C40006 50%, #C40006)'
      "
    >
      <div class="progress-text"></div>
      <div
        class="progress-radial-end"
        :style="'transform: rotate('+(point <= 50 ? deg-90 : 270+deg)+'deg)'"
      ></div>
      <div class="progress-radial-start"></div>
    </div>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      finishCount: 1,
      totalCount: 3,
      deg: 0,
      point: 0,
    };
  },
  mounted() {
    this.draw();
  },
  methods: {
    draw() {
      let point =
        Math.floor((this.finishCount / this.totalCount) * 10000) / 100;
      let deg = 0;
      // 360度,按百分比每分3.6度
      if (point <= 50) {
        deg = Math.round(90 + point * 3.6);
      } else {
        deg = Math.round(-90 + (point - 50) * 3.6);
      }
      this.deg = deg;
      this.point = point;
    },
  },
};
</script>

<style>
.input {
  margin: 30px auto;
  display: block;
}

.progress-radial {
  position: relative;
  width: 120px;
  height: 120px;
  border-radius: 50%;
  background-image: linear-gradient(
      90deg,
      #c40006 50%,
      transparent 50%,
      transparent
    ),
    linear-gradient(90deg, #fecc1c 50%, #c40006 50%, #c40006);
  line-height: normal;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
  margin: 0 auto;
}

.progress-radial-end,
.progress-radial-start {
  width: 16px;
  height: 16px;
  top: 0;
  left: 52rpx;
  border-radius: 50%;
  background: #000;
  position: absolute;
  z-index: 999;
  line-height: normal;
  transform-origin: center 60px;
}

.progress-radial .progress-text {
  width: 88px;
  height: 88px;
  background: #ed1937;
  border-radius: 50%;
  color: #fff;
  font-size: 28px;
  text-align: center;
  line-height: 88px;
  font-weight: bold;
}
</style>

参考地址文献和demo地址

**

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。