H5 小游戏开发(红包雨、打地鼠、拼图、老虎机)

一段时间没更新,是因为公司下面的子公司移动端开发需要人员去支援,好了,鄙人被派去了,主要完成四个小游戏的H5开发。接着挨个介绍这四个小游戏我是怎么完成的,以下只贴上核心代码文件,想要直接cv运行的话,可能不行嗷,看懂逻辑,自己去改也是一种学习过程!有问题私信我

红包雨

这里的需求:大小红包,点击大红包10分,小红包5分,点击之后还有打开效果,大红包:小红包比例,二比五。我这里采用的是100以内的随机数,2的整除数和5的整除数。

CountdownTimer.vue 倒计数组件,时间到,跳出游戏结束弹框

<template>
  <div class="countdown_box">
    <p class="countdown">{
   {
    formattedTime }}s</p>
  </div>
</template>

<script>
import {
    Bus } from '@/util/bus'
export default {
   
  data() {
   
    return {
   
      timeLeft: 15000, // 总时间(毫秒)
      countdownInterval: null,
      microsecondIncrement: 0, // 用于模拟微秒增加
      Count: 0
    }
  },
  computed: {
   
    formattedTime() {
   
      const seconds = Math.floor(this.timeLeft / 1000)
      const milliseconds = Math.floor((this.timeLeft % 1000) / 10)
      const microseconds = this.microsecondIncrement % 100
      return `${
     seconds.toString().padStart(2, '0')}:${
     milliseconds
        .toString()
        .padStart(2, '0')}:${
     microseconds.toString().padStart(2, '0')}`
    }
  },
  mounted() {
   
    this.startCountdown()
    Bus.$on('Total', Total => {
   
      this.Count = Total
    })
  },
  beforeDestroy() {
   
    this.stopCountdown()
  },
  methods: {
   
    startCountdown() {
   
      this.countdownInterval = setInterval(() => {
   
        if (this.timeLeft > 0) {
   
          this.timeLeft -= 10 // 每10毫秒减少10毫秒
          this.microsecondIncrement++ // 模拟微秒增加
          if (this.microsecondIncrement >= 1000) {
   
            this.microsecondIncrement = 0 // 重置微秒模拟
          }
        } else {
   
          this.stopCountdown()
          Bus.$emit('countdownEnd', this.Count)
        }
      }, 10) // 每10毫秒更新一次时间
    },
    stopCountdown() {
   
      clearInterval(this.countdownInterval)
    }
  }
}
</script>
<style lang="less" scoped>
.countdown_box {
   
  text-align: center;
  color: red;
  .countdown {
   
    font-size: 9vw;
  }
}
</style>

index.vue 核心代码

<template>
  <div class="rain-wrapper">
    <CountdownTimer style="margin-top: 7.0667vw" ref="cutdown"></CountdownTimer>
    <div ref="rainBox" id="rainBox" class="rainBox">
      <rain-point
        v-for="(item, idx) in rains"
        :key="`rain-point-${idx}`"
        :ref="`rain-point-${idx}`"
        @rainPoinclick="rainPoinclick"
      ></rain-point>
    </div>
  </div>
</template>

<script>
import rainPoint from './tpl.vue'
import countdown from './countdown'
import CountdownTimer from '../CountdownTimer.vue'
import {
    Bus } from '@/util/bus.js'
export default {
   
  name: 'rain',
  props: {
   
    density: {
   
      // 雨点创建速度
      type: Number,
      default: 600
    },
    delay: {
   
      // 雨点时长
      type: Number,
      default: 5
    },
    time: {
   
      // 动画时长(秒)
      type: Number,
      default: 10
    }
  },
  data() {
   
    return {
   
      count: 0, // 点击统计
      rains: [], // 组件列表
      rainsCount: 0, // 组件下标
      createTimer: null, // 创建雨点计时器
      flag: true, // 是否结束
      isFiveCount: 0,
      isTenCount: 0,
      event: ''
    }
  },
  components: {
   
    rainPoint,
    CountdownTimer
  },
  methods: {
   
    // 结束后回调
    timeoutCallback() {
   
      this.$emit('timeoutCallback', this.count, this.isFiveCount, this.isTenCount)
    },

    // 点击雨点
    rainPoinclick(e) {
   
      if (!this.flag) return
      this.count += 1
      if (e.target._prevClass == 'rain-point_small') this.isFiveCount += 1
      if (e.target._prevClass == 'rain-point_big') this.isTenCount += 1
      Bus.$emit('Total', this.Total)
    },
    // 生成随机起始与落点坐标
    grid() {
   
      let [startX, startY, endX, endY] = [0, 0, 0, 0]
      let rects = document.documentElement.getBoundingClientRect()
      startX = Math.random() * (rects.width - 20)
      startY = -20
      endX = Math.random() * (rects.width - 20)
      endY = rects.height

      return {
   
        startX,
        startY,
        endX,
        endY
      }
    },

    // 随机速度曲线值
    newCubicBezier() {
   
      let arr = ['0,.49,1,.3', '.04,.2,.93,.49', '.99,.36,.54,.46'] // 快 中 慢
      // let idx = parseInt(Math.random() * 10) > 2 ? 0 : 1
      let idx = parseInt(Math.random() * 3)
      return arr[idx]
    },

    // 创建雨点
    async create(rainscount) {
   
      // 生成Dom
      this.rains.push(`rain-point-${
     rainscount}`)
      // 生成坐标
      let rects = await this.grid()

      // 渲染完成后执行
      await this.$nextTick(async function () {
   
        // Dom节点
        let el = this.$refs[`rain-point-${
     rainscount}`][0]

        let initStyleText = {
   
          transform: `translate(${
     rects.startX}px, ${
     rects.startY}px)`
        }
        let actionStyleText = {
   
          transition: `transform ${
     this.delay}s cubic-bezier(${
     this.newCubicBezier()})`,
          transform: `translate(${
     rects.endX}px, ${
     rects.endY}px)`
        }
        // 设置初始坐标
        await el.setStyle(initStyleText)
        // 设置结束坐标
        await setTimeout(() => {
   
          el.setStyle(actionStyleText)
        }, 50)
        // 动画结束
        el.$el.addEventListener('transitionend', el.destory, false)
      })
    },

    // 执行
    start() {
   
      // this.$nextTick(() => {
   
      //   this.$refs.cutdown.startCountdown()
      // })
      this.clear()
      // 开启雨点点击
      this.flag = true
      // 重置点击数
      this.count = 0
      // 清除动画定时器
      countdown.clearAssignTimer('rain')
      // 动画定时器
      countdown.creatTimer({
   
        remainTime: parseInt(this.time) * 1000,
        label: 'rain',
        timeoutFn: () => {
   
          this.clear()
          this.timeoutCallback()
        }
      })
      // 创建节点
      this.createTimer = setInterval(async () => {
   
        await this.create(this.rainsCount)
        this.rainsCount += 1
      }, this.density)
    },

    // 停止
    stop() {
   
      this.flag = false
      clearInterval(this.createTimer)
    },

    // 清空
    clear() {
   
      this.stop()
      countdown.clearAssignTimer('rain')
      this.rains = []
      this.rainsCount = 0
    }
  },
  computed: {
   
    Total() {
   
      return this.isFiveCount * 5 + this.isTenCount * 10
    }
  },
  mounted() {
   
    // window.start = this.start
    // window.stop = this.stop
    // window.clear = this.clear
    // this.start()
  }
}
</script>

<style>
.rain-wrapper {
   
  width: 100%;
  height: 250vw;
  /* height: 100%; */
  overflow: hidden;
  position: relative;
}
.rainBox {
   
  width: 100%;
}
</style>

tpl.vue

<template>
  <div class="tyh">
    <div
      v-if="this.isShow"
      @click="rainPoinclick"
      :class="[
        isClick ? 'action' : '',
        this.isEven ? 'rain-point_small' : this.MultiFive ? 'rain-point_small' : 'rain-point_big'
      ]"
      :style="styleText"
    >
      <transition name="number-fade">
        <div class="addNum" v-if="showNumber">
          <span>+</span>
          <span>{
   {
    this.isEven ? 5 : this.MultiFive ? 5 : 10 }}</span>
        </div>
      </transition>
    </div>
  </div>
</template>

<script>
export default {
   
  name: 'rain-point',
  props: {
   },
  data() {
   
    return {
   
      styleText: {
   },
      isShow: true,
      isClick: false,
      // 红包
      total: 0,
      showNumber: false,
      event: ''
    }
  },
  methods: {
   
    rainPoinclick(e) {
   
      this.event = e
      if (this.isClick) return
      this.isClick = true
      this.showNumber = true
      // 在动画完成后隐藏数字
      this.$nextTick(() => {
   
        setTimeout(() => {
   
          this.showNumber = false
        }, 500) // 动画持续时间,与CSS中的transition-duration保持一致
      })
      this.$emit('rainPoinclick', e)
    },
    setStyle(params) {
   
      this.styleText = params
    },
    destory() {
   
      this.isShow = false
    },
    created() {
   }
  },
  computed: {
   
    isEven() {
   
      return Math.floor(Math.random() * 100 + 1) % 2 === 0
    },
    MultiFive() {
   
      return Math.floor(Math.random() * 100 + 1) % 10 === 0
    }
  },
  watch: {
   }
}
</script>

<style lang="less" scoped>
.tyh {
   
  .rain-point_small {
   
    position: absolute;
    width: 51px;
    height: 65px;
    background-image: url('~@/assets/image/draw/red-envelope-rain/envelope_small.png');
    border-radius: 2.5vw;
  }
  .rain-point_big {
   
    position: absolute;
    width: 75px;
    height: 95px;
    background-image: url('~@/assets/image/draw/red-envelope-rain/envelope_big.png');
    border-radius: 2.5vw;
  }
  .action {
   
    width: 90px;
    height: 120px;
    background-image: url('~@/assets/image/draw/red-envelope-rain/envelope_open.png');
  }
  .number-fade-enter-active,
  .number-fade-leave-active {
   
    transition: opacity 0.5s;
  }
  .number-fade-enter,
  .number-fade-leave-to {
   
    opacity: 0;
  }
  .addNum {
   
    font-size: 9vw;
    color: red;
    position: absolute;
  }
}
</style>

打地鼠

倒计时组件和上述红包雨的一样,需要的话自行改造

<template>
  <div class="draw-box">
    <div v-if="centerDialogVisible" class="cover">
      <div class="dialog">
        <div class="el-dialog__header">
          <div class="title">
            {
   {
    isTrue ? '挑战成功!' : '挑战失败!' }}
          </div>
          <img
            :style="isTrue ? 'top:-31vw' : 'top:-22vw'"
            :src="
              isTrue
                ? require('@/assets/image/draw/red-envelope-rain/success.png')
                : require('@/assets/image/draw/red-envelope-rain/fail.png')
            "
          />
        </div>
        <div class="el-dialog__body">
          <div class="content">
            <h1 style="margin-top: 7.7333vw">
              您的成绩为:
              <span style="color: #fa3e56">{
   {
    RankCount }}</span>
            </h1>
            <span
              class="tip"
              v-if="!isTrue"
              :style="isTrue ? 'margin-bottom: 4vw,margin-top:1.333vw' : ''"
            >
              成绩必须达到80分才能抽奖
            </span>
            <div class="divider1"></div>
            <div class="the_Best">
              <div class="BestGrade" style="margin-right: 6vw">
                <span class="text">最佳成绩</span>
                <span class="count">{
   {
    rankBestgrade.bestPoint || RankCount }}</span>
              </div>
              <div class="vertical-divider"></div>
              <div class="BestGrade" style="margin-left: 6vw">
                <span class="text">最佳排名</span>
                <span class="count">NO.{
   {
    rankBestgrade.ranking || 1 }}</span>
              </div>
            </div>
            <div class="divider2"></div>
            <span v-show="isTrue" style="margin-top: 4vw">
              今天还有
              <span style="color: #fa3e56">{
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值