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

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

效果展示

将收获

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

设计思路

具体实现

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

1. 九宫格布局实现

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

                <div class="wrap">
    <div class="title">圣诞抽抽乐</div>
    <div class="box">
        <div class="item">我爱你</div>
        <div class="item">你爱我</div>
        <div class="item">我不爱你</div>
        <div class="item">你爱我</div>
        <div class="item start">开始</div>
        <div class="item">你爱我</div>
        <div class="item">再见</div>
        <div class="item">谢谢惠顾</div>
        <div class="item">你爱我</div>
        <div class="spin"></div>
    </div>
</div>
            

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

                .box {
    display: flex;
    flex-wrap: wrap;
    width: 300px;
    height: 300px;
    position: relative;
    .item {
        box-sizing: border-box;
        width: 100px;
    }
    // 滑块
    .spin {
        box-sizing: border-box;
        position: absolute;
        left: 0;
        top: 0;
        display: inline-block;
        width: 100px;
        height: 100px;
        background-color: rgba(0,0,0,.2);
    }
}
            

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

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

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

                /**
 * 生成n维环形坐标
 * @param {number} n 维度
 * @param {number} cell 单位坐标长度
 */
function generateCirclePath(n, cell) {
  let arr = []
  for(let i=0; i< n; i++) {
      arr.push([i*cell, 0])
  }
  for(let i=0; i< n-1; i++) {
      arr.push([(n-1)*cell, (i+1)*cell])
  }
  for(let i=0; i< n-1; i++) {
      arr.push([(n-i-2)*cell, (n-1)*cell])
  }
  for(let i=0; i< n-2; i++) {
      arr.push([0, (n-i-2)*cell])
  }
  return arr
}
            

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

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

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

                function run(el, path, n = 1, i = 0, len = path.length) {
    setTimeout(() => {
        if(n > 0) {
          if(len <= i) {
              i = n === 1 ? len : 0
              n--
          }
          el.css('transform', `translate(${path[i][0]}px, ${path[i][1]}px)`)
          run(el, path, n, ++i, len)
        }
    }, 300)   
}
            

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

3.1 加速度运动

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

                function run(el, path, n = 1, speed = 60, i = 0, len = path.length) {
    setTimeout(() => {
        if(n > 0) {
          if(len <= i) {
              i = n === 1 ? len : 0
              n--
              speed += (300 - speed) / n
          }
          el.css('transform', `translate(${path[i][0]}px, ${path[i][1]}px)`)
          run(el, path, n, speed, ++i, len)
        }
    }, speed)   
}
            
3.2 随机停止实现

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

                /**
* 环形随机轨道运动函数
* @param {element} el 运动的dom元素
* @param {array} path 运动的环形坐标集合
* @param {number} speed 运动的初始速度
* @param {number} i 运动的初始位置
* @param {number} len 路径的长度
* @param {number} random 中奖坐标
*/
function run(el, path, n = 1, speed = 60, i = 0, len = path.length, random = Math.floor(Math.random() * len)) {
    setTimeout(() => {
        if(n > 0) {
          // 如果n为1,则设置中奖数值
          if(n === 1) {
            len = random
          }
          if(len <= i) {
              i = n === 1 ? len : 0
              n--
              speed += (300 - speed) / n
          }
          el.css('transform', `translate(${path[i][0]}px, ${path[i][1]}px)`)
          run(el, path, n, speed, ++i, len, random)
        }
    }, speed)   
}
            
4.实现点击开始的防抖函数以及应用

防抖函数实现:

                // 防抖函数,避免频繁点击执行多次函数
function debounce(fn, interval = 300) {
  let timeout = null
  return function () {
      clearTimeout(timeout)
      timeout = setTimeout(() => {
          fn.apply(this, arguments)
      }, interval)
  }
}
            

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

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

总结

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

最后

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

更多推荐

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

圣诞节 祝福网站 全部源码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <!--design by www.618cc.com--> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> <title>圣诞节的祝福! 西安工程大学艺纺论坛</title> <style type="text/css"> <!-- body { margin: 0px; padding:0px; background:url(img/bbgg.jpg) repeat-x; } img {border:none;} form { padding:0; margin:0} a:link { color: #FF3366; text-decoration: none; } a:visited { text-decoration: none; color: #FF3366; } a:hover { text-decoration: underline; color: #999999; } a:active { text-decoration: none; } .STYLE1 {color: #003366} --> </style> <script> function click() { if (event.button==2) { alert('艺纺论坛祝大家梦想成真!www.efangbbs.com ') } } document.onmousedown=click </script> <SCRIPT> <!-- var lusername =location.search.split("=")[1]; if(!lusername){ lusername="艺纺论坛"; } else{ lusername=unescape(lusername); } function check(obj){ if (obj.stra.value.length>28) { alert('晕,咋还有这么长啊?~'); return false; } if (obj.stra.value.length==0) { alert('还没填姓名呀'); return false; } var url ="http://"+location.host+location.pathname+"?stra="+escape(obj.stra.value); window.clipboardData.setData("Text",url); alert('网址已生成并替您复制好了,直接按 Ctrl+V 粘贴到QQ / MSN等即时通信工具或邮箱就可以了!'); window.location.replace(url); return false; } --> </SCRIPT> <!--SCRIPT language=JavaScript src="snow.js"></SCRIPT--> <BGSOUND loop=infinite src="jinbells.mid" tppabs="jinbells.mid"> </head> <body> <div style="width:1000px; margin:0 auto; height:auto"> <div style="height:626px;"> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td style="height:626px; width:104px;" rowspan="2"><img src="img/01.jpg" width="104" height="626" /></td> <td valign="top" style="height:350px; width:792px; background:url(img/001.gif) no-repeat"> <div style="padding-left:95px; padding-top:190px; font-weight:bold; font-size:30px; color:#CC3366; font-family:"黑体";">亲爱的:<font color="#1E9AFF"><SCRIPT> var b=document;var c=lusername;b.write(c); </SCRIPT></font></div> </td> <td style="height:626px; width:104px;" rowspan="2"><img src="img/003.jpg" width="104" height="626" /></td> </tr> <tr> <td valign="top" style="height:276px; width:792px; background:url(img/002.jpg) no-repeat"> <div style="padding-left:180px; padding-top:230px; font-weight:bold; font-size:14px; color:#CC3366;">亲爱的:<SCRIPT> var b=document;var c=lusername;b.write(c); </SCRIPT>,圣诞!</div> </td> </tr> </table> </div> <div style="background:url(img/bg.gif) repeat-y; height:auto; text-align:center; padding-top:20px;"><img src="img/004.gif" /></div> <div style="background:url(img/bg.gif) repeat-y; height:auto; text-align:center; padding-top:25px;"><img src="img/005.gif" /></div> <div style=" background:url(img/006.gif) no-repeat; height:343px;"> <div style="padding-left:250px; padding-top:60px; font-weight:bold; line-height:18px; font-size:12px; color:#CC3366;">圣诞节快要到了,赶快在第一时间给你的朋友们送上你的祝福吧,<br /> 他们一定会很惊喜的!在下面填入你朋友的姓名,点击获得祝福网页,<br /> 就可以得到一个属于你的祝福网页,里面就是你朋友的名字哦!<br /> 赶紧发送给他们让他们得到一个意想不到的惊喜吧! <br /> <form name="newinfo_form" id="newinfo_form" onSubmit="return check(this)"> <strong><font color=red>在此输入您要祝福人的姓名:</font></strong> <input name="stra" type="text" id="stra" tabindex="1" value="" size="16" style="width:100px;">   <input type="submit" value="点此提交" name="Submit" /> </form></div> <div style="padding-right:120px; padding-top:80px; font-size:12px; text-align:right; color:#CC3366;">你可以直接发送网址:http://www.efangbbs.com/happy/?stra=<span class="STYLE1">###</span> 给你朋友 (把<span class="STYLE1">###</span>替换成你朋友名字即可)<br /> <br /> <br /> <br /> <br /> <br /> 创意支持:<a href="http://www.efangbbs.com" target="_blank">西安工程大学艺纺论坛</a> <a href="http://www.mutoulee.cn" target="_blank">体无完肤</a><br /> </div> </div> </div> <div style="position:absolute; left:0px; top:0px; z-index:99; height:auto"><script type="text/javascript"> var swf_width=1000; var swf_height=1100; swf_width=document.documentElement.clientWidth; document.write('<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0" width="'+ swf_width +'" height="'+ swf_height +'">'); document.write('<param name="movie" value="http://www.wzfzl.cn/flash/01/23.swf"><param name="quality" value="high">'); document.write('<param name="wmode" value="transparent">'); document.write('<embed src="http://www.wzfzl.cn/flash/01/23.swf" quality="high" width="'+ swf_width +'" height="'+ swf_height +'" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />'); document.write('</object>'); </script></div> </body> </html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值