小程序 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) }
效果: