微信小程序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 相关文章推荐
javascript 网页跳转的方法
Dec 24 Javascript
jquery对元素拖动排序示例
Jan 16 Javascript
使用原生js写的一个简单slider
Apr 29 Javascript
浅析Bootstrip的select控件绑定数据的问题
May 10 Javascript
three.js绘制地球、飞机与轨迹的效果示例
Feb 28 Javascript
EasyUI框架 使用Ajax提交注册信息的实现代码
Sep 27 Javascript
浅谈Webpack打包优化技巧
Jun 12 Javascript
Vue全局分页组件的实现代码
Aug 10 Javascript
VUE渲染后端返回含有script标签的html字符串示例
Oct 28 Javascript
vue实现瀑布流组件滑动加载更多
Mar 10 Javascript
基于JavaScript实现贪吃蛇游戏
Mar 16 Javascript
vue实现购物车案例
May 30 Javascript
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
天使彦史上最神还原,性别曝光的那一刻,百万网友恋爱了
2020/03/02 国漫
PHP开发大型项目的一点经验
2006/10/09 PHP
如何过滤高亮显示非法字符
2006/10/09 PHP
php学习之 认清变量的作用范围
2010/01/26 PHP
PHP使用trim函数去除字符串左右空格及特殊字符实例
2016/01/07 PHP
PHP使用PDO操作数据库的乱码问题解决方法
2016/04/08 PHP
PHP+Ajax实现的检测用户名功能简单示例
2019/02/12 PHP
JSON为什么那样红为什么要用json(另有洞天)
2012/12/26 Javascript
JS对话框_JS模态对话框showModalDialog用法总结
2014/01/11 Javascript
Javascript中使用A标签获取当前目录的绝对路径方法
2015/03/02 Javascript
深入理解JavaScript中的箭头函数
2015/07/28 Javascript
javascript实现Email邮件显示与删除功能
2015/11/21 Javascript
Javascript中神奇的this
2016/01/20 Javascript
AngularJS中transclude用法详解
2016/11/03 Javascript
Angular2-primeNG文件上传模块FileUpload使用详解
2017/01/14 Javascript
Vue数据监听方法watch的使用
2018/03/28 Javascript
JS逻辑运算符短路操作实例分析
2018/07/09 Javascript
layui按条件隐藏表格列的实例
2019/09/19 Javascript
微信小程序如何实现精确的日期时间选择器
2020/01/21 Javascript
用jQuery实现抽奖程序
2020/04/12 jQuery
python中的二维列表实例详解
2018/06/19 Python
利用Django模版生成树状结构实例代码
2019/05/19 Python
pytorch 更改预训练模型网络结构的方法
2019/08/19 Python
Python响应对象text属性乱码解决方案
2020/03/31 Python
使用Python实现批量ping操作方法
2020/05/06 Python
Python3如何使用range函数替代xrange函数
2020/10/05 Python
HTML5 video播放器全屏(fullScreen)方法实例
2015/04/24 HTML / CSS
HTML5中input[type='date']自定义样式与日历校验功能的实现代码
2017/07/11 HTML / CSS
西班牙高科技产品购物网站:MejorDeseo
2019/09/08 全球购物
护士感人事迹
2014/05/01 职场文书
计算机毕业生自荐信
2014/06/12 职场文书
啦啦队口号大全
2014/06/16 职场文书
2014年小学重阳节活动策划方案
2014/09/16 职场文书
个人诉讼委托书范本
2014/10/17 职场文书
2015年度残疾人工作总结
2015/05/14 职场文书
如何开发一个渐进式Web应用程序PWA
2021/05/10 Javascript