[Canvas] 按钮点击圆形扩散效果

这是在微信小程序中的代码,部分API有点不同。网页端需要对canvas做一些兼容。

canvas画布大小为180px*180px,按钮为半径65px的圆形,第一次接触canvas,做的时候不知道有requestAnimationFrame来专门制作动画,使用了setTimeout来画动画帧。后面补充使用requestAnimationFrame的例子,当然这么好用的东西微信肯定不会支持的…

示例看这里!

没有使用requestAnimationFrame, 快速点击按钮时会出现卡顿,丢帧的情况。

class Animate{
  
  constructor(id){
    this.ctx = {}
    this.id = id
    //起始半径
    this.r = 65
    //扩散长度
    this.long = 30
    //起始透明度
    this.opacity = 1
    //结束透明度
    this.end_opacity = 0
    this.step_r = 0
    this.step_opacity = 0
    this.center = [90, 90]
    this.size = [180, 180]
    this.fps = 20
    //帧间隔
    this.frame_interval = 0
    //一个扩散持续的时间,单位为ms
    this.interval = 800
    //绘制数组
    this.DRAW_ARR = []
    //是否在更新
    this.isUpdating = false;
    //上一帧时间戳
    this.lastTime = 0;

    this.init()
  }

  /**
   * 初始化获取上下文对象
   */
  init(){
    let context = wx.createCanvasContext(this.id)
    this.ctx = context
    let frame_interval = Math.ceil(1000/this.fps)
    //限制最高20帧
    if (frame_interval<50){
      frame_interval = 50
    }
    this.frame_interval = frame_interval
    if(this.interval<200){
      this.interval = 200
    }

    //计算单个元素声明间隔步数
    let step = this.interval / this.frame_interval
    //计算步进半径
    this.step_r = this.long / step
    //计算步进透明度
    this.step_opacity = (this.opacity - this.end_opacity) / step

  }

  /**
   * 添加一个
   */
  add() {
    // this.paint()
    let now = new Date().getTime();
    //如果时间戳间隔超过渲染间隔则重置
    if ((now - this.lastTime) > this.frame_interval){
      this.lastTime = now
    }
    //小步数
    let small_step = (new Date().getTime() - this.lastTime) / this.frame_interval

    this.DRAW_ARR.unshift([
      this.r + this.step_r * small_step, this.opacity - this.step_opacity * small_step
    ])
    if (!this.isUpdating){
      //如果没有更新则开始更新
      this.update()
    }
  }

  /**
   * 更新数据
   */
  update(){
    //处理动画序列
    this.paint()
    //临时序列
    let tmp = [];
    for (let i = 0; i < this.DRAW_ARR.length; i++) {
      let target_r = this.DRAW_ARR[i][0] + this.step_r
      let target_opacity = this.DRAW_ARR[i][1] - this.step_opacity
      //目标半径未超过最大半径,透明度没有小于0
      if (target_r < (this.r + this.long) && target_opacity > this.end_opacity){
        tmp.push([
          target_r, target_opacity
        ])
      }
    }
    this.DRAW_ARR = tmp
    if (this.DRAW_ARR.length <= 0){
      // 序列为空,停止更新
      this.isUpdating = false;
    } else {
      //更新时间戳
      this.lastTime = new Date().getTime();
      let that = this
      setTimeout(function () {
        that.update()
      }, this.frame_interval)
    }
  }

  /**
   * 绘制
   */
  paint(){
    let context = this.ctx
    // 清空画板
    context.clearRect(0, 0, this.size[0], this.size[1]);
    context.setStrokeStyle("#ffe100")
    context.setLineWidth(1)
    for (let i = 0; i < this.DRAW_ARR.length; i++){
      context.globalAlpha = this.DRAW_ARR[i][1]
      context.arc(this.center[0], this.center[1], this.DRAW_ARR[i][0], 0, 2 * Math.PI)
      context.stroke()
    }
    context.draw()
    context.globalAlpha = 1
  }
}
export default Animate

使用方法:

// 导入
import Animate from './Animate.js'
// 传入Canvas的id实例化
let animate = new Animate('animate')
//点击按钮的时候触发add()方法
animate.add()

使用了requestAnimationFrame,动画流畅,没有丢帧,丝般顺滑,啧啧~~真香
有空再看看canvas怎么优化

let ctx = {}
let id
//起始半径
let r = 65
//扩散长度
let long = 30
//起始透明度
let opacity = 1
//结束透明度
let end_opacity = 0
let step
let step_r = 0
let step_opacity = 0
let center = [90, 90]
let size = [180, 180]
let fps = 30
//帧间隔
let frame_interval = 0
//一个扩散持续的时间,单位为ms
let interval = 1500
//绘制数组
let DRAW_ARR = []

const init = (id) => {
  id = id
  ctx = wx.createCanvasContext(id)
  frame_interval = Math.ceil(1000 / fps)
  if (interval < 200) {
    interval = 200
  }

  //计算单个元素声明间隔步数
  step = interval / frame_interval
  //计算步进半径
  step_r = long / step
  //计算步进透明度
  step_opacity = (opacity - end_opacity) / step

  animate()
}
const add = () => {
  DRAW_ARR.unshift([
    r, opacity
  ])
}
const update = () =>{
  let tmp = [];
  for (let i = 0; i < DRAW_ARR.length; i++) {
    let target_r = DRAW_ARR[i][0] + step_r
    let target_opacity = DRAW_ARR[i][1] - step_opacity
    //目标半径未超过最大半径,透明度没有小于0
    if (target_r < (r + long) && target_opacity > end_opacity) {
      tmp.push([
        target_r, target_opacity
      ])
    }
  }
  DRAW_ARR = tmp
}

const animate = () => {
  console.log('animate')
  paint()
  update()
  requestAnimationFrame(animate)
}

const paint = () =>{
  let context = ctx
  // 清空画板
  context.clearRect(0, 0, size[0], size[1]);
  context.setStrokeStyle("#ffe100")
  context.setLineWidth(1)
  for (let i = 0; i < DRAW_ARR.length; i++) {
    context.globalAlpha = DRAW_ARR[i][1]
    context.arc(center[0], center[1], DRAW_ARR[i][0], 0, 2 * Math.PI)
    context.stroke()
  }
  context.draw()
  context.globalAlpha = 1
}

module.exports = {
  init: init,
  add: add
}

使用方法:

// 导入
let Animate = require('./Animate.js')
// 初始化
Animate.init('animate')
//点击按钮的时候触发add()方法
Animate.add()

有空再补充canvas优化的实例

https://www.doctype.com.cn/archives/99/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值