Skip to content

Commit

Permalink
feat: ✨ 调整Circle环形进度条在微信小程序端使用canvas2d支持同层渲染 (#351)
Browse files Browse the repository at this point in the history
  • Loading branch information
Moonofweisheng authored Jun 2, 2024
1 parent c5b3c5f commit 4489517
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 63 deletions.
12 changes: 12 additions & 0 deletions src/pages/circle/Index.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<template>
<view class="circle">
<page-wraper>
<wd-message-box></wd-message-box>

<demo-block title="基础用法">
<wd-circle custom-class="custom-circle" v-model="current" :text="current + '%'" />
</demo-block>
Expand All @@ -22,10 +24,14 @@
<wd-button custom-style="margin-right:24rpx" type="primary" size="small" @click="doAdd">增加</wd-button>
<wd-button type="error" size="small" @click="doDecre">减少</wd-button>
</demo-block>
<demo-block title="alert">
<wd-button @click="alert">alert</wd-button>
</demo-block>
</page-wraper>
</view>
</template>
<script lang="ts" setup>
import { useMessage } from '@/uni_modules/wot-design-uni'
import { ref } from 'vue'
const current = ref<number>(20)
Expand All @@ -45,6 +51,12 @@ function doDecre() {
current.value -= 10
}
}
const message = useMessage()
function alert() {
message.alert('操作成功')
}
</script>
<style lang="scss" scoped>
.circle {
Expand Down
49 changes: 49 additions & 0 deletions src/uni_modules/wot-design-uni/components/common/canvasHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* 适配 canvas 2d 上下文
* @param ctx canvas 2d 上下文
* @returns
*/
export function canvas2dAdapter(ctx: CanvasRenderingContext2D): UniApp.CanvasContext {
return Object.assign(ctx, {
setFillStyle(color: string | CanvasGradient) {
ctx.fillStyle = color
},
setStrokeStyle(color: string | CanvasGradient | CanvasPattern) {
ctx.strokeStyle = color
},
setLineWidth(lineWidth: number) {
ctx.lineWidth = lineWidth
},
setLineCap(lineCap: 'butt' | 'round' | 'square') {
ctx.lineCap = lineCap
},

setFontSize(font: string) {
ctx.font = font
},
setGlobalAlpha(alpha: number) {
ctx.globalAlpha = alpha
},
setLineJoin(lineJoin: 'bevel' | 'round' | 'miter') {
ctx.lineJoin = lineJoin
},
setTextAlign(align: 'left' | 'center' | 'right') {
ctx.textAlign = align
},
setMiterLimit(miterLimit: number) {
ctx.miterLimit = miterLimit
},
setShadow(offsetX: number, offsetY: number, blur: number, color: string) {
ctx.shadowOffsetX = offsetX
ctx.shadowOffsetY = offsetY
ctx.shadowBlur = blur
ctx.shadowColor = color
},
setTextBaseline(textBaseline: 'top' | 'bottom' | 'middle') {
ctx.textBaseline = textBaseline
},
createCircularGradient() {},
draw() {},
addColorStop() {}
}) as unknown as UniApp.CanvasContext
}
131 changes: 68 additions & 63 deletions src/uni_modules/wot-design-uni/components/wd-circle/wd-circle.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
<template>
<view :class="`wd-circle ${customClass}`" :style="customStyle">
<canvas :width="canvasSize" :height="canvasSize" :style="style" :id="canvasId" :canvas-id="canvasId"></canvas>
<!-- #ifdef MP-WEIXIN -->
<canvas :style="canvasStyle" :id="canvasId" :canvas-id="canvasId" type="2d"></canvas>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<canvas :width="canvasSize" :height="canvasSize" :style="canvasStyle" :id="canvasId" :canvas-id="canvasId"></canvas>
<!-- #endif -->

<view v-if="!text" class="wd-circle__text">
<!-- 自定义提示内容 -->
<slot></slot>
</view>
<!-- #ifdef MP-WEIXIN -->
<cover-view v-else class="wd-circle__text">
{{ text }}
</cover-view>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<!-- eslint-disable-next-line vue/valid-v-else -->

<text v-else class="wd-circle__text">
{{ text }}
</text>
<!-- #endif -->
</view>
</template>
<script lang="ts">
Expand All @@ -29,10 +28,10 @@ export default {
}
</script>
<script lang="ts" setup>
// Circle 环形进度条
import { computed, getCurrentInstance, onBeforeMount, onMounted, onUnmounted, ref, watch } from 'vue'
import { addUnit, isObj, objToStyle, uuid } from '../common/util'
import { circleProps } from './types'
import { canvas2dAdapter } from '../common/canvasHelper'
// 大于等于0且小于等于100
function format(rate: number) {
Expand All @@ -47,23 +46,39 @@ const STEP = 1
const props = defineProps(circleProps)
const progressColor = ref<string | CanvasGradient>('') // 进度条颜色
const pixel = ref<number>(1) // 设备像素比
const currentValue = ref<number>(0) // 当前值
const interval = ref<any>(null) // 定时器
const canvasId = ref<string>(uuid()) // canvasId
const pixelRatio = ref<number>(1) // 像素比
const canvasId = ref<string>(`wd-circle${uuid()}`) // canvasId
let ctx: UniApp.CanvasContext | null = null
// canvas渲染大小
const canvasSize = computed(() => {
return props.size * pixel.value
let size = props.size
// #ifdef MP-ALIPAY
size = size * pixelRatio.value
// #endif
return size
})
// 进度条宽度
const sWidth = computed(() => {
let sWidth = props.strokeWidth
// #ifdef MP-ALIPAY
sWidth = sWidth * pixelRatio.value
// #endif
return sWidth
})
// Circle 样式
const style = computed(() => {
const canvasStyle = computed(() => {
const style = {
width: addUnit(props.size),
height: addUnit(props.size)
}
return `${objToStyle(style)}; ${props.customStyle}`
return `${objToStyle(style)};`
})
// 监听目标数值变化
Expand Down Expand Up @@ -96,38 +111,9 @@ watch(
{ immediate: false, deep: true }
)
// 监听轨道颜色变化
watch(
() => props.layerColor,
() => {
drawCircle(currentValue.value)
},
{ immediate: false }
)
// 监听轨道宽度
watch(
() => props.strokeWidth,
() => {
drawCircle(currentValue.value)
},
{ immediate: false }
)
// 监听轨道展示方向
watch(
() => props.clockwise,
() => {
drawCircle(currentValue.value)
},
{ immediate: false }
)
// #ifdef MP-ALIPAY
onBeforeMount(() => {
pixel.value = uni.getSystemInfoSync().pixelRatio
pixelRatio.value = uni.getSystemInfoSync().pixelRatio
})
// #endif
onMounted(() => {
currentValue.value = props.modelValue
Expand All @@ -143,32 +129,54 @@ const { proxy } = getCurrentInstance() as any
* 获取canvas上下文
*/
function getContext() {
if (!ctx) {
return new Promise<UniApp.CanvasContext>((resolve) => {
if (ctx) {
return resolve(ctx)
}
// #ifndef MP-WEIXIN
ctx = uni.createCanvasContext(canvasId.value, proxy)
}
return Promise.resolve(ctx)
resolve(ctx)
// #endif
// #ifdef MP-WEIXIN
uni
.createSelectorQuery()
.in(proxy)
.select(`#${canvasId.value}`)
.node((res) => {
if (res && res.node) {
const canvas = res.node
ctx = canvas2dAdapter(canvas.getContext('2d') as CanvasRenderingContext2D)
canvas.width = props.size * pixelRatio.value
canvas.height = props.size * pixelRatio.value
ctx.scale(pixelRatio.value, pixelRatio.value)
resolve(ctx)
}
})
.exec()
// #endif
})
}
/**
* 设置canvas
*/
function presetCanvas(context: any, strokeStyle: string | CanvasGradient, beginAngle: number, endAngle: number, fill?: string) {
const canvasSize = props.size * pixel.value
let strokeWidth = props.strokeWidth * pixel.value
const position = canvasSize / 2
let width = sWidth.value
const position = canvasSize.value / 2
if (!fill) {
strokeWidth = strokeWidth / 2
width = width / 2
}
const radius = position - strokeWidth / 2
const radius = position - width / 2
context.strokeStyle = strokeStyle
context.setLineWidth(strokeWidth)
context.setStrokeStyle(strokeStyle)
context.setLineWidth(width)
context.setLineCap(props.strokeLinecap)
context.beginPath()
context.arc(position, position, radius, beginAngle, endAngle, !props.clockwise)
context.stroke()
if (fill) {
context.setLineWidth(strokeWidth)
context.setLineWidth(width)
context.setFillStyle(fill)
context.fill()
}
Expand All @@ -184,14 +192,13 @@ function renderLayerCircle(context: UniApp.CanvasContext) {
* 渲染进度条
*/
function renderHoverCircle(context: UniApp.CanvasContext, formatValue: number) {
const canvasSize = props.size * pixel.value
// 结束角度
const progress = PERIMETER * (formatValue / 100)
const endAngle = props.clockwise ? BEGIN_ANGLE + progress : 3 * Math.PI - (BEGIN_ANGLE + progress)
// 设置进度条颜色
if (isObj(props.color)) {
const LinearColor = context.createLinearGradient(canvasSize, 0, 0, 0)
const LinearColor = context.createLinearGradient(canvasSize.value, 0, 0, 0)
Object.keys(props.color)
.sort((a, b) => parseFloat(a) - parseFloat(b))
.map((key) => LinearColor.addColorStop(parseFloat(key) / 100, (props.color as Record<string, any>)[key]))
Expand All @@ -207,12 +214,11 @@ function renderHoverCircle(context: UniApp.CanvasContext, formatValue: number) {
* 进度值为0时渲染一个圆点
*/
function renderDot(context: UniApp.CanvasContext) {
const canvasSize = props.size * pixel.value
const strokeWidth = props.strokeWidth * pixel.value // 管道宽度=小圆点直径
const position = canvasSize / 2 // 坐标
const strokeWidth = sWidth.value // 管道宽度=小圆点直径
const position = canvasSize.value / 2 // 坐标
// 设置进度条颜色
if (isObj(props.color)) {
const LinearColor = context.createLinearGradient(canvasSize, 0, 0, 0)
const LinearColor = context.createLinearGradient(canvasSize.value, 0, 0, 0)
Object.keys(props.color)
.sort((a, b) => parseFloat(a) - parseFloat(b))
.map((key) => LinearColor.addColorStop(parseFloat(key) / 100, (props.color as Record<string, any>)[key]))
Expand All @@ -230,9 +236,8 @@ function renderDot(context: UniApp.CanvasContext) {
* 画圆
*/
function drawCircle(currentValue: number) {
const canvasSize = props.size * pixel.value
getContext().then((context) => {
context.clearRect(0, 0, canvasSize, canvasSize)
context.clearRect(0, 0, canvasSize.value, canvasSize.value)
renderLayerCircle(context)
const formatValue = format(currentValue)
Expand Down

0 comments on commit 4489517

Please sign in to comment.