自定义el-slider实现进度条效果,并实现用js修改after伪元素的样式

先来看下效果图:

我是把进度条封装成组件ProgressBar.vue,由父组件传递需要展示的数组,然后循环数据实现整体效果。

其中修改slider颜色需要用到的类名为:

    /* 滑动条颜色 */
    .el-slider__bar {}

    /* 滑块颜色 */
    .el-slider__button {}

    /* 滑动轨颜色 */
    .el-slider__runway {}

由于设计要求滑块的中间会有个小圆点,如果大家需要实现的效果只有一种颜色的话,直接用css即可实现:

/* 滑块颜色 */
    .el-slider__button {
      border-color: #ff9c12;
      background: rgba(79, 49, 7, 0.48);

      &:after {
        content: '';
        display: block;
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
        width: 7px;
        height: 7px;
        border-radius: 50%;
        background: #ff9c12;
      }
    }

但因为这次的需求是按预设颜色数组循环展示,所以我需要通过js去动态渲染颜色。

但是JS无法直接获取元素的::after伪元素节点,因为它不是DOM节点。可以使用getComputedStyle方法获取元素的样式,进而获取::after伪元素的内容。所以最开始我是想通过获取.el-slider__button的伪类节点然后改变他的backgroundColor属性

let sliderButton = document.querySelector(`.slider${i} .el-slider__button`) as HTMLElement
let sliderButtonAfter = getComputedStyle(sliderButton, '::after')
sliderButtonAfter.setProperty('backgroundColor', sliderButtonColors[index])

但是结果控制台报出了如下错误,提示无法修改只读属性:

Uncaught (in promise) DOMException: Failed to execute 'setProperty' on 'CSSStyleDeclaration': These styles are computed, and therefore the 'backgroundColor' property is read-only.

只好换一种方法咯,首先可以将切换的颜色设为一个变量,然后通过修改这个变量的值实现动态渲染的效果。这时候就可以用setProperty去设置这个属性的值了,试了一下果然可以!

// 在style里定义变量,并在伪元素里绑定
--slider-color: #0000;

.el-slider__button {
   &:after {
     background: var(--slider-color);
   }
 }


// script方法执行
sliderButton.style.setProperty('--slider-color', sliderButtonColors[index])

子组件ProgressBar.vue整体代码如下:

<template>
  <div class="progress-contaioner">
    <div class="slider-demo-block" v-for="(item, index) in props.progressList" :key="item.title">
      <div class="progress-info">
        <div class="progress-tltle">{{ item.title }}</div>
        <div class="progress-proportion">
          <span>{{ item.value }}</span>
          <span class="proportion-maximums">/9000000</span>
          <span class="proportion-unit">元</span>
        </div>
      </div>
      <el-slider
        v-model="item.value"
        :class="`slider${index}`"
        :max="9000000"
        :min="0"
        :show-tooltip="false"
        disabled />
    </div>
  </div>
</template>

<script setup lang="ts">
  import { onMounted } from 'vue'

  interface IProgressBar {
    title: string
    value: number
  }

  const props = withDefaults(
    defineProps<{
      progressList: IProgressBar[]
    }>(),
    {},
  )

  // 预设滑动条颜色数组
  const sliderBarColors = [
    'linear-gradient(270deg, #FF9C12 0%, rgba(253,114,25,0.32) 100%)',
    'linear-gradient(270deg, #FFD570 0%, rgba(255,161,101,0.32) 100%)',
    'linear-gradient(270deg, #FF6600 0%, rgba(253,114,25,0.32) 100%)',
  ]

  // 预设滑块颜色数组
  const sliderButtonColors = ['#FF9C12', '#FCD270', '#F86402']

  const computedBarColor = () => {
    for (let i = 0; i < props.progressList.length; i++) {
      let sliderBar = document.querySelector(`.slider${i} .el-slider__bar`) as HTMLElement
      let sliderButton = document.querySelector(`.slider${i} .el-slider__button`) as HTMLElement
      // 循环预设颜色数组
      let index = i
      if (i >= sliderBarColors.length) {
        index = i % sliderBarColors.length
      }
      sliderBar.style.background = sliderBarColors[index]
      sliderButton.style.borderColor = sliderButtonColors[index]
      sliderButton.style.setProperty('--slider-color', sliderButtonColors[index])
    }
  }
  onMounted(() => {
    computedBarColor()
  })
</script>

<style scoped lang="less">
  .progress-contaioner {
    /*声明一个变量*/
    --slider-color: #0000;

    width: 100%;
    height: 100%;
  }

  :deep(.slider-demo-block .el-slider) {
    margin-top: 0.44rem;
    margin-bottom: 1.63rem;
    height: 0.25rem;

    /* 滑动条颜色 */
    .el-slider__bar {
      // background: linear-gradient(270deg, #ff9c12 0%, rgba(253, 114, 25, 0.32) 100%);
      border-radius: 0.13rem;
    }

    /* 滑块颜色 */
    .el-slider__button {
      // border-color: #ff9c12;
      background: rgba(79, 49, 7, 0.48);

      &:after {
        content: '';
        display: block;
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
        width: 7px;
        height: 7px;
        border-radius: 50%;
        background: var(--slider-color);
      }
    }

    /* 滑动轨颜色 */
    .el-slider__runway {
      background-color: rgba(47, 47, 47, 0.6);
    }
  }

  .progress-info {
    display: flex;
    justify-content: space-between;
    color: #ffffff;
    font-size: 0.75rem;
  }
  .proportion-maximums {
    color: rgba(255, 255, 255, 0.5);
  }
  .proportion-unit {
    font-size: 0.5rem;
    margin-left: 0.25rem;
  }
</style>

父组件调用的时候记得把数组传过去就行了:

<progress-bar :progressList="progressList"></progress-bar>

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值