小程序 Canvas 倒计时组件 (React 版)
基于前面Web版修改而来 Canvas 倒计时
源码:
import { Canvas, View } from "@tarojs/components"
import Taro from '@tarojs/taro'
import { FC } from "@tarojs/taro"
import rpxToPx from "../../utils/dom"
import { useEffect, useState } from "react"
import './countDown.less'
import React from "react"
interface ICountDownProps {
onMissed: () => void
duration: number // 总时长
countTime: number // 已经错过的时间
}
let timer
const CountDown: FC<ICountDownProps> = ({ onMissed, duration, countTime }) => {
const basePx = 280
const [time] = useState(duration) // 可以随意设置
let [count, setCount] = useState(duration - countTime) // 倒计时剩余时间
const percent = 2 / time // 每一秒需要的度数
useEffect(() => {
Taro.eventCenter.on('onSignClose', () => {
clearInterval(timer)
})
return () => {
clearInterval(timer)
Taro.eventCenter.off('onSignClose')
}
}, [])
const XYPoint = rpxToPx(basePx) / 2
const R = (rpxToPx(basePx) - 7) / 2
const getPercent = (count) => {
const deg = (time - count) * percent
console.log('count , deg:', count, deg)
return deg <= 0 ? -0.5 * Math.PI : (1.5 - deg) * Math.PI
}
const drawBottom = () => {
wx.createSelectorQuery()
.select('#cvs1')
.fields({
node: true,
size: true,
})
.exec((res) => {
console.log(res)
if (!res[0]) return
const canvas = res[0].node
const ctx1 = canvas.getContext('2d')
// 设置canvas 高宽
const dpr = wx.getSystemInfoSync().pixelRatio
canvas.width = rpxToPx(basePx) * dpr
canvas.height = rpxToPx(basePx) * dpr
ctx1.scale(dpr, dpr)
ctx1.beginPath()
ctx1.lineWidth = 3
ctx1.strokeStyle = '#d9d9d9'
ctx1.arc(XYPoint, XYPoint, R, 0, 2 * Math.PI, false)
ctx1.stroke()
})
}
const drawCenter = () => {
wx.createSelectorQuery()
.select('#cvs2')
.fields({
node: true,
size: true,
})
.exec((res) => {
console.log(res)
if (!res[0]) return
const canvas = res[0].node
const ctx2 = canvas.getContext('2d')
// 设置canvas 高宽
const dpr = wx.getSystemInfoSync().pixelRatio
canvas.width = rpxToPx(basePx) * dpr
canvas.height = rpxToPx(basePx) * dpr
ctx2.scale(dpr, dpr)
ctx2.beginPath()
ctx2.lineWidth = 5
ctx2.strokeStyle = '#4F8BFF'
ctx2.lineCap = 'round'
ctx2.arc(XYPoint, XYPoint, R, 1.5 * Math.PI, getPercent(count), false)
ctx2.stroke()
})
}
const drawTop = () => {
wx.createSelectorQuery()
.select('#cvs3')
.fields({
node: true,
size: true,
})
.exec((res) => {
console.log(res)
if (!res[0]) return
const canvas = res[0].node
const ctx3 = canvas.getContext('2d')
// 设置canvas 高宽
const dpr = wx.getSystemInfoSync().pixelRatio
canvas.width = rpxToPx(basePx) * dpr
canvas.height = rpxToPx(basePx) * dpr
ctx3.scale(dpr, dpr)
ctx3.beginPath()
ctx3.lineWidth = 5
ctx3.strokeStyle = '#4F8BFF'
ctx3.lineCap = 'round'
ctx3.arc(XYPoint, XYPoint, R, 1.5 * Math.PI, getPercent(count), false)
ctx3.stroke()
})
}
const startCountDown = () => {
if (count <= 1) {
onMissed()
clearInterval(timer)
return
}
setCount(--count)
wx.createSelectorQuery()
.select('#cvs2')
.fields({
node: true,
size: true,
})
.exec((res) => {
console.log(res)
if (!res[0]) return
const canvas = res[0].node
const ctx2 = canvas.getContext('2d')
// 设置canvas 高宽
const dpr = wx.getSystemInfoSync().pixelRatio
canvas.width = 0
canvas.width = rpxToPx(basePx) * dpr
canvas.height = rpxToPx(basePx) * dpr
ctx2.scale(dpr, dpr)
})
setTimeout(() => {
drawCenter()
wx.createSelectorQuery()
.select('#cvs3')
.fields({
node: true,
size: true,
})
.exec((res) => {
console.log(res)
if (!res[0]) return
const canvas = res[0].node
const ctx3 = canvas.getContext('2d')
// 设置canvas 高宽
const dpr = wx.getSystemInfoSync().pixelRatio
canvas.width = 0
canvas.width = rpxToPx(basePx) * dpr
canvas.height = rpxToPx(basePx) * dpr
ctx3.scale(dpr, dpr)
})
setTimeout(() => {
drawTop()
}, 20)
}, 20)
}
useEffect(() => {
setTimeout(() => {
drawBottom()
drawCenter()
drawTop()
}, 100)
timer = setInterval(startCountDown, 1000)
}, [])
const _pixel = rpxToPx(basePx)
return <View
style={`width: ${_pixel}px; height: ${_pixel}px;`}
className="countdown-container flex flex-item-center flex-justify-center">
<Canvas style={`width: ${_pixel}px; height: ${_pixel}px;z-index: 0;`} id="cvs1" type="2d"></Canvas>
<Canvas style={`width: ${_pixel}px; height: ${_pixel}px;z-index: 1;`} id="cvs2" type="2d"></Canvas>
<Canvas style={`width: ${_pixel}px; height: ${_pixel}px;z-index: 2;`} id="cvs3" type="2d"></Canvas>
<View className="flex flex-col flex-justify-center flex-item-center">
<View className="timer-count flex flex-item-center flex-justify-center"> {count} </View>
<View className="timer-tips flex flex-item-center" > 倒计时 (S) </View>
</View>
</View >
}
export default CountDown
rpx to px(横屏、分辨率适配需要用到):
export default function rpxToPx(rpx) {
const orientation = wx.getSystemInfoSync().deviceOrientation
if (orientation == 'landscape') {
return Math.round(rpx * wx.getSystemInfoSync().windowHeight / 750)
}
return Math.round(rpx * wx.getSystemInfoSync().windowWidth / 750)
}
效果:


浙公网安备 33010602011771号