小程序表盘进度动画(类似芝麻信用动画)

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)
      }
  }
  
})

有需要的欢迎转载,有问题欢迎留言~

上一篇下一篇

猜你喜欢

热点阅读