飞道的博客

用60行代码实现一个高性能的圣诞抽抽乐H5小游戏(含源码)

427人阅读  评论(0)

今天圣诞节,先预祝大家节日快乐.既然是圣诞节,那我们就来学点有意思的,用几十行代码来实现一个高性能的抽奖小游戏.也基于此,来巩固我们的javascript基础,以及前端一些基本算法的应用

效果展示

将收获

防抖函数的应用用css实现九宫格布局生成n维环形坐标的算法如何实现环形随机轨道运动函数实现加速度动画性能分析与优化

设计思路

具体实现

由于目前已有很多方案可以实现九宫格抽奖动画,比如使用动态active实现边框动画,用随机算法和定时器设置在何处停止等等. 为了进一步提高性能,本文介绍的方法,将使用坐标法,将操作dom的成本降低,完全由js实现滑块的路径的计算,滑块元素采用绝对定位,让其脱离文档流,避免其他元素的重绘等等,最后点击按钮我们会使用防抖函数来避免频繁执行函数,造成不必要的性能损失.

1. 九宫格布局实现

为了让大家更加熟悉dom结构,这里我就不用js动态生成了.如下html结构:


   
  1. <div class="wrap">
  2. <div class="title">圣诞抽抽乐 </div>
  3. <div class="box">
  4. <div class="item">我爱你 </div>
  5. <div class="item">你爱我 </div>
  6. <div class="item">我不爱你 </div>
  7. <div class="item">你爱我 </div>
  8. <div class="item start">开始 </div>
  9. <div class="item">你爱我 </div>
  10. <div class="item">再见 </div>
  11. <div class="item">谢谢惠顾 </div>
  12. <div class="item">你爱我 </div>
  13. <div class="spin"> </div>
  14. </div>
  15. </div>

九宫格布局我们使用flex来实现,核心代码如下:


   
  1. .box {
  2. display: flex;
  3. flex-wrap: wrap;
  4. width: 300px;
  5. height: 300px;
  6. position: relative;
  7. .item {
  8. box-sizing: border-box;
  9. width: 100px;
  10. }
  11. // 滑块
  12. .spin {
  13. box-sizing: border-box;
  14. position: absolute;
  15. left: 0;
  16. top: 0;
  17. display: inline-block;
  18. width: 100px;
  19. height: 100px;
  20. background-color: rgba(0,0,0,.2);
  21. }
  22. }

由上可知容器box采用flex布局,要想让flex子元素换行,我们这里要设置flex-wrap: wrap;此时九宫格布局就实现了. 滑块采用绝对定位,至于具体如何去沿着环形轨道运动,请继续看下文介绍.

2.生成n维环形坐标的算法

由上图我们可以知道,一个九宫格的4条边,可以用以上8个坐标收尾连接起来,那么我们可以基于这个规律.来生成环形坐标集合.代码如下:


   
  1. /**
  2. * 生成n维环形坐标
  3. * @param {number} n 维度
  4. * @param {number} cell 单位坐标长度
  5. */
  6. function generateCirclePath(n, cell) {
  7. let arr = []
  8. for( let i= 0; i< n; i++) {
  9. arr.push([i*cell, 0])
  10. }
  11. for( let i= 0; i< n -1; i++) {
  12. arr.push([(n -1)*cell, (i+ 1)*cell])
  13. }
  14. for( let i= 0; i< n -1; i++) {
  15. arr.push([(n-i -2)*cell, (n -1)*cell])
  16. }
  17. for( let i= 0; i< n -2; i++) {
  18. arr.push([ 0, (n-i -2)*cell])
  19. }
  20. return arr
  21. }

如果是单位坐标,那么cell为1,cell设计的目的就位为了和现实的元素相结合,我们可以手动设置单元格的宽度来实现不同大小的n维环形坐标集.

3.实现环形随机轨道运动函数

由抽奖动画分析可知,我们滑块运动的轨迹,其实就是环形坐标集合,所以我们只要让滑块的顶点(默认左上角)沿着环形坐标集合一步步变化就好了.


   
  1. function run(el, path, n = 1, i = 0, len = path.length) {
  2. setTimeout( () => {
  3. if(n > 0) {
  4. if(len <= i) {
  5. i = n === 1 ? len : 0
  6. n--
  7. }
  8. el.css( 'transform', `translate(${path[i][0]}px, ${path[i][1]}px)`)
  9. run(el, path, n, ++i, len)
  10. }
  11. }, 300)
  12. }

这样就能实现我们的滑块按照九宫格边框运动的动画了,当然以上函数只是基本的动画, 还没有实现在随机位置停止, 以及滑块的加速度运动,这块需要一定的技巧和js基础知识比如闭包.

3.1 加速度运动

加速度运动其实很简单,比如每转过一圈将setTimeout的延迟时间改变即可.代码如下:


   
  1. function run(el, path, n = 1, speed = 60, i = 0, len = path.length) {
  2. setTimeout( () => {
  3. if(n > 0) {
  4. if(len <= i) {
  5. i = n === 1 ? len : 0
  6. n--
  7. speed += ( 300 - speed) / n
  8. }
  9. el.css( 'transform', `translate(${path[i][0]}px, ${path[i][1]}px)`)
  10. run(el, path, n, speed, ++i, len)
  11. }
  12. }, speed)
  13. }

3.2 随机停止实现

随机停止这块主要是用了Math.random这个API, 我们在最后一圈的时候, 根据随机返回的数值来决定何时停止,这里我们在函数内部实现随机数值,完整代码如下:


   
  1. /**
  2. * 环形随机轨道运动函数
  3. * @param {element} el 运动的dom元素
  4. * @param {array} path 运动的环形坐标集合
  5. * @param {number} speed 运动的初始速度
  6. * @param {number} i 运动的初始位置
  7. * @param {number} len 路径的长度
  8. * @param {number} random 中奖坐标
  9. */
  10. function run(el, path, n = 1, speed = 60, i = 0, len = path.length, random = Math.floor(Math.random() * len)) {
  11. setTimeout( () => {
  12. if(n > 0) {
  13. // 如果n为1,则设置中奖数值
  14. if(n === 1) {
  15. len = random
  16. }
  17. if(len <= i) {
  18. i = n === 1 ? len : 0
  19. n--
  20. speed += ( 300 - speed) / n
  21. }
  22. el.css( 'transform', `translate(${path[i][0]}px, ${path[i][1]}px)`)
  23. run(el, path, n, speed, ++i, len, random)
  24. }
  25. }, speed)
  26. }

4.实现点击开始的防抖函数以及应用

防抖函数实现:


   
  1. // 防抖函数,避免频繁点击执行多次函数
  2. function debounce(fn, interval = 300) {
  3. let timeout = null
  4. return function () {
  5. clearTimeout(timeout)
  6. timeout = setTimeout( () => {
  7. fn.apply( this, arguments)
  8. }, interval)
  9. }
  10. }

那么我们点击时,代码应该长这样:


   
  1. // 点击开始按钮,开始抽奖
  2. $( '.start').on( 'click',debounce( () => { run($( '.spin'), generateCirclePath( 3, 100), 3) }))

总结

该实现方式的好处是支持n维环形坐标的抽奖,基于坐标法的应用还有很多,尤其是游戏和图形领域,在实现过程中一定要考虑性能和可扩展性,这样我们就可以在不同场景使用同一套方法论,岂不乐哉?本文完整源码我会放在github上,欢迎交流学习~

最后

如果想了解更多H5游戏, webpacknodegulpcss3javascriptnodeJScanvas数据可视化等前端知识和实战,关注《趣谈前端》加入我们一起学习讨论,共同探索前端的边界。

更多推荐

欢迎关注下方公众号,回复 lodash,将获取本人亲自翻译的lodash API中文
思维导图源文档。


转载:https://blog.csdn.net/KlausLily/article/details/103707806
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场