实现思路
- 使用canvas 2d接口,获取canvas示例
- 初始化画布大小
- 设置计时器,并在计时器中使用canvas.fillText绘制文本
仓库:https://github.com/bgmmd/canvas-count-down-wx-minapp.git
代码:
count.wxml
//count.wxml
<view>
<canvas id="myCanvas" type="2d" style="height: {{height}};width:{{width}} ;" />
</view>
coutDown.js
// components/countDown/countDown.js
Component({
/**
* 组件的属性列表
*/
properties: {
total: {
type: Number,
value: 10
},
fontSize: {
type: Number,
value: 16
},
fontColor: {
type: String,
value: '#000'
},
width: {
type: String,
value: '100rpx'
},
height: {
type: String,
value: '100rpx'
}
},
/**
* 组件的初始数据
*/
data: {
timer: null,
totalSecond: null,
totalSecond: '',
ctx: null,
canvasWidth: '',
canvasHeight: ''
},
lifetimes: {
attached() {
this.data.totalSecond = this.properties.total
this.createSelectorQuery()
.select('#myCanvas') // 在 WXML 中填入的 id
.fields({
node: true,
size: true
})
.exec((res) => {
// console.log(res)
// Canvas 对象
const canvas = res[0].node
// Canvas 画布的实际绘制宽高
const renderWidth = res[0].width
const renderHeight = res[0].height
// Canvas 绘制上下文
this.data.ctx = canvas.getContext('2d')
// 初始化画布大小
const dpr = wx.getWindowInfo().pixelRatio
canvas.width = renderWidth * dpr
canvas.height = renderHeight * dpr
this.data.canvasWidth = renderWidth
this.data.canvasHeight = renderHeight
this.data.ctx.scale(dpr, dpr)
this.data.ctx.font = `${this.properties.fontSize}px sans-serif` //设置字体大小
this.data.ctx.fillStyle = this.properties.fontColor //设置字体颜色
// console.log(this.data.ctx)
// console.log(this.data)
this.data.ctx.textAlign = 'center' //设置居中
this.data.ctx.fillText(this.data.totalSecond, this.data.canvasWidth / 2, this.data.canvasHeight / 2 + this.data.fontSize / 2) //这里是设置文本居中的方法,x坐标是canvas.width的一半,但是y坐标还要加上字体尺寸的一半是因为fillText绘制文本时是字体从下往上绘制的。设置fillText('xxx',0,0)会发现字体并没有在左上角,是因为相对于坐标(0,0)进行字体从下往上绘制,所以这里垂直居中还要加上字体尺寸的一半
})
},
},
/**
* 组件的方法列表
*/
methods: {
start() {
if(this.data.totalSecond<=0)return
const that = this
this.data.timer = setInterval(() => {
this.data.totalSecond = this.data.totalSecond - 1
this.data.ctx.clearRect(0, 0, this.data.canvasWidth, this.data.canvasHeight) //清除画布,避免上次绘制文本与此次重叠
this.data.ctx.fillText(this.data.totalSecond, this.data.canvasWidth / 2, this.data.canvasHeight / 2 + this.data.fontSize / 2)
if (this.data.totalSecond <= 0) {
clearInterval(this.data.timer)
this.triggerEvent('countEnd')
}
}, 1000)
},
pause(){
if(!this.data.timer) return
clearInterval(this.data.timer)
this.triggerEvent('countPause')
},
reset(){
if(this.data.totalSecond==this.properties.total) return
if(!this.data.timer) return
clearInterval(this.data.timer)
this.data.totalSecond = this.properties.total
//重新绘制
this.data.ctx.clearRect(0, 0, this.data.canvasWidth, this.data.canvasHeight) //清除画布,避免上次绘制文本与此次重叠
this.data.ctx.fillText(this.data.totalSecond, this.data.canvasWidth / 2, this.data.canvasHeight / 2 + this.data.fontSize / 2)
this.triggerEvent('countReset')
}
}
})
演示代码
导入创建的自定义组件
countDownDemo.json
{
"usingComponents": {
"count-down":"/components/countDown/countDown"
}
}
countDownDemo.wxml
<!--pages/countDownDemo/countDownDemo.wxml-->
<view class="container">
<text>countDownDemo演示</text>
<view class="count-down-wrapper">
<count-down id="count-demo" fontColor="#fff" fontSize="20" total="15" width="200rpx" height="200rpx" bind:countEnd="countOver" bind:countPause="countStopCallback" bind:countReset="countResetCallback" />
</view>
<view wx:if="{{isEnd}}" class="cout-end-notice">
<text>倒计时结束了</text>
</view>
<view wx:else class="cout-end-notice">
</view>
<view class="btns">
<button bind:tap="beginCount" style="width: 30%;">开始</button>
<button bind:tap="stopCount" style="width: 30%;">暂停</button>
<button bind:tap="resetCount" style="width: 30%;">重置</button>
</view>
</view>
countDownDemo.js
// pages/countDownDemo/countDownDemo.js
Page({
/**
* 页面的初始数据
*/
data: {
isEnd: false
},
countOver() {
this.setData({
isEnd: true
})
},
beginCount() {
const coutDownRef = this.selectComponent('#count-demo')
coutDownRef.start()
},
stopCount(){
const coutDownRef = this.selectComponent('#count-demo')
coutDownRef.pause()
},
resetCount(){
const coutDownRef = this.selectComponent('#count-demo')
coutDownRef.reset()
this.setData({
isEnd:false
})
},
countStopCallback(){
console.log('count-down触发了暂停')
},
countResetCallback(){
console.log('count-down触发了重置')
}
})
组件文档
组件prop
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
total | 倒计时时长,单位秒 | number | 10 |
fontSize | 倒计时字体大小 | number | 16 (单位px) |
fontColor | 字体颜色 | string | #000 |
width | canvas宽 | string | 100rpx |
height | canvas高 | string | 100rpx |
组件events
事件名 | 说明 | 回调参数 |
---|---|---|
bind:countPause | 倒计时终止时触发 | 无 |
bind:countEnd | 倒计时结束时触发 | 无 |
bind:countReset | 倒计时重置时触发 | 无 |
组件调用方法
使用selectComponent可以获取到 CountDown 实例并调用实例方法。
方法名 | 参数 | 返回值 | 介绍 |
---|---|---|---|
start | - | - | 开始倒计时 |
pause | - | - | 暂停倒计时 |
reset | - | - | 重设倒计时 |
示例代码
const coutDownRef = this.selectComponent('#count-demo') //获取组件示例
coutDownRef.start()//调用组件开始方法