zoukankan      html  css  js  c++  java
  • 2048小游戏源码(vue自定义指令使用)

    不多说,直接上代码

    <template>
      <div class="gameMain">
        <div class="gameName">2048小游戏</div>
        <div class="maxScore">
          最高分:<span id="maxScore">{{ maxScore }}</span>
        </div>
        <div class="col-sm-3 col-md-4"></div>
        <div class="gameBody col-sm-6 col-md-4" id="gameBody" v-touch:left="move" v-touch:right="move" v-touch:up="move" v-touch:down="move">
          <div class="row" v-for="(row, index) in gameList" :key="index">
            <div
              class="item"
              :style="{ background: refreshColorData[item.num] }"
              v-for="(item, idx) in row"
              :key="idx"
            >
              {{ item ? item.num : null }}
            </div>
          </div>
        </div>
        <div class="col-sm-4 col-md-4 gameDirection">
          <span @click="move('up')">上</span>
          <span @click="move('down')">下</span>
          <span @click="move('left')">左</span>
          <span @click="move('right')">右</span>
        </div>
        <div class="scoreAndRefresh col-sm-6 col-md-6">
          <div class="gameScore ">
            得分:<span id="gameScore">{{ gameScore }}</span> 分
          </div>
          <div class="btn btn-danger refreshBtn" @click="refreshGame">刷新</div>
        </div>
    
       <div class="gameOver" v-if="gameOver">游戏结束</div>
      </div>
    </template>
    
    <script>
    import touch from './directives.js'
    export default {
      name: 'Games',
      directives: { touch },
      data () {
        return {
          refreshColorData: {
            2: 'rgb(250, 225, 188)',
            4: 'rgb(202, 240, 240)',
            8: 'rgb(117, 231, 193)',
            16: 'rgb(240, 132, 132)',
            32: 'rgb(181, 240, 181)',
            64: 'rgb(182, 210, 246)',
            128: 'rgb(255, 207, 126)',
            256: 'rgb(250, 216, 216)',
            521: 'rgb(124, 183, 231)',
            1024: 'rgb(225, 219, 215)',
            2048: 'rgb(221, 160, 221)',
            4096: 'rgb(250, 139, 176)'
          },
          gameOver: false,
          gameScore: 0,
          maxScore: 0, // 最高分
          gameList: null,
          isNewRndItem: false //   // 是否产生新元素
        }
      },
      created () {
        this.gameList = this.matrix(4, 4, null)
        // 游戏初始化
        this.gameInit()
      },
      methods: {
        gameInit () {
          // 初始化分数
          this.gameScore = 0
          this.gameScore = 0
          // 最大分值
          if (localStorage.getItem('maxScore')) {
            this.maxScore = localStorage.getItem('maxScore') - 0
          } else {
            this.maxScore = 0
          }
          // 随机生成两个新元素
          this.newRndItem()
          this.newRndItem()
        },
        move (direction) {
          if (this.gameOver) return false
          // 获取所有非空元素
          let nonEmptyItems = [].concat
            .apply([], this.gameList)
            .filter(item => item.num !== null)
          // 如果按下的方向是左或上,则正向遍历非空元素
          if (direction === 'left' || direction === 'up') {
            for (let i = 0; i < nonEmptyItems.length; i++) {
              let currentItem = nonEmptyItems[i]
              this.itemMove(currentItem, direction)
            }
          } else if (direction === 'right' || direction === 'down') {
            // 如果按下的方向是右或下,则反向遍历非空元素
            for (let i = nonEmptyItems.length - 1; i >= 0; i--) {
              let currentItem = nonEmptyItems[i]
              this.itemMove(currentItem, direction)
            }
          }
          // 是否产生新元素
          if (this.isNewRndItem && !this.gameOver) {
            this.newRndItem()
          }
          this.isGameOver()
        },
        getSideItem (current, direction) {
          let sideItemX = current.id.substr(0, 1)
          let sideItemY = current.id.slice(1, 2)
          let falg
          switch (direction) {
            case 'left':
              falg = sideItemX > 0
              sideItemX = falg ? Number(sideItemX) - 1 : sideItemX
              break
            case 'right':
              falg = sideItemX < 3
              sideItemX = falg ? Number(sideItemX) + 1 : sideItemX
              break
            case 'up':
              falg = sideItemY > 0
              sideItemY = falg ? Number(sideItemY) - 1 : sideItemY
              break
            case 'down':
              falg = sideItemY < 3
              sideItemY = falg ? Number(sideItemY) + 1 : sideItemY
              break
          }
          let currentId = sideItemX + sideItemY
          let currentItem = falg
            ? [].concat(...this.gameList).filter(item => item.id === currentId)[0]
            : null
          // 判断移动方向是否有空位
          return currentItem
        },
        itemMove (currentItem, direction) {
          var sideItem = this.getSideItem(currentItem, direction)
          // 当前元素在最边上
          if (sideItem === null) return false
          // 当前元素不在最后一个且左(右、上、下)侧元素是空元素
          if (sideItem.num === null) {
            this.setGameList(sideItem, currentItem.num)
            sideItem.num = currentItem.num
            currentItem.num = null
            this.itemMove(sideItem, direction)
            this.isNewRndItem = true
          } else if (sideItem.num === currentItem.num) {
            sideItem.num = Number(currentItem.num) * 2
            currentItem.num = null
            this.gameScore += Number(sideItem.num) * 10
            this.maxScore =
              this.maxScore < this.gameScore ? this.gameScore : this.maxScore
            localStorage.setItem('maxScore', this.maxScore)
            this.itemMove(sideItem, direction)
            this.isNewRndItem = true
          }
        },
        // 游戏是否结束
        isGameOver () {
          let nonEmptyItems = [].concat
            .apply([], this.gameList)
            .filter(item => item.num !== null)
          let Items = [].concat
            .apply([], this.gameList)
          let gameOver = true
          if (Items.length === nonEmptyItems.length) { // 所有元素的个数 == 所有非空元素的个数  即没有空元素
            nonEmptyItems.forEach(currentItem => {
              // let up = this.getSideItem(currentItem, 'up') && this.getSideItem(currentItem, 'up').num
              // let down = this.getSideItem(currentItem, 'down') && this.getSideItem(currentItem, 'down').num
              // let left = this.getSideItem(currentItem, 'left') && this.getSideItem(currentItem, 'left').num
              // let right = this.getSideItem(currentItem, 'right') && this.getSideItem(currentItem, 'right').num
              // console.log(up + 'up' + down + 'down' + left + 'left' + right + 'right')
              // alert(currentItem.num + 'up' + this.getSideItem(currentItem, 'up').num + 'down' + this.getSideItem(currentItem, 'down').num + 'left' + this.getSideItem(currentItem, 'left').num + 'right' + this.getSideItem(currentItem, 'right').num)
              if (this.getSideItem(currentItem, 'up') && currentItem.num === this.getSideItem(currentItem, 'up').num) {
                gameOver = false
              } else if (this.getSideItem(currentItem, 'down') && currentItem.num === this.getSideItem(currentItem, 'down').num) {
                gameOver = false
              } else if (this.getSideItem(currentItem, 'left') && currentItem.num === this.getSideItem(currentItem, 'left').num) {
                gameOver = false
              } else if (this.getSideItem(currentItem, 'right') && currentItem.num === this.getSideItem(currentItem, 'right').num) {
                gameOver = false
              }
            })
          } else {
            gameOver = false
          }
          this.gameOver = gameOver
        },
        // 随机生成新数字
        newRndItem () {
          var newRndArr = [2, 2, 4]
          var newRndNum = newRndArr[this.getRandom(0, 2)]
          let emptyItemList = [].concat
            .apply([], this.gameList)
            .filter(item => item.num === null)
          var newRndSite = this.getRandom(0, emptyItemList.length - 1)
          var emptyItem = emptyItemList[newRndSite]
          this.setGameList(emptyItem, newRndNum)
        },
        // 设置数字
        setGameList (item, num) {
          if (!item) return false
          for (var row = 0; row < this.gameList.length; ++row) {
            for (var col = 0; col < this.gameList[row].length; ++col) {
              if (this.gameList[row][col].id === item.id) {
                this.gameList[row][col].num = num
              }
            }
          }
        },
        // 产生随机数,包括min、max
        getRandom (min, max) {
          return min + Math.floor(Math.random() * (max - min + 1))
        },
        // 刷新操作
        refreshGame () {
          this.gameList = this.matrix(4, 4, null)
          this.gameOver = false
          // 游戏初始化
          this.gameInit()
        },
        // 随机生成一个两位数组
        matrix (numrows, numcols, initial) {
          var arr = []
          for (var i = 0; i < numrows; ++i) {
            var columns = []
            for (var j = 0; j < numcols; ++j) {
              columns[j] = { id: j + '' + i, num: initial }
              // columns[j] = initial
            }
            arr[i] = columns
          }
          return arr
        }
      }
    }
    </script>
    
    <style scoped lang="scss">
    // @import "./index.scss";
    .gameMain {
      height: calc(100vh - 88px);
      font-size: 28px;
      background: #d7d3b6;
      .gameName {
        font-size: 28px;
        font-weight: bold;
        padding-top: 20px;
      }
      .maxScore {
        font-size: 38px;
        margin: 20px auto;
        span {
          color: red;
          font-weight: bold;
        }
      }
      .gameBody {
         80%;
        height: 50%;
        margin: 0 auto;
        display: flex;
        flex-direction: column;
        justify-content: space-between;
        padding: 10px;
        background: #999;
        border-radius: 8px;
        padding-top: 5px;
        padding-bottom: 5px;
        .row {
          display: flex;
          justify-content: space-between;
          .item {
             100px;
            height: 100px;
            border-radius: 10px;
            background: #fff;
            text-align: center;
            line-height: 100px;
            font-size: 30px;
            font-weight: bold;
            margin: 5px;
            color: #666;
          }
        }
      }
      .gameDirection {
        margin: 50px auto;
        font-size: 26px;
        font-weight: bold;
        span {
           100px;
          display: inline-block;
        }
      }
      .gameRule {
        font-size: 26px;
        font-weight: bold;
        margin-top: 5px;
      }
      .gameScore {
        font-size: 20px;
        font-weight: bold;
        line-height: 40px;
        span {
          color: red;
          font-size: 30px;
        }
      }
      .scoreAndRefresh {
        display: flex;
        justify-content: space-around;
            align-items: center;
         280px;
        margin: 20px auto;
        .refreshBtn {
          padding:10px 20px;
        line-height: 40px;
          margin-top: 8px;
          background: #093233;
          color: #fff;
          border-radius: 6px;
        }
      }
    
      .gameOver{
          color: red;
          font-size: 40px;
      }
    }
    </style>

    注释

    v-touch:left,v-touch:right,v-touch:up,v-touch:down
    使用vue自定义指令

    const touch = {
      bind (el, binding, vnode) {
        console.log(binding)
        // 滑动指令
        var touchType = binding.arg // 传入的模式 press swipeRight swipeLeft swipeTop swipeDowm Tap
        var timeOutEvent = 0
        var direction = ''
        // 滑动处理
        var startX, startY
    
        // 返回角度
        function GetSlideAngle (dx, dy) {
          return Math.atan2(dy, dx) * 180 / Math.PI
        }
    
        // 根据起点和终点返回方向 1:向上,2:向下,3:向左,4:向右,0:未滑动
        function GetSlideDirection (startX, startY, endX, endY) {
          var dy = startY - endY
          var dx = endX - startX
          var result = 0
    
          // 如果滑动距离太短
          if (Math.abs(dx) < 2 && Math.abs(dy) < 2) {
            return result
          }
    
          var angle = GetSlideAngle(dx, dy)
          if (angle >= -45 && angle < 45) {
            result = 'right'
          } else if (angle >= 45 && angle < 135) {
            result = 'up'
          } else if (angle >= -135 && angle < -45) {
            result = 'down'
          } else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) {
            result = 'left'
          }
          return result
        }
    
        el.addEventListener('touchstart', function (ev) {
          startX = ev.touches[0].pageX
          startY = ev.touches[0].pageY
    
          // 判断长按
          timeOutEvent = setTimeout(() => {
            timeOutEvent = 0
            if (touchType === 'press') {
              binding.value()
            }
          }, 500)
        }, false)
    
        el.addEventListener('touchmove', function (ev) {
          clearTimeout(timeOutEvent)
          timeOutEvent = 0
        })
    
        el.addEventListener('touchend', function (ev) {
          var endX, endY
          endX = ev.changedTouches[0].pageX
          endY = ev.changedTouches[0].pageY
          direction = GetSlideDirection(startX, startY, endX, endY)
    
          clearTimeout(timeOutEvent)
          switch (direction) {
            case 0:
              break
            case 'up':
              if (touchType === 'up') {
                binding.value(direction)
              }
              break
            case 'down':
              if (touchType === 'down') {
                binding.value(direction)
              }
              break
            case 'left':
              if (touchType === 'left') {
                binding.value(direction)
              }
              break
            case 'right':
              if (touchType === 'right') {
                binding.value(direction)
              }
              break
            default:
          }
        }, false)
      }
    }
    
    export default touch


     
  • 相关阅读:
    Codeforces 1485C Floor and Mod (枚举)
    CodeForces 1195D Submarine in the Rybinsk Sea (算贡献)
    CodeForces 1195C Basketball Exercise (线性DP)
    2021年初寒假训练第24场 B. 庆功会(搜索)
    任务分配(dp)
    开发工具的异常现象
    Telink MESH SDK 如何使用PWM
    Telink BLE MESH PWM波的小结
    [LeetCode] 1586. Binary Search Tree Iterator II
    [LeetCode] 1288. Remove Covered Intervals
  • 原文地址:https://www.cnblogs.com/Adyblog/p/15419158.html
Copyright © 2011-2022 走看看