小程序表盘进度动画(类似芝麻信用动画)
2019-07-26 本文已影响0人
疯子不需要风
先上个效果图:
timer.gif
本案例是用微信小程序实现,canvas属性方法跟H5 canvas略有些不同,不过大体意思一样,所以在H5中实现原理也是一样的
下面是样式布局,相对比较简单
<!--pages/charge/charging/charging.wxml-->
<view class="charging-view">
<view class="charging-top">
<view class="charging-title">充电站</view>
<view class="charging-canvas">
<canvas canvas-id="canvas" style="width: {{canvasWidth}}px; height: {{canvasWidth*4/5}}px; margin: auto auto;"></canvas>
</view>
<view class="v-flex charging-time">
<view>21</view>
<view>21</view>
<view>21</view>
</view>
</view>
<view class="charging-item">
<view class="charging-item-title">开始电池电量</view>
<view class="charging-item-bg item-bg1">
<view class="charging-item-step" style="width: {{startElec}}%">{{startElec}}%</view>
</view>
</view>
<view class="charging-item">
<view class="charging-item-title">当前电池电量</view>
<view class="charging-item-bg item-bg2">
<view class="charging-item-step" style="width: {{currentElec}}%">{{currentElec}}%</view>
</view>
</view>
<view class="charging-item">
<view class="charging-item-title">已充电量</view>
<view class="charging-item-bg item-bg3">
<view class="charging-item-step" style="width: {{currentElec-startElec}}%">{{(currentElec-startElec)}}%</view>
</view>
</view>
<view class="v-flex charging-status">
<view class="v-flex-item charging-item-title">预计剩余时长</view>
<view class="charging-status-text">01:00:00</view>
</view>
<view class="v-flex charging-status">
<view class="v-flex-item charging-item-title">消费金额</view>
<view class="charging-status-text">100元</view>
</view>
<view class="charging-btn">
<button class="v-button" hover-class="v-active" bindtap="endCharge">结束充电</button>
<view class="charging-tips">温馨提示:充电完成后将在钱包中自动划扣消费金额。</view>
</view>
</view>
接下来是js实现部分,在代码里基本上都有详细的注释说明,直接上代码了
// pages/charge/charging/charging.js
var context; //canvas上下文环境
const lineWidth = 15; //蓝色环形边框宽度
const pointR = 12; //动画圆圈半径
let centerX, centerY, circleR; //大圆圆心半径
let timer;
Page({
/**
* 页面的初始数据
*/
data: {
canvasWidth: '', //canvas宽度
startElec: 30, //起始电量
currentElec: 30, //当前电量
alreadyElec: 0 //已充电量
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
// 获取设备信息
this.getSystemInfo()
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
// 开始绘制画布
this.createCanvas()
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
},
/**
* 自定义事件
*/
endCharge() {
wx.navigateTo({
url: "./../endCharge/endCharge"
})
},
// 獲取設備信息
getSystemInfo() {
const that = this
wx.getSystemInfo({
success (res) {
that.setData({
canvasWidth: res.windowWidth * 0.6
})
}
})
},
// canvas绘制
createCanvas() {
context = wx.createCanvasContext('canvas')
centerX = (this.data.canvasWidth)/2
centerY = (this.data.canvasWidth)/2
circleR = centerX-10
this.startAnimate()
timer = setInterval(() => {
this.startAnimate()
}, 1000)
},
startAnimate() {
this.setData({
currentElec: this.data.currentElec+3
})
if(this.data.currentElec>100){
this.setData({
currentElec: 100
})
}
context.clearRect(0, 0, this.data.canvasWidth, this.data.canvasWidth)
this.drawLightCircle()
this.drawLineCircle()
this.drawText(this.data.currentElec + '%')
this.drawAnimateCircle(this.data.currentElec)
this.drawAnimateDot(this.data.currentElec)
},
// 绘制浅蓝色静态大圆
drawLightCircle() {
context.save();
context.setStrokeStyle('#E1EAF5');
context.setLineWidth(lineWidth);
context.beginPath();
context.translate(centerX, centerY);
context.rotate(150 * Math.PI/180);
context.arc(0, 0, circleR-lineWidth/2, (Math.PI/180)*0, (Math.PI/180)*240, false);
context.stroke();
context.closePath();
context.restore();
},
// 绘制间隔线条
drawLineCircle() {
let angel = 4.5 //每间隔多少度画一条线
let outR = circleR - lineWidth - 12 //间条线外边点距离中心点的长度(即外边半径)
let inR = circleR - lineWidth * 2 - 5 //间条线内边点距离中心点的长度(即内边半径)
context.save()
context.translate(centerX, centerY); //把中心点移动到
context.rotate(150 * Math.PI/180); //旋转画布150弧度
// 遍历,通过两个半径长度outR和inR,利用三角函数得到两个点的坐标绘制线条
// 遍历的序列号i即为角度值
for(var i = 0;i<54;i++){
let startX = outR * Math.cos(angel * i / 180 * Math.PI)
let startY = outR * Math.sin(angel * i / 180 * Math.PI)
let endX = inR * Math.cos(angel * i / 180 * Math.PI)
let endY = inR * Math.sin(angel * i / 180 * Math.PI)
context.beginPath();
context.setLineWidth(1);
context.setStrokeStyle("#9E9CBC");
context.moveTo(startX, startY)
context.lineTo(endX, endY)
context.stroke();
context.closePath();
}
context.restore()
},
// 绘制中间百分数文字
drawText(text) {
context.save()
context.translate(centerX, centerY);
context.beginPath()
context.setFontSize(30)
context.setFillStyle("#519FF7")
context.setTextAlign('center')
context.setTextBaseline('middle')
context.fillText(text, 0, 0)
context.closePath()
context.restore()
},
// 绘制蓝色动态大圆
drawAnimateCircle(percent) {
// 整个大圆240度,将240度分为100份,那么每份的角度是2.4度
let angel = percent * 2.4
context.save();
context.setStrokeStyle('#8CC2FF');
context.setLineWidth(lineWidth);
context.beginPath();
context.translate(centerX, centerY);
context.rotate(150 * Math.PI/180);
// 绘制半径为大圆半径减去线边框一半,绘制角度从0-angel
context.arc(0, 0, circleR-lineWidth/2, (Math.PI/180)*0, (Math.PI/180)*angel, false);
context.stroke();
context.closePath();
context.restore();
},
// 绘制动画圆点
drawAnimateDot(percent) {
let angel = percent * 2.4
// 计算出circleR-lineWidth/2即为中心点到小圆圆心的长度,利用三角函数关系得到小圆圆心坐标点
let pointX = (circleR-lineWidth/2) * Math.cos(angel/180 * Math.PI)
let pointY = (circleR-lineWidth/2) * Math.sin(angel/180 * Math.PI)
context.save();
context.translate(centerX, centerY);
context.rotate(150 * Math.PI/180);
context.beginPath();
context.setFillStyle("#ffffff")
context.setLineWidth(1);
context.setShadow(0, 0, 5, '#519FF7')
// 再有圆心点和小圆半径绘制小圆所在的位置
context.arc(pointX, pointY, pointR, 0, 2 * Math.PI)
context.fill()
context.closePath();
context.draw()
context.restore();
if(this.data.currentElec>=100){
clearInterval(timer)
}
}
})
有需要的欢迎转载,有问题欢迎留言~