在这个漫长的假期里,无聊。所以开始做一个计划已久的小玩意,水果吃角子老虎机。嗯,这是一个小程序,而不是一个小游戏。
使用模板结构(视图)生成水果盘的优点是用户可以定制可以输出nn的定制吃角子老虎机,即通过算法样式很容易生成布局,定位数据也可以通过wx.selectQueryAll的方法很容易捕捉到,但问题是动画表现太弱。如果构建一个如图所示的7x7水果盘,动画表现估计很可怕,纯模板结构的动画效果很差(测试结论),已知的动画方法可控性差。
使用canvas生成果盘的好处是动画表现很好(canvas2d),但定制性和扩展性较差。
所以综上所述,使用模板(视图)布局和画布来实现动画。既保证了组件的性能,又具有良好的定制性和扩展性。
没有定时器方法无法生成动画。settimeout/setinterval真的不够。还有很多问题。做过web开发的人一定知道window.requestAnimationFrame,小程序的timer方法中没有。幸运的是,帆布。Requestanimation frame(函数回调)方法可以用在canvas2d中。
在水果吃角子老虎机中,活动状态会沿着四面果盘做非线性运动(easeInOut好用),需要基本的运动算法来计算实际的运动距离。在动画方法中,我们可以使用ease-in/ease-out等ease算法来实现动画效果,但这里必须使用tween.js中的ease算法来实现运动效果(因为我们需要控制运动节点)。
你会想到用css的关键帧动画来做这个运动效果吗?经过我的测试,css的动画和动画会在每一边实现一个缓和的动作(非常奇怪的效果
使用其中之一来节省代码量。
/* * Tween.js * t:当前时间;* b:起始值(初始值);* c:值的变化;* d:持续时间。*/*///Quart点动const easeinoutquart=function (t,b,c,d){ if((t/=d/2)1)return c/2 * t * t * t b;return-c/2 *(t-=2)* t * t * t-2)b;}复制代码补间算法是根据时间(时间比=距离比)来计算单位时间的实际移动距离。
以上图为例,我们需要制作一个77的果盘,实际有效奖品方块数为7*4-4,有24个有效方块。
高效格算法
射流研究…
//0-6第一行所有方块有效//21-27最后一行所有方块有效//中间部分I% 7==0和I% 7==(7-1)有效//算法源代码有点无聊。根据以上思路,你可以遍历28个方块,识别奖品方块valie=true////想想6x6 5x5。
view class='水果-容器' view class='水果-表格' block wx : for=' { { ary } } ' wx : key=' index ' view wx : if=' { { item . valie } } Class=' valid ' { { item . title } }/view wx : else Class=' in-valid '/view/block/view canvas type=' 2d './/查看文案代码
只提取关键样式,从而覆盖水果盘上的画布。水果-容器{相对位置:宽度: 400 px;他
ight: 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(); // 启动复制代码
以上为我的小程序水果老虎机的基本开发思路