Skip to content

Commit 4489517

Browse files
feat: ✨ 调整Circle环形进度条在微信小程序端使用canvas2d支持同层渲染 (#351)
1 parent c5b3c5f commit 4489517

File tree

3 files changed

+129
-63
lines changed

3 files changed

+129
-63
lines changed

src/pages/circle/Index.vue

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<template>
22
<view class="circle">
33
<page-wraper>
4+
<wd-message-box></wd-message-box>
5+
46
<demo-block title="基础用法">
57
<wd-circle custom-class="custom-circle" v-model="current" :text="current + '%'" />
68
</demo-block>
@@ -22,10 +24,14 @@
2224
<wd-button custom-style="margin-right:24rpx" type="primary" size="small" @click="doAdd">增加</wd-button>
2325
<wd-button type="error" size="small" @click="doDecre">减少</wd-button>
2426
</demo-block>
27+
<demo-block title="alert">
28+
<wd-button @click="alert">alert</wd-button>
29+
</demo-block>
2530
</page-wraper>
2631
</view>
2732
</template>
2833
<script lang="ts" setup>
34+
import { useMessage } from '@/uni_modules/wot-design-uni'
2935
import { ref } from 'vue'
3036
3137
const current = ref<number>(20)
@@ -45,6 +51,12 @@ function doDecre() {
4551
current.value -= 10
4652
}
4753
}
54+
55+
const message = useMessage()
56+
57+
function alert() {
58+
message.alert('操作成功')
59+
}
4860
</script>
4961
<style lang="scss" scoped>
5062
.circle {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* 适配 canvas 2d 上下文
3+
* @param ctx canvas 2d 上下文
4+
* @returns
5+
*/
6+
export function canvas2dAdapter(ctx: CanvasRenderingContext2D): UniApp.CanvasContext {
7+
return Object.assign(ctx, {
8+
setFillStyle(color: string | CanvasGradient) {
9+
ctx.fillStyle = color
10+
},
11+
setStrokeStyle(color: string | CanvasGradient | CanvasPattern) {
12+
ctx.strokeStyle = color
13+
},
14+
setLineWidth(lineWidth: number) {
15+
ctx.lineWidth = lineWidth
16+
},
17+
setLineCap(lineCap: 'butt' | 'round' | 'square') {
18+
ctx.lineCap = lineCap
19+
},
20+
21+
setFontSize(font: string) {
22+
ctx.font = font
23+
},
24+
setGlobalAlpha(alpha: number) {
25+
ctx.globalAlpha = alpha
26+
},
27+
setLineJoin(lineJoin: 'bevel' | 'round' | 'miter') {
28+
ctx.lineJoin = lineJoin
29+
},
30+
setTextAlign(align: 'left' | 'center' | 'right') {
31+
ctx.textAlign = align
32+
},
33+
setMiterLimit(miterLimit: number) {
34+
ctx.miterLimit = miterLimit
35+
},
36+
setShadow(offsetX: number, offsetY: number, blur: number, color: string) {
37+
ctx.shadowOffsetX = offsetX
38+
ctx.shadowOffsetY = offsetY
39+
ctx.shadowBlur = blur
40+
ctx.shadowColor = color
41+
},
42+
setTextBaseline(textBaseline: 'top' | 'bottom' | 'middle') {
43+
ctx.textBaseline = textBaseline
44+
},
45+
createCircularGradient() {},
46+
draw() {},
47+
addColorStop() {}
48+
}) as unknown as UniApp.CanvasContext
49+
}

src/uni_modules/wot-design-uni/components/wd-circle/wd-circle.vue

Lines changed: 68 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
<template>
22
<view :class="`wd-circle ${customClass}`" :style="customStyle">
3-
<canvas :width="canvasSize" :height="canvasSize" :style="style" :id="canvasId" :canvas-id="canvasId"></canvas>
3+
<!-- #ifdef MP-WEIXIN -->
4+
<canvas :style="canvasStyle" :id="canvasId" :canvas-id="canvasId" type="2d"></canvas>
5+
<!-- #endif -->
6+
<!-- #ifndef MP-WEIXIN -->
7+
<canvas :width="canvasSize" :height="canvasSize" :style="canvasStyle" :id="canvasId" :canvas-id="canvasId"></canvas>
8+
<!-- #endif -->
9+
410
<view v-if="!text" class="wd-circle__text">
511
<!-- 自定义提示内容 -->
612
<slot></slot>
713
</view>
8-
<!-- #ifdef MP-WEIXIN -->
9-
<cover-view v-else class="wd-circle__text">
10-
{{ text }}
11-
</cover-view>
12-
<!-- #endif -->
13-
<!-- #ifndef MP-WEIXIN -->
14-
<!-- eslint-disable-next-line vue/valid-v-else -->
14+
1515
<text v-else class="wd-circle__text">
1616
{{ text }}
1717
</text>
18-
<!-- #endif -->
1918
</view>
2019
</template>
2120
<script lang="ts">
@@ -29,10 +28,10 @@ export default {
2928
}
3029
</script>
3130
<script lang="ts" setup>
32-
// Circle 环形进度条
3331
import { computed, getCurrentInstance, onBeforeMount, onMounted, onUnmounted, ref, watch } from 'vue'
3432
import { addUnit, isObj, objToStyle, uuid } from '../common/util'
3533
import { circleProps } from './types'
34+
import { canvas2dAdapter } from '../common/canvasHelper'
3635
3736
// 大于等于0且小于等于100
3837
function format(rate: number) {
@@ -47,23 +46,39 @@ const STEP = 1
4746
const props = defineProps(circleProps)
4847
4948
const progressColor = ref<string | CanvasGradient>('') // 进度条颜色
50-
const pixel = ref<number>(1) // 设备像素比
49+
5150
const currentValue = ref<number>(0) // 当前值
5251
const interval = ref<any>(null) // 定时器
53-
const canvasId = ref<string>(uuid()) // canvasId
52+
const pixelRatio = ref<number>(1) // 像素比
53+
const canvasId = ref<string>(`wd-circle${uuid()}`) // canvasId
5454
let ctx: UniApp.CanvasContext | null = null
55+
5556
// canvas渲染大小
5657
const canvasSize = computed(() => {
57-
return props.size * pixel.value
58+
let size = props.size
59+
// #ifdef MP-ALIPAY
60+
size = size * pixelRatio.value
61+
// #endif
62+
63+
return size
64+
})
65+
66+
// 进度条宽度
67+
const sWidth = computed(() => {
68+
let sWidth = props.strokeWidth
69+
// #ifdef MP-ALIPAY
70+
sWidth = sWidth * pixelRatio.value
71+
// #endif
72+
return sWidth
5873
})
5974
6075
// Circle 样式
61-
const style = computed(() => {
76+
const canvasStyle = computed(() => {
6277
const style = {
6378
width: addUnit(props.size),
6479
height: addUnit(props.size)
6580
}
66-
return `${objToStyle(style)}; ${props.customStyle}`
81+
return `${objToStyle(style)};`
6782
})
6883
6984
// 监听目标数值变化
@@ -96,38 +111,9 @@ watch(
96111
{ immediate: false, deep: true }
97112
)
98113
99-
// 监听轨道颜色变化
100-
watch(
101-
() => props.layerColor,
102-
() => {
103-
drawCircle(currentValue.value)
104-
},
105-
{ immediate: false }
106-
)
107-
108-
// 监听轨道宽度
109-
watch(
110-
() => props.strokeWidth,
111-
() => {
112-
drawCircle(currentValue.value)
113-
},
114-
{ immediate: false }
115-
)
116-
117-
// 监听轨道展示方向
118-
watch(
119-
() => props.clockwise,
120-
() => {
121-
drawCircle(currentValue.value)
122-
},
123-
{ immediate: false }
124-
)
125-
126-
// #ifdef MP-ALIPAY
127114
onBeforeMount(() => {
128-
pixel.value = uni.getSystemInfoSync().pixelRatio
115+
pixelRatio.value = uni.getSystemInfoSync().pixelRatio
129116
})
130-
// #endif
131117
132118
onMounted(() => {
133119
currentValue.value = props.modelValue
@@ -143,32 +129,54 @@ const { proxy } = getCurrentInstance() as any
143129
* 获取canvas上下文
144130
*/
145131
function getContext() {
146-
if (!ctx) {
132+
return new Promise<UniApp.CanvasContext>((resolve) => {
133+
if (ctx) {
134+
return resolve(ctx)
135+
}
136+
// #ifndef MP-WEIXIN
147137
ctx = uni.createCanvasContext(canvasId.value, proxy)
148-
}
149-
return Promise.resolve(ctx)
138+
resolve(ctx)
139+
// #endif
140+
// #ifdef MP-WEIXIN
141+
uni
142+
.createSelectorQuery()
143+
.in(proxy)
144+
.select(`#${canvasId.value}`)
145+
.node((res) => {
146+
if (res && res.node) {
147+
const canvas = res.node
148+
ctx = canvas2dAdapter(canvas.getContext('2d') as CanvasRenderingContext2D)
149+
canvas.width = props.size * pixelRatio.value
150+
canvas.height = props.size * pixelRatio.value
151+
ctx.scale(pixelRatio.value, pixelRatio.value)
152+
resolve(ctx)
153+
}
154+
})
155+
.exec()
156+
// #endif
157+
})
150158
}
151159
152160
/**
153161
* 设置canvas
154162
*/
155163
function presetCanvas(context: any, strokeStyle: string | CanvasGradient, beginAngle: number, endAngle: number, fill?: string) {
156-
const canvasSize = props.size * pixel.value
157-
let strokeWidth = props.strokeWidth * pixel.value
158-
const position = canvasSize / 2
164+
let width = sWidth.value
165+
const position = canvasSize.value / 2
159166
if (!fill) {
160-
strokeWidth = strokeWidth / 2
167+
width = width / 2
161168
}
162-
const radius = position - strokeWidth / 2
169+
const radius = position - width / 2
163170
context.strokeStyle = strokeStyle
164-
context.setLineWidth(strokeWidth)
171+
context.setStrokeStyle(strokeStyle)
172+
context.setLineWidth(width)
165173
context.setLineCap(props.strokeLinecap)
166174
167175
context.beginPath()
168176
context.arc(position, position, radius, beginAngle, endAngle, !props.clockwise)
169177
context.stroke()
170178
if (fill) {
171-
context.setLineWidth(strokeWidth)
179+
context.setLineWidth(width)
172180
context.setFillStyle(fill)
173181
context.fill()
174182
}
@@ -184,14 +192,13 @@ function renderLayerCircle(context: UniApp.CanvasContext) {
184192
* 渲染进度条
185193
*/
186194
function renderHoverCircle(context: UniApp.CanvasContext, formatValue: number) {
187-
const canvasSize = props.size * pixel.value
188195
// 结束角度
189196
const progress = PERIMETER * (formatValue / 100)
190197
const endAngle = props.clockwise ? BEGIN_ANGLE + progress : 3 * Math.PI - (BEGIN_ANGLE + progress)
191198
192199
// 设置进度条颜色
193200
if (isObj(props.color)) {
194-
const LinearColor = context.createLinearGradient(canvasSize, 0, 0, 0)
201+
const LinearColor = context.createLinearGradient(canvasSize.value, 0, 0, 0)
195202
Object.keys(props.color)
196203
.sort((a, b) => parseFloat(a) - parseFloat(b))
197204
.map((key) => LinearColor.addColorStop(parseFloat(key) / 100, (props.color as Record<string, any>)[key]))
@@ -207,12 +214,11 @@ function renderHoverCircle(context: UniApp.CanvasContext, formatValue: number) {
207214
* 进度值为0时渲染一个圆点
208215
*/
209216
function renderDot(context: UniApp.CanvasContext) {
210-
const canvasSize = props.size * pixel.value
211-
const strokeWidth = props.strokeWidth * pixel.value // 管道宽度=小圆点直径
212-
const position = canvasSize / 2 // 坐标
217+
const strokeWidth = sWidth.value // 管道宽度=小圆点直径
218+
const position = canvasSize.value / 2 // 坐标
213219
// 设置进度条颜色
214220
if (isObj(props.color)) {
215-
const LinearColor = context.createLinearGradient(canvasSize, 0, 0, 0)
221+
const LinearColor = context.createLinearGradient(canvasSize.value, 0, 0, 0)
216222
Object.keys(props.color)
217223
.sort((a, b) => parseFloat(a) - parseFloat(b))
218224
.map((key) => LinearColor.addColorStop(parseFloat(key) / 100, (props.color as Record<string, any>)[key]))
@@ -230,9 +236,8 @@ function renderDot(context: UniApp.CanvasContext) {
230236
* 画圆
231237
*/
232238
function drawCircle(currentValue: number) {
233-
const canvasSize = props.size * pixel.value
234239
getContext().then((context) => {
235-
context.clearRect(0, 0, canvasSize, canvasSize)
240+
context.clearRect(0, 0, canvasSize.value, canvasSize.value)
236241
renderLayerCircle(context)
237242
238243
const formatValue = format(currentValue)

0 commit comments

Comments
 (0)