ソースを参照

feat: circleProgress taro (#569)

* feat: circleProgress taro
Drjingfubo 4 年 前
コミット
f84fa95397

+ 11 - 0
src/config.json

@@ -430,6 +430,17 @@
           "show": true,
           "desc": "用来展示进度",
           "author": "Drjingubo"
+        },
+        {
+          "version": "3.0.0",
+          "name": "CircleProgress",
+          "taro": true,
+          "sort": 8,
+          "cName": "环形进度条",
+          "type": "component",
+          "show": true,
+          "desc": "用来展示进度",
+          "author": "Drjingubo"
         }
       ]
     },

+ 96 - 0
src/packages/__VUE/circleprogress/demo.vue

@@ -0,0 +1,96 @@
+<template>
+  <div class="demo full">
+    <h2>基础用法</h2>
+    <div class="demo__piece">
+      <nut-circleprogress :progress="10"> </nut-circleprogress>
+    </div>
+
+    <h2>环形进度条自定义样式</h2>
+    <div class="demo__piece">
+      <nut-circleprogress :progress="50" :progress-option="progressOption">
+      </nut-circleprogress>
+    </div>
+
+    <h2>环形进度条自定义内容</h2>
+    <div class="demo__piece">
+      <nut-circleprogress :progress="50" :is-auto="isAuto">
+        <slot>自定义</slot>
+      </nut-circleprogress>
+    </div>
+    <h2>动态改变环形进度条的进度</h2>
+    <div class="demo__piece">
+      <nut-circleprogress
+        :progress="percent"
+        :progress-option="progressOption"
+        :stroke-inner-width="strokeInnerWidth"
+      >
+      </nut-circleprogress>
+    </div>
+    <div class="demo__btn">
+      <nut-button type="primary" @click="setReduceVal">减少</nut-button>
+      <nut-button type="primary" @click="setAddVal">增加</nut-button>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { reactive, 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 setAddVal = () => {
+      strokeInnerWidth.value = 10;
+      if (percent.value >= 100) {
+        return;
+      }
+      percent.value += 10;
+    };
+    const setReduceVal = () => {
+      if (percent.value - 10 <= 0) {
+        strokeInnerWidth.value = 0;
+        percent.value = 0;
+        return;
+      }
+      percent.value -= 10;
+    };
+    return {
+      progressOption,
+      isAuto,
+      setAddVal,
+      setReduceVal,
+      percent
+    };
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+.demo__btn {
+  text-align: center;
+  width: 100%;
+  height: 50px;
+  border-top: 1px solid rgba(234, 240, 251, 1);
+  padding-top: 6px;
+  background: rgba(255, 255, 255, 1);
+  .nut-button {
+    margin-right: 10px;
+  }
+}
+
+.demo__piece {
+  display: flex;
+  justify-content: center;
+  background: rgba(255, 255, 255, 1);
+}
+</style>

+ 89 - 0
src/packages/__VUE/circleprogress/doc.md

@@ -0,0 +1,89 @@
+# CricleProgress 进度条
+
+### 介绍
+
+展示操作或任务的当前进度。
+
+### 安装
+
+``` javascript
+import { createApp } from 'vue';
+//vue
+import { CirecleProgress } from '@nutui/nutui';
+//taro
+import { CirecleProgress } from '@nutui/nutui-taro';
+
+const app = createApp();
+app.use(CirecleProgress);
+
+```
+
+## 代码示例
+
+### 基础用法
+
+```html
+<nut-circleprogress progress="10"></nut-circleprogress>
+```
+### 环形进度条自定义样式
+
+```html
+<nut-circleprogress progress="50" :progress-option="progressOption"></nut-circleprogress>
+```
+### 环形进度条自定义内容
+
+```html
+ <nut-circleprogress progress="50" :is-auto="true"></nut-circleprogress>
+```
+### 动态改变环形进度条的进度
+
+```html
+  <nut-circleprogress :progress="percent" :progress-option="progressOption" :stroke-inner-width="strokeInnerWidth"> </nut-circleprogress>
+  <nut-button type="primary"  @click="setReduceVal" >减少</nut-button>
+  <nut-button type="primary"  @click="setAddVal">增加</nut-button>
+```
+```javascript
+ 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 setAddVal = () => {
+      strokeInnerWidth.value = 10;
+      if (percent.value >= 100) {
+        return;
+      }
+      percent.value += 10;
+    };
+    const setReduceVal = () => {
+      if (percent.value - 10 <= 0) {
+        strokeInnerWidth.value = 0;
+        percent.value = 0;
+        return;
+      }
+      percent.value -= 10;
+    };
+    return {
+      progressOption,
+      isAuto,
+      setAddVal,
+      setReduceVal,
+      percent
+    };
+  }
+```
+
+
+## Prop
+
+| 字段 | 说明 | 类型 | 默认值
+|----- | ----- | ----- | -----
+| progress | 百分比 | Number,String | 必传项,无默认值
+| stroke-inner-width | 圆弧的宽度 | Number,String | 10
+| is-auto | 是否自定义内容显示(taro暂不支持) | Boolean | false
+| progress-option | 外圆相关参数对象,其中包括半径,宽度,背景颜色,进度色值 | Object | {radius: 50,strokeOutWidth: 10, backColor: '#d9d9d9',progressColor: 'red'}

+ 52 - 0
src/packages/__VUE/circleprogress/index.scss

@@ -0,0 +1,52 @@
+.nut-circleprogress {
+  position: relative;
+
+  .nut-circleprogress-content {
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+  }
+
+  .nut-circleprogress__line {
+    position: absolute;
+    width: 50%;
+    height: 100%;
+    top: 0;
+    overflow: hidden;
+  }
+
+  .nut-circleprogress__progress {
+    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__l {
+    top: 0px;
+    transform: rotate(0deg);
+    left: 0px;
+  }
+
+  .nut-circleprogress__r {
+    top: 0px;
+    transform: rotate(180deg);
+    right: 0px;
+  }
+
+  .nut-circleprogress__line__c {
+    width: 200%;
+    height: 100%;
+    border: 10px solid transparent;
+    border-radius: 50%;
+    position: absolute;
+    box-sizing: border-box;
+    top: 0;
+    transform: rotate(-45deg);
+  }
+}

+ 202 - 0
src/packages/__VUE/circleprogress/index.taro.vue

@@ -0,0 +1,202 @@
+<template>
+  <div :class="classes" :style="pieStyle">
+    <div v-if="!isMobile">
+      <div class="nut-circleprogress__line nut-circleprogress__r">
+        <div class="nut-circleprogress__line__c" :style="RightStyle"></div>
+      </div>
+      <div class="nut-circleprogress__progress">{{ progress }}%</div>
+      <div class="nut-circleprogress__line nut-circleprogress__l">
+        <div class="nut-circleprogress__line__c" :style="LeftStyle"></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">{{ progress }}%</div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import Taro from '@tarojs/taro';
+import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue';
+import { createComponent } from '../../utils/create';
+const { componentName, create } = createComponent('circleprogress');
+
+interface progressOption {
+  radius: string | number;
+  strokeOutWidth: string | number;
+  backColor: string;
+  progressColor: string;
+}
+
+export default create({
+  props: {
+    progress: {
+      type: [Number, String],
+      required: true
+    },
+    strokeInnerWidth: {
+      type: [Number, String],
+      default: 10
+    },
+    progressOption: {
+      type: Object,
+      default: () => {}
+    }
+  },
+  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 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 RightStyle = computed(() => {
+      // taro转的h5不支持使用border-top这种边框属性,目前解决方案,taro转的h5使用svg实现
+      return {
+        transform: `rotate(${rotateRight.value + 'deg'})`,
+        transition: `all 0.2s`,
+        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.2s`,
+        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 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}`;
+    });
+
+    return {
+      isMobile,
+      rotateLeft,
+      InnerWidth,
+      rotateRight,
+      classes,
+      pieStyle,
+      RightStyle,
+      LeftStyle,
+      option,
+      arcLength
+    };
+  }
+});
+</script>
+
+<style lang="scss">
+@import 'index.scss';
+</style>

+ 109 - 0
src/packages/__VUE/circleprogress/index.vue

@@ -0,0 +1,109 @@
+<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"
+        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-content">
+      <template v-if="!isAuto"
+        ><slot>{{ progress }}%</slot></template
+      >
+      <template v-else><slot></slot></template>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { computed, onMounted, ref, watch } from 'vue';
+import { createComponent } from '../../utils/create';
+const { componentName, create } = createComponent('circleprogress');
+export default create({
+  props: {
+    progress: {
+      type: [Number, String],
+      required: true
+    },
+    strokeInnerWidth: {
+      type: [Number, String],
+      default: 10
+    },
+    isAuto: {
+      tyep: Boolean,
+      default: false
+    },
+    progressOption: {
+      type: Object,
+      default: () => {}
+    }
+  },
+  setup(props, { emit }) {
+    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: ''
+      };
+      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}`;
+    });
+
+    return {
+      classes,
+      option,
+      arcLength
+    };
+  }
+});
+</script>
+
+<style lang="scss">
+@import 'index.scss';
+</style>

+ 2 - 1
src/sites/mobile-taro/vue/src/app.config.ts

@@ -19,7 +19,8 @@ export default {
         'pages/drag/index',
         'pages/steps/index',
         'pages/infiniteloading/index',
-        'pages/progress/index'
+        'pages/progress/index',
+        'pages/circleprogress/index'
       ]
     },
     {

+ 3 - 0
src/sites/mobile-taro/vue/src/feedback/pages/circleprogress/index.config.ts

@@ -0,0 +1,3 @@
+export default {
+  navigationBarTitleText: 'Backtop'
+};

+ 94 - 0
src/sites/mobile-taro/vue/src/feedback/pages/circleprogress/index.vue

@@ -0,0 +1,94 @@
+<template>
+  <div class="demo full">
+    <h2>基础用法</h2>
+    <div class="demo__piece">
+      <nut-circleprogress :progress="20"> </nut-circleprogress>
+    </div>
+
+    <h2>环形进度条自定义样式</h2>
+    <div class="demo__piece">
+      <nut-circleprogress :progress="50" :progress-option="progressOption">
+      </nut-circleprogress>
+    </div>
+
+    <h2>环形进度条自定义内容</h2>
+    <div class="demo__piece">
+      <nut-circleprogress :progress="50" :is-auto="isAuto">
+        <slot>自定义</slot>
+      </nut-circleprogress>
+    </div>
+    <h2>动态改变环形进度条的进度</h2>
+    <div class="demo__piece">
+      <nut-circleprogress
+        :progress="percent"
+        :progress-option="progressOption"
+        :stroke-inner-width="strokeInnerWidth"
+      >
+      </nut-circleprogress>
+    </div>
+    <div class="demo__btn">
+      <nut-button type="primary" @click="setReduceVal">减少</nut-button>
+      <nut-button type="primary" @click="setAddVal">增加</nut-button>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { reactive, ref } from 'vue';
+export default {
+  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 setAddVal = () => {
+      strokeInnerWidth.value = 10;
+      if (percent.value >= 100) {
+        return;
+      }
+      percent.value += 10;
+    };
+    const setReduceVal = () => {
+      if (percent.value - 10 <= 0) {
+        strokeInnerWidth.value = 0;
+        percent.value = 0;
+        return;
+      }
+      percent.value -= 10;
+    };
+    return {
+      progressOption,
+      isAuto,
+      setAddVal,
+      setReduceVal,
+      percent
+    };
+  }
+};
+</script>
+
+<style lang="scss">
+.demo__btn {
+  text-align: center;
+  width: 100%;
+  height: 50px;
+  border-top: 1px solid rgba(234, 240, 251, 1);
+  padding-top: 6px;
+  background: rgba(255, 255, 255, 1);
+  .nut-button {
+    margin-right: 10px;
+  }
+}
+
+.demo__piece {
+  display: flex;
+  justify-content: center;
+  background: rgba(255, 255, 255, 1);
+}
+</style>