浅用一下svg标签

982 阅读1分钟

背景

在业务需求中,遇到了上述的开发需求环形进度条,如上所示,在实际开发中,环形进度条也比较常见。使用组件库的会偏多。

本来打算用border-radius实现,会有两个问题

1.圆圈的切口是圆状的

2.不是整个圆

就不是很合适。

思路

然后搜了一下,发现了svg标签中的circle

svg标签中还有其他的图形,svg标签,几种图形基本使用

step1: 先一个创建svg画布,绘制一个简单的圆形。作为环形进度条的基础。

step2: 两个圆圈 ,打底的圆圈,外边的渐变颜色

step3: 利用circle属性

strokeLinecap="round" // 切面为圆

strokeDasharray="400,134" //实现空白

strokeDashoffset="-134" //偏移位置缺口

实现

  <svg width="174" height="174" className="circle-progress">

        <circle

          cx="87" // 距离父亲的横向坐标系

          cy="87" // 距离父亲的纵向坐标系

          r="85" // 半径

          fill="none" // 填充

          strokeLinecap="round" //切面为圆

          strokeWidth="4" // 边的宽度

          stroke="#F4E8DC" // 颜色

          strokeDasharray="400,134"

          strokeDashoffset="-134" //偏移的角度

        />

        <circle

          className={clsx(

            `progress-${progressNum.split('%')[0]}`,

          )}

          cx="87"

          cy="87"

          r="85"

          fill="none"

          strokeLinecap="round"

          strokeWidth="4"

          strokeDasharray="400,133"

          strokeDashoffset="-134"  //偏移的角度

        />

      </svg>

storke-dasharray

cx,cy,r

由于是固定几个百分比,又不知道怎么向css传参,又想有动效,然后我的代码就每写一个百分比就写对应百分比的动效className

.circle-progress-wrap {

  margin: auto;

  width: 245px;

  margin-top: 24px;

  height: 245px;

  position: relative;

  background: url('~@/assets/img/progress_bg@2x.png') no-repeat top/contain;



  .circle-progress-num {

    position: absolute;

    top: 78px;

    width: 100%;

    margin: auto;

    .circle-progress-label {

      color: var(--hll-color-black-40);

    }

    .circle-progress-value {

      font-weight: 700;

      color: var(--hll-color-black-90);

      margin-top: 4px;

      line-height: 53px;

      height: 53px;

      font-size: 40px;

    }

  }

  .circle-progress {

    margin-top: 35px;

    transform: rotate(44deg);

  }



  .progress {

    animation: circleProgress 2s;

    animation-iteration-count: 1;

    animation-fill-mode: forwards;

  }

  @keyframes circleProgress {

    0% {

      stroke-dashoffset: 500;

      stroke: #ffe19d;

    }

    100% {

      stroke-dashoffset: 150;

      stroke: #c4955b;

    }

  }



  .progress-33 {

    animation: circleProgress33 2s;

    animation-iteration-count: 1;

    animation-fill-mode: forwards;

  }

  @keyframes circleProgress33 {

    0% {

      stroke-dasharray: 0, 400;

      stroke: #ffe19d;

    }

    100% {

      stroke-dasharray: 132, 268;

      stroke: #c4955b;

    }

  }



  .progress-50 {

    animation: circleProgress50 2s;

    animation-iteration-count: 1;

    animation-fill-mode: forwards;

  }

  @keyframes circleProgress50 {

    0% {

      stroke-dasharray: 0, 534;

      stroke: #ffe19d;

    }

    100% {

      // stroke-dashoffset: 340;

      stroke-dasharray: 200, 334;

      stroke: #c4955b;

    }

  }

最终章

然后我想不会吧。组件库里边也是我这样?

mobile.ant.design/zh/componen…

antd-mobile circleProgress

看高亮部分。其他的可不看。

import React, { CSSProperties, FC } from 'react'



const classPrefix = `adm-progress-circle`



export type ProgressCircleProps = {

  percent?: number

  children?: React.ReactNode

} & NativeProps<'--size' | '--track-width' | '--track-color' | '--fill-color'>



export const ProgressCircle: FC<ProgressCircleProps> = p => {

  const props = mergeProps({ percent: 0 }, p)

**  const style: CSSProperties & Record<'--percent', string> = {

    '--percent': props.percent.toString(),

  } //这个是重点**

  return 

    <div className={`${classPrefix}`} style={style}>

      <div className={`${classPrefix}-content`}>

        <svg className={`${classPrefix}-svg`}>

          <circle className={`${classPrefix}-track`} fill='transparent' />

          <circle className={`${classPrefix}-fill`} fill='transparent' />

        </svg>

        <div className={`${classPrefix}-info`}>{props.children}</div>

      </div>

    </div>

  )

}
@class-prefix-progress-circle: ~'adm-progress-circle';



/*进度圈*/

.@{class-prefix-progress-circle} {

  --track-width: var(--adm-progress-circle-track-width, 3px); //支持传参使用,默认xxx

  --size: var(--adm-progress-circle-size, 50px);

  --track-color: var(--adm-progress-circle-track-color, #e5e5e5);

  --fill-color: var(--adm-progress-circle-fill-color, var(--adm-color-primary));



  --percent: 0;

  --pi: 3.14159265358979;

  --radius: calc(var(--size) / 2 - var(--track-width) / 2);

  --circumference: calc(var(--radius) * var(--pi) * 2);



  display: inline-block;

  width: var(--size);

  height: var(--size);



  &-svg {

    width: 100%;

    height: 100%;

    > .@{class-prefix-progress-circle}-track,

    .@{class-prefix-progress-circle}-fill {

      stroke-width: var(--track-width);

      r: var(--radius);

      cx: calc(var(--size) / 2);

      cy: calc(var(--size) / 2);

      transform: rotate(-90deg);

      transform-origin: 50% 50%;

    }

    > .@{class-prefix-progress-circle}-track {

      stroke: var(--track-color);

    }

    > .@{class-prefix-progress-circle}-fill {

      transition: stroke-dashoffset 0.35s;//动效用得transition 

      stroke: var(--fill-color);

      stroke-dasharray: var(--circumference);

   **   stroke-dashoffset: calc(

        var(--circumference) * (1 - var(--percent) / 100)

      ); //这个--percent是从style的属性传参进来的**

      stroke-linecap: round;

    }

  }



  &-content {

    position: relative;

    margin: auto;

    width: 100%;

    height: 100%;

  }



  &-info {

    position: absolute;

    width: 100%;

    top: 50%;

    left: 50%;

    text-align: center;

    transform: translate(-50%, -50%);

  }

}

发现直接通过传入的percent,然后传入css属性,通过calc来算的。优秀了

后续

然后敲了几个 demo ,放在了codesandbox上了,进行对比。demo1,是我自己的初版,demo2,是antd-mobile的源码,demo3是最终版

存在问题

calc不太聪明的样子,有些组合运算算不明白?

颜色渐变,可能还需要通过进度具体数值计算,然后渐变。 这个渐变的,我现在解决啦。

<circle

className={`${classPrefix}-fill`}

stroke="url(#linearGradient)" //这里是重点

fill="transparent"

/>

<defs>

<linearGradient

    x1="16%"

    y1="30.9878035%"

    x2="96.9769965%"

    y2="100%"

    id="linearGradient" //这里是重点

   >

    <stop stopColor="#c4955b" offset="0%"></stop>

    <stop stopColor="#c4955b" offset="52.7095376%"></stop>

    <stop stopColor="#ffe19d" offset="100%"></stop>

 </linearGradient>

</defs>

最终的效果就

5.jpg