微信小程序canvas开发水果老虎机的思路详解


Posted in Javascript onFebruary 07, 2020

在这个超长假期中,无聊。。。,所以动手做一个早就计划要做的小玩意, 水果老虎机 ,嗯,这是一个小程序而不是小游戏...

微信小程序canvas开发水果老虎机的思路详解 

使用结构还是canvas?

使用模板结构(view)生成水果盘的好处一是用户可自定义产出 n x n 的定制化老虎机,二是容易通过算法样式生成布局,三是通过 wx.selectQueryAll 的方法能够很方便的抓到定位数据。但,问题是动画性能过于孱弱,如图构建一个 7x7 的水果盘,动画性能估计会惨不忍睹,而且纯粹模板结构无论使用 animation 动画方法还是 css 的keyframe的动画方法得到的动画效果都非常差(测试过的结论),还有是已知的动画方法可控性很差

使用canvas来生成水果盘好处是动画性能很好(canvas2d),但是定制性和扩展性比较差

so综上考虑,使用模板(view)布局,使用canvas来实现动画。既保证了组件的性能,同时定制型,扩展性也很好

准备计时器方法

动画的生成离不开计时器方法,settimeout/setinterval这两兄弟真的不够看啊,问题还多,做过web开发的一定都知道 window.requestAnimationFrame ,这货在小程序的计时器方法中不存在,好在 canvas2d 中可以使用 Canvas.requestAnimationFrame(function callback) 方法来实现

准备运动算法

在水果老虎机中,激活状态会沿着四方的水果盘做非线性运动(easeInOut比较好用),需要基础的运动算法来计算实际的运动距离。在 animation 动画方法中,我们可以使用 ease-in/ease-out 等缓动算法来实现动画效果,但在这里必须要借助 tween.js 中的缓动算法来实现运动效果(因为需要控制运动节点)。

你会不会想到用css的keyframe动画来做这个运动效果,经过我的测试,css的动画和animation的动画会在每一条边上实现一次(ease)缓动运动(很奇怪的效果)

推荐这篇文章

使用其中一个,节省代码量

/*
 * Tween.js
 * t: current time(当前时间);
 * b: beginning value(初始值);
 * c: change in value(变化量);
 * d: duration(持续时间)。
 */
// Quart 四次方的缓动
const easeInOutQuart = function (t, b, c, d) {
 if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b;
 return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
}

tween算法是以时间为基准(时间比率 = 距离比率)来计算单位时间的实际运动距离

布局

以上面的图为例,我们需要做一个 7 x 7 的水果盘,实际有效的奖品格子数为 7*4-4 共24个有效格子

有效格子算法

js

// 0-6 第一行所有格子全部有效 
// 21-27 最后一行所有格子全部有效 
// 中间部分 i%7===0 和 i%7 === (7-1) 有效
// 算法源码有点无聊,依据上述思路,即可遍历28个格子并标识奖品格子valide=true
// 可以扩展想一想 6x6 5x5,思路是一样的

wxml

<view class="fruits-container" >
  <view class="fruits-table" >
    <block wx:for="{{ary}}" wx:key="index" >
      <view wx:if="{{item.valide}}" class="valide">{{item.title}}</view>
      <view wx:else class="in-valide"></view>
    </block>
  </view>
  <canvas type="2d" .... />
</view>

样式

只节选关键样式,目的是让canvas覆盖在水果盘上,长宽一致

.fruits-container {
  position: relative;
  width: 400px;
  height: 400px;
  ...
}

.fruits-table {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  ...
}

抓取位置信息

canvas的绘制需要X轴, Y轴的精确信息,可以使用 wx.createSelectorQuery 方式抓取类名为‘valide'的 view (奖品格子)的位置信息

let query = wx.createSelectorQuery().in(this)
query.selectAll(`.fruits-table .valide`).boundingClientRect(ret => {
  ....
  console.log(ret[0]) // top, left, right, bottom, width, height
  console.log(ret[1]) // top, left, right, bottom, width, height
  ...
  ...
  console.log(ret[23]) // top, left, right, bottom, width, height
})

得到每一个奖品格子的位置信息后,就可以使用canvas的 fillRect 方法来绘制激活状态了。

绘制一个激活状态

let query = wx.createSelectorQuery().in(this)
query.selectAll(`.fruits-table .valide`).boundingClientRect(ret => {
  ....
  let {top, left, right, bottom, width, height} = ret[0]
  const canvasQuery = wx.createSelectorQuery()
  canvasQuery.select('#fruit-canvas')
  .fields({ node: true, size: true })
  .exec((res) => {
    const canvas = res[0].node
    const ctx = canvas.getContext('2d') 
    let x = top
    let y = left
    let dx = width
    let dy = height
    ctx.shadowOffsetX = 2
    ctx.shadowOffsetY = -2
    ctx.shadowColor = 'red'
    ctx.shadowBlur = 50
    ctx.lineWidth = 5
    ctx.strokeStyle = 'red'
    ctx.clearRect(0, 0, canvas.width, canvas.height)
    ctx.strokeRect(x, y, dx, dy)
  })
})

跑起来

已经绘制了一个激活状态,接下来使它能够简单动起来

// 抽象激活方法 
functon rect(point, canvas){
  let {x, y, dx, dy} = getPosition(point)
  ctx.shadowOffsetX = 2
  ctx.shadowOffsetY = -2
  ...
  ...
  ctx.clearRect(0, 0, canvas.width, canvas.height) // 擦除整个水果盘
  ctx.strokeRect(x, y, dx, dy) // 绘制激活区域
}

function run(){
  setTimeout(()=>{
    if (ret.length) {
      let point = ret.shift()
      rect(point, canvas)
      run()
    }
  }, 100)
}

执行run方法后可以看到水果盘的激活状态一步一步的往前走(100毫秒),拖拉机终于可以启动了

配上运动算法

经过上面的试验我们终于可以看到基本的运动效果了,接下来配上运动算法和计时器方法

// Quart 四次方的缓动
const easeInOutQuart = function (t, b, c, d) {
 if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b;
 return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
}

let start = 0 // 开始时间
let begin = 0 // 开始奖品位置
let end = 23 // 终点位置,这里跑一圈
let during = 5000 // 运动总时间

// 1000/60 ≈ 17,
// 17毫秒即表示屏幕60帧刷新率每秒 ≈ requestAnimationFrame计数频率(一般情况) 
const steper = () => {
 // left为位移距离
 // 老虎机的运动位移是节点位移,不是精确位移
 // 所以这里用parseInt处理,只取整数部分
 // 数据变化为 0,1,2,3,4,5...23
 // 间隔时间/距离由easeInOutQuart算法计算
 var left = easeInOutQuart(start, begin, end, during);
 let idx = parseInt(left)
 start = start + 17; 
 if (idx <= end) {
  let point = this.ret[idx] // 取节点位置信息
  this.rect(point) // 绘制
 }
 // 时间递增
 if (start <= during) {
  this.ctx.requestAnimationFrame(steper); // 计时器
 } else {
  // 动画结束,这里可以插入回调...
  // callback()...
 }
};
steper(); // 启动

总结

以上所述是小编给大家介绍的微信小程序canvas开发水果老虎机的思路详解,希望对大家有所帮助!

Javascript 相关文章推荐
客户端脚本中常常出现的一些问题和调试技巧
Jan 09 Javascript
Prototype源码浅析 String部分(二)
Jan 16 Javascript
探索Emberjs制作一个简单的Todo应用
Nov 07 Javascript
Web Inspector:关于在 Sublime Text 中调试Js的介绍
Apr 18 Javascript
浅谈jQuery的offset()方法及示例分享
Jul 17 Javascript
location.hash保存页面状态的技巧
Apr 28 Javascript
js 获取今天以及过去日期
Apr 11 Javascript
vue 解决循环引用组件报错的问题
Sep 06 Javascript
vue添加axios,并且指定baseurl的方法
Sep 19 Javascript
实例讲解JavaScript截取字符串
Nov 30 Javascript
JavaScript常见鼠标事件与用法分析
Jan 03 Javascript
jQuery事件多次绑定与解绑问题实例分析
Feb 19 jQuery
Node.js 在本地生成日志文件的方法
Feb 07 #Javascript
node.js 微信开发之定时获取access_token
Feb 07 #Javascript
jQuery操作选中select下拉框的值代码实例
Feb 07 #jQuery
webpack的 rquire.context用法实现工程自动化的方法
Feb 07 #Javascript
详解为element-ui的Select和Cascader添加弹层底部操作按钮
Feb 07 #Javascript
vue-cli设置css不生效的解决方法
Feb 07 #Javascript
js生成1到100的随机数最简单的实现方法
Feb 07 #Javascript
You might like
浅谈电磁辐射对健康的影响
2021/03/01 无线电
测试php连接mysql是否成功的代码分享
2014/01/24 PHP
PHP实现获取某个月份周次信息的方法
2015/08/11 PHP
layui框架实现文件上传及TP3.2.3(thinkPHP)对上传文件进行后台处理操作示例
2018/05/12 PHP
Laravel Eloquent分表方法并使用模型关联的实现
2019/11/25 PHP
js监听输入框值的即时变化onpropertychange、oninput
2011/07/13 Javascript
Javascript继承(上)——对象构建介绍
2012/11/08 Javascript
javascript获取选中的文本的方法代码
2013/10/30 Javascript
nodeType属性返回被选节点的节点类型介绍
2013/11/22 Javascript
用jquery.sortElements实现table排序
2014/05/04 Javascript
JavaScript设计模式学习之“类式继承”
2015/03/12 Javascript
js禁止页面刷新与后退的方法
2015/06/08 Javascript
微信小程序 图片绝对定位(背景图片)
2017/04/05 Javascript
BootStrap Table前台和后台分页对JSON格式的要求
2017/06/28 Javascript
JS+canvas绘制的动态机械表动画效果
2017/09/12 Javascript
nodejs调取微信收货地址的方法
2017/12/20 NodeJs
js实现圆形显示鼠标单击位置
2020/02/11 Javascript
原生js实现瀑布流效果
2020/03/09 Javascript
基于vue+echarts 数据可视化大屏展示的方法示例
2020/03/09 Javascript
在vue中实现嵌套页面(iframe)
2020/07/30 Javascript
[45:32]Liquid vs LGD 2018国际邀请赛淘汰赛BO3 第二场 8.23
2018/08/24 DOTA
python3中bytes和string之间的互相转换
2017/02/09 Python
使用python爬取B站千万级数据
2018/06/08 Python
局域网内python socket实现windows与linux间的消息传送
2019/04/19 Python
python从入门到精通 windows安装python图文教程
2019/05/18 Python
关于不懂Chromedriver如何配置环境变量问题解决方法
2019/06/12 Python
torch 中各种图像格式转换的实现方法
2019/12/26 Python
老生常谈CSS中的长度单位
2016/06/27 HTML / CSS
入党积极分子介绍信
2014/01/17 职场文书
酒店管理毕业生自我鉴定
2014/03/02 职场文书
房屋出售协议书
2014/04/10 职场文书
2014年乡镇领导个人整改措施
2014/09/19 职场文书
2014大学辅导员工作总结
2014/12/02 职场文书
如何写好竞聘报告
2019/04/03 职场文书
python使用pygame创建精灵Sprite
2021/04/06 Python
如何解决.cuda()加载用时很长的问题
2021/05/24 Python