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

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


今天圣诞节,先预祝大家节日快乐.既然是圣诞节,那我们就来学点有意思的,用几十行代码来实现一个高性能的抽奖小游戏.也基于此,来巩固我们的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, 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) }))

延伸



在文章发布之后,有热心的小伙伴们提出了几个建议,综合如下:


  • 抽奖动画结束后提供回调来通知页面以便处理其他逻辑
  • 处理多次点击时,虽然加了防抖,但是用户在动画没结束时点击了开始按钮,又会执行动画导致动画越来越快,发生混乱.


综合以上问题,我在之前基础上做了进一步扩展,来解决以上提到的问题.


  1. 添加动画结束时回调:


/**
* 环形随机轨道运动函数
* @param {element} el 运动的dom元素
* @param {array} path 运动的环形坐标集合
* @param {func} cb 动画结束时回调
* @param {number} speed 运动的初始速度
* @param {number} i 运动的初始位置
* @param {number} len 路径的长度
* @param {number} random 中奖坐标
*/
function run(el, path, n = 1, cb, 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, cb, speed, ++i, len, random)
        }else {
          cb && cb()
        }
    }, speed)
}
  1. 处理多次点击时,虽然加了防抖,但是用户在动画没结束时点击了开始按钮,又会执行动画导致动画越来越快,发生混乱.
// 1. 点击开始按钮,开始抽奖
$('.start').on('click',debounce(() => {
    // 点击开始后禁用点击
    $('.start').css('pointer-events', 'none')
    run($('.spin'), generateCirclePath(3, 100), 3, () => {
      // 动画结束后开启按钮点击
      $('.start').css('pointer-events', 'auto')
      alert('抽奖结束')
    }) 
}))

谢谢各位认真的建议,继续优化吧.


总结



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



目录
相关文章
|
前端开发 程序员
程序员也可以很浪漫,精选10个圣诞节特效及源码
最近离圣诞节不远了、整理了一些关于圣诞相关的前端特效网页设计和小游戏的代码送大家、直接上效果吧。 代码过长的 可直接预览获取 [快速预览](https://www.hereitis.cn/articleDetails/969)
程序员也可以很浪漫,精选10个圣诞节特效及源码
|
Java 数据库 开发者
深度剖析:探寻Spring事务失效的背后
【4月更文挑战第18天】
238 0
深度剖析:探寻Spring事务失效的背后
|
SQL Java 数据库连接
在java中h2数据库的使用
H2 是一个轻量级的嵌入式数据库,可以在 Java 应用程序中使用
762 0
教大家用 Python 绘制几棵圣诞树~
今天分享五种用 Python 绘制圣诞树的方法,从基础到高级,效果也不断攀升分为 1 到 5 五个 Level 水平;
教大家用 Python 绘制几棵圣诞树~
|
存储 弹性计算 资源调度
openstack组件部署 3
openstack组件部署
312 0
|
10月前
|
数据采集 机器学习/深度学习 人工智能
探索人工智能与大数据的融合之路####
本文将深入探讨人工智能(AI)与大数据之间的共生关系,揭示二者如何相互促进,共同推动技术边界的拓展。不同于传统摘要的概述形式,本部分将以一个生动的比喻开篇:如果把大数据比作广阔无垠的数字海洋,那么人工智能就是航行其间的智能航船,两者相辅相成,缺一不可。随后,简述文章将从数据采集、处理、分析到决策应用的全流程中,详细阐述AI如何借助大数据的力量实现自我迭代与优化,以及大数据如何在AI算法的驱动下释放出前所未有的价值。最后,预告文章还将探讨当前面临的挑战与未来趋势,为读者勾勒一幅AI与大数据融合发展的宏伟蓝图。 ####
经济生产批量(Economic Production Quantity,EPQ)
经济生产批量(Economic Production Quantity,EPQ)
|
存储 数据可视化 知识图谱
使用Llama index构建多代理 RAG
检索增强生成(RAG)已成为增强大型语言模型(LLM)能力的一种强大技术。通过从知识来源中检索相关信息并将其纳入提示,RAG为LLM提供了有用的上下文,以产生基于事实的输出。
436 0
|
消息中间件 Cloud Native Kafka
一文搞懂 Kafka consumer 与 broker 交互机制与原理
AutoMQ致力于打造下一代云原生Kafka系统,解决Kafka痛点。本文深入解析Kafka Consumer与Broker的交互机制,涉及消费者角色、核心组件及常用接口。消费者以group形式工作,包括leader和follower。交互流程涵盖FindCoordinator、JoinGroup、SyncGroup、拉取消息和退出过程。文章还探讨了broker的consumer group状态管理和rebalance原理。AutoMQ团队分享Kafka技术,感兴趣的话可以关注他们。
1116 3
一文搞懂 Kafka consumer 与 broker 交互机制与原理
|
人工智能 PyTorch 算法框架/工具
AI计算机视觉笔记十四:YOLOV5环境搭建及测试全过程
本文详细记录了在Windows 10环境下从零开始搭建yolov5环境并进行测试的全过程,涵盖环境配置、依赖安装及模型测试等关键步骤。文章首先介绍了所需环境(Python 3.8、yolov5-5.0),接着详细说明了如何使用Miniconda3创建与激活虚拟环境,并通过具体命令演示了如何下载安装yolov5及相关依赖库。最后,通过一系列命令展示了如何下载预训练模型并对示例图像进行目标检测,同时解决了一些常见错误。适合初学者跟随实践。如需转载,请注明原文出处。

热门文章

最新文章