Browse Source

fix: circleprogress

Drjnigfubo 3 years ago
parent
commit
86b55d315d

+ 23 - 27
src/packages/__VUE/circleprogress/demo.vue

@@ -2,24 +2,29 @@
   <div class="demo full">
     <h2>基础用法</h2>
     <div class="demo__piece">
-      <nut-circleprogress :progress="10"> </nut-circleprogress>
+      <nut-circleprogress :progress="20"> </nut-circleprogress>
     </div>
-
-    <h2>环形进度条自定义样式</h2>
+    <h2>环形进度条自定义宽度</h2>
     <div class="demo__piece">
-      <nut-circleprogress :progress="50" :progress-option="progressOption"> </nut-circleprogress>
+      <nut-circleprogress :progress="50" strokeWidth="10"> </nut-circleprogress>
     </div>
 
+    <h2>环形进度条自定义颜色(支持渐变色)</h2>
+    <div class="demo__piece">
+      <nut-circleprogress :progress="50" color="red" />
+      <nut-circleprogress :progress="100" :color="gradientColor" />
+    </div>
+    <h2>环形进度条自定义大小</h2>
+    <div class="demo__piece">
+      <nut-circleprogress :progress="50" radius="60"></nut-circleprogress>
+    </div>
     <h2>环形进度条自定义内容</h2>
     <div class="demo__piece">
-      <nut-circleprogress :progress="50" :is-auto="isAuto">
-        <div>自定义</div>
-      </nut-circleprogress>
+      <nut-circleprogress :progress="50" radius="60">自定义</nut-circleprogress>
     </div>
     <h2>动态改变环形进度条的进度</h2>
     <div class="demo__piece">
-      <nut-circleprogress :progress="percent" :progress-option="progressOption" :stroke-inner-width="strokeInnerWidth">
-      </nut-circleprogress>
+      <nut-circleprogress :progress="percent"> </nut-circleprogress>
     </div>
     <div class="demo__btn">
       <nut-button type="primary" @click="setReduceVal">减少</nut-button>
@@ -27,25 +32,18 @@
     </div>
   </div>
 </template>
-
 <script lang="ts">
-import { reactive, ref } from 'vue';
+import { ref } from 'vue';
 import { createComponent } from '../../utils/create';
 const { createDemo } = createComponent('circleprogress');
 export default createDemo({
-  props: {},
   setup() {
-    const progressOption = reactive({
-      radius: 50,
-      strokeOutWidth: 10,
-      backColor: '#d9d9d9',
-      progressColor: 'red'
-    });
-    const percent = ref(50);
-    const strokeInnerWidth = ref(10);
-    const isAuto = ref(true);
+    const gradientColor = {
+      '0%': '#FF5E5E',
+      '100%': '#FFA062'
+    };
+    const percent = ref(30);
     const setAddVal = () => {
-      strokeInnerWidth.value = 10;
       if (percent.value >= 100) {
         return;
       }
@@ -53,19 +51,16 @@ export default createDemo({
     };
     const setReduceVal = () => {
       if (percent.value - 10 <= 0) {
-        strokeInnerWidth.value = 0;
         percent.value = 0;
         return;
       }
       percent.value -= 10;
     };
     return {
-      progressOption,
-      strokeInnerWidth,
-      isAuto,
       setAddVal,
       setReduceVal,
-      percent
+      percent,
+      gradientColor
     };
   }
 });
@@ -88,5 +83,6 @@ export default createDemo({
   display: flex;
   justify-content: center;
   background: rgba(255, 255, 255, 1);
+  padding: 10px 0;
 }
 </style>

+ 58 - 50
src/packages/__VUE/circleprogress/doc.md

@@ -17,74 +17,85 @@ const app = createApp();
 app.use(CircleProgress);
 
 ```
-
-
 ### 基础用法
 :::demo
 ```html
 <template>
     <nut-cell>
-    <nut-circleprogress :progress="10"> </nut-circleprogress>
+     <nut-circleprogress :progress="20"> </nut-circleprogress>
    </nut-cell>
 </template>
 ```
 :::
-### 环形进度条自定义样式
+
+### 环形进度条自定义宽度
 :::demo
 ```html
 <template>
-  <nut-cell>
-    <nut-circleprogress :progress="50" :progress-option="progressOption"> </nut-circleprogress>
-  </nut-cell>
+    <nut-cell>
+     <nut-circleprogress :progress="50" strokeWidth="10"> </nut-circleprogress>
+   </nut-cell>
+</template>
+```
+:::
+
+
+### 环形进度条自定义颜色(支持渐变色)
+:::demo
+```html
+<template>
+    <nut-cell>
+    <nut-circleprogress :progress="50" color="red" />
+      <nut-circleprogress :progress="100" :color="gradientColor" />
+   </nut-cell>
 </template>
 <script>
-import { reactive, ref } from 'vue';
-export default {
+import { ref } from 'vue';
+export default{
   setup() {
-    const progressOption = reactive({
-      radius: 50,
-      strokeOutWidth: 10,
-      backColor: '#d9d9d9',
-      progressColor: 'red'
-    });
+     const gradientColor = {
+      '0%': '#FF5E5E',
+      '100%': '#FFA062'
+    };
     return {
-      progressOption,
+      gradientColor
     };
   }
 }
 </script>
 ```
 :::
+
+### 环形进度条自定义大小
+:::demo
+```html
+<template>
+    <nut-cell>
+     <nut-circleprogress :progress="50" radius="60"></nut-circleprogress>
+    </nut-cell>
+</template>
+```
+:::
+
+
+
 ### 环形进度条自定义内容
 :::demo
 ```html
 <template>
-  <nut-cell>
-     <nut-circleprogress :progress="50" :is-auto="isAuto">
-       <div>自定义</div>
-    </nut-circleprogress>
-  </nut-cell>
+    <nut-cell>
+     <nut-circleprogress :progress="50" radius="60">自定义</nut-circleprogress>
+    </nut-cell>
 </template>
-<script>
-import { reactive, ref } from 'vue';
-export default {
-  setup() {
-    const isAuto = ref(true);
-    return {
-      isAuto,
-    };
-  }
-}
-</script>
 ```
 :::
+
 ### 动态改变环形进度条的进度
 :::demo
 ```html
 <template>
   <div>
-    <nut-circleprogress :progress="percent" :progress-option="progressOption" :stroke-inner-width="strokeInnerWidth">
-    </nut-circleprogress>
+     <nut-circleprogress :progress="percent"></nut-circleprogress>
   </div>
   <div>
     <nut-button type="primary" @click="setReduceVal">减少</nut-button>
@@ -95,17 +106,8 @@ export default {
 import { reactive, ref } from 'vue';
 export default {
   setup() {
-    const progressOption = reactive({
-      radius: 50,
-      strokeOutWidth: 10,
-      backColor: '#d9d9d9',
-      progressColor: 'red'
-    });
-    const percent = ref(50);
-    const strokeInnerWidth = ref(10);
-    const isAuto = ref(true);
+    const percent = ref(30);
     const setAddVal = () => {
-      strokeInnerWidth.value = 10;
       if (percent.value >= 100) {
         return;
       }
@@ -113,15 +115,12 @@ export default {
     };
     const setReduceVal = () => {
       if (percent.value - 10 <= 0) {
-        strokeInnerWidth.value = 0;
         percent.value = 0;
         return;
       }
       percent.value -= 10;
     };
     return {
-      progressOption,
-      isAuto,
       setAddVal,
       setReduceVal,
       percent
@@ -138,6 +137,15 @@ export default {
 | 字段 | 说明 | 类型 | 默认值
 |----- | ----- | ----- | -----
 | progress | 百分比 | Number,String | 必传项,无默认值
-| stroke-inner-width | 圆弧的宽度 | Number,String | 10
-| is-auto | 是否自定义内容显示 | Boolean | false
-| progress-option | 外圆相关参数对象,其中包括半径,宽度,背景颜色,进度色值 | Object | {radius: 50,strokeOutWidth: 10, backColor: '#d9d9d9',progressColor: 'red'}
+| stroke-width | 圆弧的宽度 | Number,String | 5
+| radius | 半径 | Number,String | 50
+| color | 圆环进度条颜色 | Number,String | '#fa2c19'
+| path-color | 圆环轨道颜色| String | '#d9d9d9'
+| stroke-linecap | 圆环进度条端点形状可选值为 square butt| String | 'round'
+| clockwise| 是否顺时针展示| Boolean | true
+## Slots
+
+| 字段 | 说明 | 
+|----- | ----- |
+| default | 自定义文字内容| 
+

+ 12 - 53
src/packages/__VUE/circleprogress/index.scss

@@ -1,65 +1,24 @@
 .nut-circleprogress {
   position: relative;
 
-  .nut-circleprogress-content {
-    position: absolute;
-    top: 50%;
-    left: 50%;
-    transform: translate(-50%, -50%);
-  }
-  .nut-circleprogress__right {
-    width: 50%;
-    height: 100%;
-    position: relative;
-    overflow: hidden;
-    float: right;
-  }
-  .nut-circleprogress__left {
-    width: 50%;
-    height: 100%;
-    position: relative;
-    overflow: hidden;
-    float: left;
+  &-hover {
+    stroke: $circle-progress-primary-color;
+    transition: stroke-dasharray 0.6s ease 0s, stroke 0.6s ease 0s;
   }
 
-  .nut-circleprogress__line {
-    position: absolute;
-    width: 100%;
-    height: 100%;
-    top: 0;
-    overflow: hidden;
-    &.nut-circleprogress__l {
-      top: 0px;
-      transform: rotate(0deg);
-      left: 0px;
-    }
-
-    &.nut-circleprogress__r {
-      top: 0px;
-      transform: rotate(180deg);
-      right: 0px;
-    }
+  &-path {
+    stroke: $circle-progress-path-color;
   }
 
-  .nut-circleprogress__progress {
+  &-text {
     position: absolute;
     top: 50%;
-    left: 50%;
-    transform: translate(-50%, -50%);
-    font-size: 16px;
-    font-family: PingFangSC;
-    font-weight: normal;
-    color: rgba(29, 29, 33, 1);
-  }
-
-  .nut-circleprogress__line__c {
-    width: 200%;
-    height: 100%;
-    border: 10px solid transparent;
-    border-radius: 50%;
-    position: absolute;
+    left: 0;
     box-sizing: border-box;
-    top: 0;
-    transform: rotate(-45deg);
+    width: 100%;
+    transform: translateY(-50%);
+    text-align: center;
+    color: $circle-progress-text-color;
+    font-size: $circle-progress-text-size;
   }
 }

+ 133 - 168
src/packages/__VUE/circleprogress/index.taro.vue

@@ -1,205 +1,170 @@
 <template>
-  <div :class="classes" :style="pieStyle">
-    <div :style="mobileStyle" v-if="!isMobile">
-      <div class="nut-circleprogress__right">
-        <div class="nut-circleprogress__line nut-circleprogress__r">
-          <div class="nut-circleprogress__line__c" :style="RightStyle"></div>
-        </div>
-      </div>
-      <div class="nut-circleprogress__progress">
-        <template v-if="!isAuto">
-          <slot>{{ progress }}%</slot>
-        </template>
-        <template v-else><slot></slot></template>
-      </div>
-
-      <div class="nut-circleprogress__left">
-        <div class="nut-circleprogress__line nut-circleprogress__l">
-          <div class="nut-circleprogress__line__c" :style="LeftStyle"></div>
-        </div>
-      </div>
-    </div>
-    <div v-else>
-      <svg :height="option.size" :width="option.size" x-mlns="http://www.w3.org/200/svg">
-        <circle
-          :r="option.radius"
-          :cx="option.cx"
-          :cy="option.cy"
-          :stroke="option.backColor"
-          :stroke-width="option.strokeOutWidth"
-          fill="none"
-        />
-        <circle
-          :r="option.radius"
-          :cx="option.cx"
-          :cy="option.cy"
-          :stroke="option.progressColor"
-          :stroke-dasharray="arcLength"
-          :stroke-width="strokeInnerWidth"
-          fill="none"
-          :transform="option.startPosition"
-          stroke-linecap="round"
-          style="transition: stroke-dasharray 0.6s ease 0s, stroke 0.6s ease 0s"
-        />
-      </svg>
-      <div class="nut-circleprogress__progress">
-        <template v-if="!isAuto">
-          <slot>{{ progress }}%</slot>
-        </template>
-        <template v-else><slot></slot></template>
-      </div>
+  <div :class="classes" :style="{ height: radius * 2 + 'px', width: radius * 2 + 'px' }">
+    <view :style="style"></view>
+    <div class="nut-circleprogress-text">
+      <slot></slot>
+      <span v-if="!slotDefault">{{ progress }}%</span>
     </div>
   </div>
 </template>
 
 <script lang="ts">
-import Taro from '@tarojs/taro';
-import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue';
+import { computed, ref, watch, useSlots } from 'vue';
 import { createComponent } from '../../utils/create';
 const { componentName, create } = createComponent('circleprogress');
-
-interface progressOption {
-  radius: string | number;
-  strokeOutWidth: string | number;
-  backColor: string;
-  progressColor: string;
+interface Item {
+  key?: string;
+  value?: string;
 }
-
 export default create({
   props: {
     progress: {
       type: [Number, String],
       required: true
     },
-    strokeInnerWidth: {
+    strokeWidth: {
+      type: [Number, String],
+      default: 5
+    },
+    radius: {
       type: [Number, String],
-      default: 10
+      default: 50
     },
-    isAuto: {
-      tyep: Boolean,
-      default: false
+    strokeLinecap: {
+      type: String,
+      default: 'round'
     },
-    progressOption: {
-      type: Object,
-      default: () => {}
+    color: {
+      type: [String, Object],
+      default: '#FF673E'
+    },
+    pathColor: {
+      type: String,
+      default: '#d9d9d9'
+    },
+    clockwise: {
+      type: Boolean,
+      default: true
     }
   },
+  emits: ['update:progress'],
   setup(props, { emit }) {
-    const rotateLeft = ref();
-    const rotateRight = ref();
-    const InnerWidth = ref(props.strokeInnerWidth);
-    const isMobile = ref(false);
-    const cricleData = reactive({
-      radius: 50,
-      strokeOutWidth: 10,
-      backColor: '#d9d9d9',
-      progressColor: 'red'
-    });
-    const loadPercent = (x: number, y: number) => {
-      let rotate = (x / y) * 360;
-      let rotateRc = 0;
-      let rotateLc = 0;
-      if (rotate < 180) {
-        rotateRc = rotate + -45;
-      } else {
-        rotateRc = 135;
-        rotateLc = rotate - 180 - 45;
-        rotateLeft.value = rotateLc;
-      }
-      rotateRight.value = rotateRc;
-    };
-    watch(
-      () => props.progress,
-      (value) => {
-        loadPercent(value as number, 100);
-      }
-    );
-    onMounted(() => {
-      if (Taro.getEnv() === 'WEB') {
-        isMobile.value = true;
-      } else {
-        isMobile.value = false;
-        loadPercent(props.progress as number, 100);
-        Object.assign(cricleData, props.progressOption as progressOption);
-      }
-    });
+    const slotDefault = !!useSlots().default;
     const classes = computed(() => {
       const prefixCls = componentName;
       return {
         [prefixCls]: true
       };
     });
-    const pieStyle = computed(() => {
-      return {
-        width: (cricleData.radius + cricleData.strokeOutWidth) * 2 + 'px',
-        height: (cricleData.radius + cricleData.strokeOutWidth) * 2 + 'px'
-      };
-    });
-    const mobileStyle = computed(() => {
+    const currentRate = ref(props.progress);
+    const refRandomId = Math.random().toString(36).slice(-8);
+    const isObject = (val: unknown): val is Record<any, any> => val !== null && typeof val === 'object';
+
+    const transColor = (color: string | undefined) => {
+      return color && color.replace('#', '%23');
+    };
+    const stop = () => {
+      if (!isObject(props.color)) {
+        return [];
+      }
+      let color = props.color;
+      const colorArr = Object.keys(color).sort((a, b) => parseFloat(a) - parseFloat(b));
+
+      let stopArr: object[] = [];
+      colorArr.map((item) => {
+        let obj = {
+          key: '',
+          value: ''
+        };
+        obj.key = item;
+        obj.value = color[item];
+        stopArr.push(obj);
+      });
+      return stopArr;
+    };
+
+    const style = computed(() => {
+      let { strokeWidth } = props;
+
+      let stopArr: Array<object> = stop();
+      let stopDom: string[] = [];
+      if (stopArr) {
+        stopArr.map((item: Item) => {
+          let obj = '';
+          obj = `%3Cstop offset='${item.key}' stop-color='${transColor(item.value)}'/%3E`;
+          stopDom.push(obj);
+        });
+      }
+      let perimeter = 283;
+      let progress = +currentRate.value;
+      let offset = (perimeter * Number(format(parseFloat(progress.toFixed(1))))) / 100;
+      const isWise = props.clockwise ? 1 : 0;
+      const color = isObject(props.color) ? `url(%23${refRandomId})` : transColor(props.color);
+      let d = `M 50 50 m -45 0 a 45 45 0 1 ${isWise} 90 0  a 45 45 0 1 ${isWise} -90 0`;
+      const pa = `%3Cdefs%3E%3ClinearGradient id='${refRandomId}' x1='100%25' y1='0%25' x2='0%25' y2='0%25'%3E${stopDom}%3C/linearGradient%3E%3C/defs%3E`;
+      const path = `%3Cpath d='${d}' stroke-width='${strokeWidth}' stroke='${transColor(
+        props.pathColor
+      )}' fill='none'/%3E`;
+      const path1 = `%3Cpath d='${d}' stroke-width='${strokeWidth}' stroke-dasharray='${offset},${perimeter}' stroke-linecap='round' stroke='${color}' fill='none'/%3E`;
+
       return {
+        background: `url("data:image/svg+xml,%3Csvg viewBox='0 0 100 100'  xmlns='http://www.w3.org/2000/svg'%3E${pa}${path}${path1}%3C/svg%3E")`,
         width: '100%',
-        height: '100%'
-      };
-    });
-    const RightStyle = computed(() => {
-      // taro转的h5不支持使用border-top这种边框属性,目前解决方案,taro转的h5使用svg实现
-      return {
-        transform: `rotate(${rotateRight.value + 'deg'})`,
-        transition: `all 0.3s`,
-        borderTop: `${InnerWidth.value + 'px'} solid ${cricleData.backColor};`,
-        borderLeft: `${InnerWidth.value + 'px'} solid  ${cricleData.backColor};`,
-        borderBottom: `${InnerWidth.value + 'px'} solid ${cricleData.progressColor};`,
-        borderRight: `${InnerWidth.value + 'px'} solid ${cricleData.progressColor};`
-      };
-    });
-    const LeftStyle = computed(() => {
-      // taro转的h5不支持使用border-top这种边框属性
-      return {
-        transform: `rotate(${rotateLeft.value + 'deg'})`,
-        transition: `all 0.3s`,
-        borderTop: `${InnerWidth.value + 'px'} solid ${cricleData.backColor};`,
-        borderLeft: `${InnerWidth.value + 'px'} solid  ${cricleData.backColor};`,
-        borderBottom: `${InnerWidth.value + 'px'} solid ${cricleData.progressColor};`,
-        borderRight: `${InnerWidth.value + 'px'} solid ${cricleData.progressColor};`
+        height: '100%',
+        transform: 'rotate(90deg)',
+        transformOrigin: '50% 50%'
       };
     });
-    const option = computed(() => {
-      // 所有进度条的可配置项
-      let baseOption = {
-        radius: 50,
-        strokeOutWidth: 10,
-        backColor: '#d9d9d9',
-        progressColor: 'red',
-        cy: 1,
-        cx: 1,
-        size: 1,
-        startPosition: ''
-      };
-      Object.assign(baseOption, props.progressOption);
-      // 圆心位置自动生成
-      baseOption.cy = baseOption.cx = baseOption.radius + baseOption.strokeOutWidth;
-      baseOption.size = (baseOption.radius + baseOption.strokeOutWidth) * 2;
-      baseOption.startPosition = 'rotate(-90,' + baseOption.cx + ',' + baseOption.cy + ')';
-      return baseOption;
-    });
-    const arcLength = computed(() => {
-      let circleLength = Math.floor(2 * Math.PI * option.value.radius);
-      let progressLength = ((props as any).progress / 100) * circleLength;
-      return `${progressLength},${circleLength}`;
-    });
+    const format = (progress: string | number) => Math.min(Math.max(+progress, 0), 100);
+    const requestAnimationFrame = function (callback: Function, lastTime: any) {
+      var lastTime;
+      if (typeof lastTime === 'undefined') {
+        lastTime = 0;
+      }
+      var currTime = new Date().getTime();
+      var timeToCall = Math.max(0, 16.7 - (currTime - lastTime));
+      lastTime = currTime + timeToCall;
+      var id = setTimeout(function () {
+        callback(currTime + timeToCall, lastTime);
+      }, timeToCall);
+      return id;
+    };
 
+    const cancelAnimationFrame = function (id: any) {
+      clearTimeout(id);
+    };
+
+    watch(
+      () => props.progress,
+      (value, oldvalue) => {
+        let rafId: number | undefined;
+        const startTime = Date.now();
+        const startRate = Number(oldvalue);
+        const endRate = Number(value);
+        const duration = Math.abs(((startRate - endRate) * 1000) / +100);
+        const animate = () => {
+          const now = Date.now();
+          const progress = Math.min((now - startTime) / duration, 1);
+          const rate = progress * (endRate - startRate) + startRate;
+          currentRate.value = Math.min(Math.max(+rate, 0), 100);
+          emit('update:progress', format(parseFloat(rate.toFixed(1))));
+          if (endRate > startRate ? rate < endRate : rate > endRate) {
+            rafId = requestAnimationFrame(animate, 0);
+          }
+        };
+        if (rafId) {
+          cancelAnimationFrame(rafId);
+        }
+        rafId = requestAnimationFrame(animate, 0);
+      }
+    );
     return {
-      isMobile,
-      rotateLeft,
-      InnerWidth,
-      rotateRight,
+      slotDefault,
+      style,
+      currentRate,
+      refRandomId,
       classes,
-      pieStyle,
-      RightStyle,
-      LeftStyle,
-      option,
-      arcLength,
-      mobileStyle
+      stop
     };
   }
 });

+ 89 - 58
src/packages/__VUE/circleprogress/index.vue

@@ -1,39 +1,36 @@
 <template>
-  <div :class="classes" :style="{ height: option.size + 'px', width: option.size + 'px' }">
-    <svg :height="option.size" :width="option.size" x-mlns="http://www.w3.org/200/svg">
-      <circle
-        :r="option.radius"
-        :cx="option.cx"
-        :cy="option.cy"
-        :stroke="option.backColor"
-        :stroke-width="option.strokeOutWidth"
+  <div :class="classes" :style="{ height: radius * 2 + 'px', width: radius * 2 + 'px' }">
+    <svg viewBox="0 0 100 100">
+      <defs>
+        <linearGradient :id="refRandomId" x1="100%" y1="0%" x2="0%" y2="0%">
+          <stop v-for="(item, index) in stop" :key="index" :offset="item.key" :stop-color="item.value"></stop>
+        </linearGradient>
+      </defs>
+      <path class="nut-circleprogress-path" :style="pathStyle" :d="path" fill="none" :stroke-width="strokeWidth">
+        >
+      </path>
+      <path
+        class="nut-circleprogress-hover"
+        :style="hoverStyle"
+        :d="path"
         fill="none"
-      />
-      <circle
-        :r="option.radius"
-        :cx="option.cx"
-        :cy="option.cy"
-        :stroke="option.progressColor"
-        :stroke-dasharray="arcLength"
-        :stroke-width="strokeInnerWidth"
-        fill="none"
-        :transform="option.startPosition"
-        stroke-linecap="round"
-        style="transition: stroke-dasharray 0.6s ease 0s, stroke 0.6s ease 0s"
-      />
+        :stroke="hoverColor"
+        :stroke-linecap="strokeLinecap"
+        transform="rotate(90,50,50)"
+        :stroke-width="strokeWidth"
+      ></path>
     </svg>
-    <div class="nut-circleprogress-content">
-      <template v-if="!isAuto">
-        <slot>{{ progress }}%</slot>
-      </template>
-      <template v-else><slot></slot></template>
+    <div class="nut-circleprogress-text">
+      <slot></slot>
+      <div v-if="!slotDefault">{{ progress }}%</div>
     </div>
   </div>
 </template>
 
 <script lang="ts">
-import { computed, onMounted, ref, watch } from 'vue';
+import { computed, useSlots } from 'vue';
 import { createComponent } from '../../utils/create';
+import { isObject } from '@/packages/utils/util';
 const { componentName, create } = createComponent('circleprogress');
 export default create({
   props: {
@@ -41,55 +38,89 @@ export default create({
       type: [Number, String],
       required: true
     },
-    strokeInnerWidth: {
+    strokeWidth: {
+      type: [Number, String],
+      default: 5
+    },
+    radius: {
       type: [Number, String],
-      default: 10
+      default: 50
     },
-    isAuto: {
-      tyep: Boolean,
-      default: false
+    strokeLinecap: {
+      type: String,
+      default: 'round'
     },
-    progressOption: {
-      type: Object,
-      default: () => {}
+    color: {
+      type: [String, Object],
+      default: ''
+    },
+    pathColor: {
+      type: String,
+      default: ''
+    },
+    clockwise: {
+      type: Boolean,
+      default: true
     }
   },
+
   setup(props, { emit }) {
+    const slotDefault = !!useSlots().default;
+    const refRandomId = Math.random().toString(36).slice(-8);
     const classes = computed(() => {
       const prefixCls = componentName;
       return {
         [prefixCls]: true
       };
     });
-    const option = computed(() => {
-      // 所有进度条的可配置项
-      let baseOption = {
-        radius: 50,
-        strokeOutWidth: 10,
-        backColor: '#d9d9d9',
-        progressColor: 'red',
-        cy: 1,
-        cx: 1,
-        size: 1,
-        startPosition: ''
+    const path = computed(() => {
+      const isWise = props.clockwise ? 1 : 0;
+      return `M 50 50 m -45 0 a 45 45 0 1 ${isWise} 90 0  a 45 45 0 1 ${isWise} -90 0`;
+    });
+    const hoverColor = computed(() => {
+      return isObject(props.color) ? `url(#${refRandomId})` : props.color;
+    });
+    const hoverStyle = computed(() => {
+      let perimeter = 283;
+      let offset = (perimeter * Number(props.progress)) / 100;
+      return {
+        stroke: isObject(props.color) ? `url(#${refRandomId})` : props.color,
+        strokeDasharray: `${offset}px ${perimeter}px`
+      };
+    });
+    const pathStyle = computed(() => {
+      return {
+        stroke: props.pathColor
       };
-      Object.assign(baseOption, props.progressOption);
-      // 圆心位置自动生成
-      baseOption.cy = baseOption.cx = baseOption.radius + baseOption.strokeOutWidth;
-      baseOption.size = (baseOption.radius + baseOption.strokeOutWidth) * 2;
-      baseOption.startPosition = 'rotate(-90,' + baseOption.cx + ',' + baseOption.cy + ')';
-      return baseOption;
     });
-    const arcLength = computed(() => {
-      let circleLength = Math.floor(2 * Math.PI * option.value.radius);
-      let progressLength = ((props as any).progress / 100) * circleLength;
-      return `${progressLength},${circleLength}`;
+    const stop = computed(() => {
+      if (!isObject(props.color)) {
+        return;
+      }
+      let color = props.color;
+      const colorArr = Object.keys(color).sort((a, b) => parseFloat(a) - parseFloat(b));
+      let stopArr: object[] = [];
+      colorArr.map((item, index) => {
+        let obj = {
+          key: '',
+          value: ''
+        };
+        obj.key = item;
+        obj.value = color[item];
+        stopArr.push(obj);
+      });
+      return stopArr;
     });
 
     return {
       classes,
-      option,
-      arcLength
+      hoverStyle,
+      pathStyle,
+      path,
+      hoverColor,
+      stop,
+      slotDefault,
+      refRandomId
     };
   }
 });

+ 0 - 10
src/packages/__VUE/circleprogress/test/__snapshots__/index.spec.ts.snap

@@ -1,10 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render component  when using require prop 1`] = `
-"<div class=\\"nut-circleprogress\\" style=\\"height: 120px; width: 120px;\\"><svg height=\\"120\\" width=\\"120\\" x-mlns=\\"http://www.w3.org/200/svg\\">
-    <circle r=\\"50\\" cx=\\"60\\" cy=\\"60\\" stroke=\\"#d9d9d9\\" stroke-width=\\"10\\" fill=\\"none\\"></circle>
-    <circle r=\\"50\\" cx=\\"60\\" cy=\\"60\\" stroke=\\"red\\" stroke-dasharray=\\"94.2,314\\" stroke-width=\\"10\\" fill=\\"none\\" transform=\\"rotate(-90,60,60)\\" stroke-linecap=\\"round\\" style=\\"transition: stroke-dasharray 0.6s ease 0s, stroke 0.6s ease 0s;\\"></circle>
-  </svg>
-  <div class=\\"nut-circleprogress-content\\">30%</div>
-</div>"
-`;

+ 21 - 18
src/packages/__VUE/circleprogress/test/index.spec.ts

@@ -2,44 +2,47 @@ import { mount } from '@vue/test-utils';
 import Circleprogress from '../index.vue';
 
 test('should render component  when using require prop', async () => {
-  // 默认圆弧半径为50  2πR为314  默认圆弧宽度为10 所以整个元素宽高为(10+50)*2
   const wrapper = mount(Circleprogress, {
     props: {
       progress: 30
     }
   });
-  expect(wrapper.html()).toMatchSnapshot();
+  //不能生成快照,因其具有随机id
+  // expect(wrapper.html()).toMatchSnapshot();
 });
 
 test('should change stoke when use width props', async () => {
-  // 圆弧宽度为20 所以整个元素宽高为(20+60)*2
   const wrapper = mount(Circleprogress, {
     props: {
       progress: 40,
-      strokeInnerWidth: 20,
-      progressOption: {
-        radius: 60,
-        strokeOutWidth: 20
-      }
+      strokeWidth: 10,
+      radius: 60
     }
   });
   let element = wrapper.element as HTMLElement;
-  expect(element.style.width).toEqual('160px');
-  expect(element.style.height).toEqual('160px');
+  expect(element.style.width).toEqual('120px');
+  expect(element.style.height).toEqual('120px');
 });
 test('should change color when use color props', async () => {
-  // 圆弧宽度为20 所以整个元素宽高为(20+60)*2
   const wrapper = mount(Circleprogress, {
     props: {
       progress: 40,
-      strokeInnerWidth: 20,
-      progressOption: {
-        backColor: 'blue',
-        progressColor: 'yelllow'
+      color: 'red'
+    }
+  });
+  let path = wrapper.findAll('path');
+  expect(path[1].html()).toContain('red');
+});
+test('渐变色', async () => {
+  const wrapper = mount(Circleprogress, {
+    props: {
+      progress: 40,
+      clockwise: false,
+      color: {
+        '0%': '#FF5E5E',
+        '100%': '#FFA062'
       }
     }
   });
-  let circle = wrapper.findAll('circle');
-  expect(circle[0].html()).toContain('blue');
-  expect(circle[1].html()).toContain('yelllow');
+  expect(wrapper.html()).toContain('stop');
 });

+ 2 - 2
src/packages/__VUE/numberkeyboard/doc.md

@@ -249,10 +249,10 @@ export default{
 | title | 键盘标题 | String | - |
 | type | 键盘模式  | String | `default`:默认样式<br>`rightColumn`:带右侧栏 |
 | random-keys | 随机数  | Boolean | false |
-| custom-key | 自定义键盘额外的键  | array<br>string | 数组形式最多支持添加2个,超出默认取前2项 |
+| custom-key | 自定义键盘额外的键  | String [] | 数组形式最多支持添加2个,超出默认取前2项 |
 | overlay | 是否显示遮罩  | Boolean| true |
 | v-model:value | 当前输入值		 | String | - |
-| maxlength  | 输入值最大长度,结合 v-model 使用 | number <br> String| 6 |
+| maxlength  | 输入值最大长度,结合 v-model 使用 | Number | String| 6 |
 | confirm-text  | 自定义完成按钮文字,如"支付","下一步","提交"等 | String | 完成 |
 
 

+ 6 - 0
src/packages/styles/variables-jdt.scss

@@ -727,5 +727,11 @@ $list-item-margin: 0 0 10px 0;
 //Ecard
 $ecard-bg-color: #f0f2f5 !default;
 
+// circleProgress
+$circle-progress-primary-color: $primary-color !default;
+$circle-progress-path-color: #e5e9f2 !default;
+$circle-progress-text-color: #000000 !default;
+$circle-progress-text-size: $font-size-3 !default;
+
 @import './mixins/index';
 @import './animation/index';

+ 6 - 0
src/packages/styles/variables.scss

@@ -751,5 +751,11 @@ $list-item-margin: 0 0 10px 0;
 //Ecard
 $ecard-bg-color: #f0f2f5 !default;
 
+// circleProgress
+$circle-progress-primary-color: $primary-color !default;
+$circle-progress-path-color: #e5e9f2 !default;
+$circle-progress-text-color: #000000 !default;
+$circle-progress-text-size: $font-size-3 !default;
+
 @import './mixins/index';
 @import './animation/index';

+ 20 - 23
src/sites/mobile-taro/vue/src/exhibition/pages/circleprogress/index.vue

@@ -4,22 +4,27 @@
     <div class="demo__piece">
       <nut-circleprogress :progress="20"> </nut-circleprogress>
     </div>
-
-    <h2>环形进度条自定义样式</h2>
+    <h2>环形进度条自定义宽度</h2>
     <div class="demo__piece">
-      <nut-circleprogress :progress="20" :progress-option="progressOption"> </nut-circleprogress>
+      <nut-circleprogress :progress="50" strokeWidth="10"> </nut-circleprogress>
     </div>
 
+    <h2>环形进度条自定义颜色(支持渐变色)</h2>
+    <div class="demo__piece">
+      <nut-circleprogress :progress="50" color="red" />
+      <nut-circleprogress :progress="100" :color="gradientColor" />
+    </div>
+    <h2>环形进度条自定义大小</h2>
+    <div class="demo__piece">
+      <nut-circleprogress :progress="50" radius="60"></nut-circleprogress>
+    </div>
     <h2>环形进度条自定义内容</h2>
     <div class="demo__piece">
-      <nut-circleprogress :progress="60" :is-auto="isAuto">
-        <div>自定义</div>
-      </nut-circleprogress>
+      <nut-circleprogress :progress="50" radius="60">自定义</nut-circleprogress>
     </div>
     <h2>动态改变环形进度条的进度</h2>
     <div class="demo__piece">
-      <nut-circleprogress :progress="percent" :progress-option="progressOption" :stroke-inner-width="strokeInnerWidth">
-      </nut-circleprogress>
+      <nut-circleprogress :progress="percent"> </nut-circleprogress>
     </div>
     <div class="demo__btn">
       <nut-button type="primary" @click="setReduceVal">减少</nut-button>
@@ -31,19 +36,13 @@
 <script lang="ts">
 import { reactive, ref } from 'vue';
 export default {
-  props: {},
   setup() {
-    const progressOption = reactive({
-      radius: 50,
-      strokeOutWidth: 10,
-      backColor: '#d9d9d9',
-      progressColor: 'blue'
-    });
-    const percent = ref(50);
-    const strokeInnerWidth = ref(10);
-    const isAuto = ref(true);
+    const gradientColor = {
+      '0%': '#FF5E5E',
+      '100%': '#FFA062'
+    };
+    const percent = ref(30);
     const setAddVal = () => {
-      strokeInnerWidth.value = 10;
       if (percent.value >= 100) {
         return;
       }
@@ -51,18 +50,16 @@ export default {
     };
     const setReduceVal = () => {
       if (percent.value - 10 <= 0) {
-        strokeInnerWidth.value = 0;
         percent.value = 0;
         return;
       }
       percent.value -= 10;
     };
     return {
-      progressOption,
-      isAuto,
       setAddVal,
       setReduceVal,
-      percent
+      percent,
+      gradientColor
     };
   }
 };