123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- <template>
- <view class="tui-circular-container" :style="{ width: diam + 'px', height: (height || diam) + 'px' }">
- <canvas
- class="tui-circular-default"
- :canvas-id="defaultCanvasId"
- :id="defaultCanvasId"
- :style="{ width: diam + 'px', height: (height || diam) + 'px' }"
- v-if="defaultShow"
- ></canvas>
- <canvas class="tui-circular-progress" :canvas-id="progressCanvasId" :id="progressCanvasId" :style="{ width: diam + 'px', height: (height || diam) + 'px' }"></canvas>
- <slot />
- </view>
- </template>
- <script>
- export default {
- name: 'tuiCircularProgress',
- props: {
- /*
- 传值需使用rpx进行转换保证各终端兼容
- px = rpx / 750 * wx.getSystemInfoSync().windowWidth
- 圆形进度条(画布)宽度,直径 [px]
- */
- diam: {
- type: Number,
- default: 60
- },
- //圆形进度条(画布)高度,默认取diam值[当画半弧时传值,height有值时则取height]
- height: {
- type: Number,
- default: 0
- },
- //进度条线条宽度[px]
- lineWidth: {
- type: Number,
- default: 4
- },
- /*
- 线条的端点样式
- butt:向线条的每个末端添加平直的边缘
- round 向线条的每个末端添加圆形线帽
- square 向线条的每个末端添加正方形线帽
- */
- lineCap: {
- type: String,
- default: 'round'
- },
- //圆环进度字体大小 [px]
- fontSize: {
- type: Number,
- default: 12
- },
- //圆环进度字体颜色
- fontColor: {
- type: String,
- default: '#5677fc'
- },
- //是否显示进度文字
- fontShow: {
- type: Boolean,
- default: true
- },
- /*
- 自定义显示文字[默认为空,显示百分比,fontShow=true时生效]
- 可以使用 slot自定义显示内容
- */
- percentText: {
- type: String,
- default: ''
- },
- //是否显示默认(背景)进度条
- defaultShow: {
- type: Boolean,
- default: true
- },
- //默认进度条颜色
- defaultColor: {
- type: String,
- default: '#CCC'
- },
- //进度条颜色
- progressColor: {
- type: String,
- default: '#5677fc'
- },
- //进度条渐变颜色[结合progressColor使用,默认为空]
- gradualColor: {
- type: String,
- default: ''
- },
- //起始弧度,单位弧度
- sAngle: {
- type: Number,
- default: -Math.PI / 2
- },
- //指定弧度的方向是逆时针还是顺时针。默认是false,即顺时针
- counterclockwise: {
- type: Boolean,
- default: false
- },
- //进度百分比 [10% 传值 10]
- percentage: {
- type: Number,
- default: 0
- },
- //进度百分比缩放倍数[使用半弧为100%时,则可传2]
- multiple: {
- type: Number,
- default: 1
- },
- //动画执行时间[单位毫秒,低于50无动画]
- duration: {
- type: Number,
- default: 800
- },
- //backwards: 动画从头播;forwards:动画从上次结束点接着播
- activeMode: {
- type: String,
- default: 'backwards'
- }
- },
- watch: {
- percentage(val) {
- this.initDraw();
- }
- },
- data() {
- return {
- // #ifdef MP-WEIXIN
- progressCanvasId: 'progressCanvasId',
- defaultCanvasId: 'defaultCanvasId',
- // #endif
- // #ifndef MP-WEIXIN
- progressCanvasId: this.getCanvasId(),
- defaultCanvasId: this.getCanvasId(),
- // #endif
- progressContext: null,
- linearGradient: null,
- //起始百分比
- startPercentage: 0
- // dpi
- //pixelRatio: uni.getSystemInfoSync().pixelRatio
- };
- },
- mounted() {
- this.initDraw(true);
- },
- methods: {
- //初始化绘制
- initDraw(init) {
- let start = this.activeMode === 'backwards' ? 0 : this.startPercentage;
- start = start > this.percentage ? 0 : start;
- if (this.defaultShow && init) {
- this.drawDefaultCircular();
- }
- this.drawProgressCircular(start);
- },
- //默认(背景)圆环
- drawDefaultCircular() {
- let ctx = uni.createCanvasContext(this.defaultCanvasId, this);
- ctx.setLineWidth(this.lineWidth);
- ctx.setStrokeStyle(this.defaultColor);
- //终止弧度
- let eAngle = Math.PI * (this.height ? 1 : 2) + this.sAngle;
- this.drawArc(ctx, eAngle);
- },
- //进度圆环
- drawProgressCircular(startPercentage) {
- let ctx = this.progressContext;
- let gradient = this.linearGradient;
- if (!ctx) {
- ctx = uni.createCanvasContext(this.progressCanvasId, this);
- //创建一个线性的渐变颜色 CanvasGradient对象
- gradient = ctx.createLinearGradient(0, 0, this.diam, 0);
- gradient.addColorStop('0', this.progressColor);
- if (this.gradualColor) {
- gradient.addColorStop('1', this.gradualColor);
- }
- // #ifdef APP-PLUS
- const res = uni.getSystemInfoSync();
- if (!this.gradualColor && res.platform.toLocaleLowerCase() == 'android') {
- gradient.addColorStop('1', this.progressColor);
- }
- // #endif
- this.progressContext = ctx;
- this.linearGradient = gradient;
- }
- ctx.setLineWidth(this.lineWidth);
- ctx.setStrokeStyle(gradient);
- let time = this.percentage == 0 || this.duration < 50 ? 0 : this.duration / this.percentage;
- if (this.percentage > 0) {
- startPercentage = this.duration < 50 ? this.percentage - 1 : startPercentage;
- startPercentage++;
- }
- if (this.fontShow) {
- ctx.setFontSize(this.fontSize);
- ctx.setFillStyle(this.fontColor);
- ctx.setTextAlign('center');
- ctx.setTextBaseline('middle');
- let percentage = this.percentText;
- if (!percentage) {
- percentage = this.counterclockwise ? 100 - startPercentage * this.multiple : startPercentage * this.multiple;
- percentage = `${percentage}%`;
- }
- let radius = this.diam / 2;
- ctx.fillText(percentage, radius, radius);
- }
- if (this.percentage == 0 || (this.counterclockwise && startPercentage == 100)) {
- ctx.draw();
- }else{
- let eAngle = ((2 * Math.PI) / 100) * startPercentage + this.sAngle;
- this.drawArc(ctx, eAngle);
- }
- setTimeout(() => {
- this.startPercentage = startPercentage;
- if (startPercentage == this.percentage) {
- this.$emit('end', {
- canvasId: this.progressCanvasId,
- percentage: startPercentage
- });
- } else {
- this.drawProgressCircular(startPercentage);
- }
- this.$emit('change', {
- percentage: startPercentage
- });
- }, time);
- // #ifdef H5
- // requestAnimationFrame(()=>{})
- // #endif
- },
- //创建弧线
- drawArc(ctx, eAngle) {
- ctx.setLineCap(this.lineCap);
- ctx.beginPath();
- let radius = this.diam / 2; //x=y
- ctx.arc(radius, radius, radius - this.lineWidth, this.sAngle, eAngle, this.counterclockwise);
- ctx.stroke();
- ctx.draw();
- },
- //生成canvasId
- getCanvasId() {
- let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
- return (c === 'x' ? (Math.random() * 16) | 0 : 'r&0x3' | '0x8').toString(16);
- });
- return uuid;
- }
- }
- };
- </script>
- <style scoped>
- .tui-circular-container,
- .tui-circular-default {
- position: relative;
- }
- .tui-circular-progress {
- position: absolute;
- left: 0;
- top: 0;
- z-index: 10;
- }
- </style>
|