【必贏】寫一個鬥地主記牌器

前言

最近一段時間,閒暇之餘玩了幾把鬥地主,惋惜的是本身沒錢買記牌器,腦子又記不住那麼多牌,常常翻車,震怒!遂寫記牌器,逆天改命!html

界面

不用多說,確定是模仿鬥地主的記牌器最實用好看啦。
image.png
ps:這個是最終稿的截圖,前幾版還有一些按鈕,爲何去掉了能夠看後面。數組

交互

首先,怎麼設計這個記牌器的交互纔是關鍵,由於咱們只能手動記牌,其餘牌友出了什麼牌,咱們就從一副牌裏減去出掉的牌。app

  • 想法1:鍵盤輸入,回車|按鈕出牌ide

    • 若是是1-9,那確定是很快的,可是JQK大小王這個輸入起來太麻煩,若是換成其餘鍵盤上連續的字母來代替JQK大小王那麼又須要記憶的成本了,我tm記憶力好還要個🔨記牌器。🙅‍!
  • 想法2:點擊選取,而後回車|按鈕出牌flex

    • 這個實際上是比較符合咱們的鬥地主的操做邏輯的,可是有一個問題若是我出的是飛機的話,就要點不少下,很是麻煩!不太行。
  • 想法3:點擊+滑動直接出牌,去除回車或者按鈕,右鍵撤回spa

    • 這個就是最終的交互邏輯了,若是對手只出了1張,那麼咱們只須要點擊就行,若是出連隊,飛機,那咱們只須要滑幾下而已。由於去除了出牌按鈕,因此容易點錯,這裏加入了一個右鍵給某張牌撤回一張的功能。

實現

很容易就能夠想出思路:設計

  1. 畫出界面,13種牌(4張/種)+大小王(2張,這裏並未區分大小王,由於通常我只是怕炸彈而已,大小王缺一張怕錘子,並且缺了一張之後,不論是大王仍是小王都是最大的,有區別嗎?)
  2. 添加左鍵點擊事件,以及左鍵按下滑動事件。
  3. 阻止默認右鍵事件,添加右鍵點擊事件。
  4. 作一些細節的處理,好比說4張牌紅色警告,0張牌,牌面變灰色等等。

實現1:畫界面

這裏實在太簡單,不想說了。你能夠手動寫十幾個div,也能夠用js動態添加。3d

實現2:左鍵點擊和滑動處理

咱們這裏應該分紅兩布,第一步是選,第二步是出牌。因此咱們能夠用一個數組來保存咱們已經選擇的牌的序號。code

  • 左鍵點擊orm

    • 直接將這個牌的序號添加進數組
    • 而後出牌
  • 滑動

    • 兩種作法,一種是經過座標定位,判斷鼠標移動先後距離包含了哪幾個牌來決定哪些牌應該被選取。第二種就是給每一個牌添加mouseenter,mouseleave事件,鼠標通過的時候,就會觸發,就把出發的牌的序號添加進數組。
    • 而後出牌

實現3:右鍵增長牌數

這裏惟一要注意的是下面這個,首先屏蔽默認的右鍵事件。

document.oncontextmenu = function (event) {
      event.preventDefault();
      if (event.button == 2) {
        addCardCount(event.target)
      }
    };

而後對event.target進行處理就行了。

targetcurrentTarget的區別是:前者多是綁定事件元素下的子元素,後者是綁定事件元素自己。注意使用區別。

實現4:狀態改變

你看以前咱們的界面設計,當牌還有4個的時候是紅色加粗的,說明可能有炸彈;當沒有炸彈時是灰色的;當出完牌之後是帶透明的淡灰色。
這裏咱們只須要根據這個牌數來更改狀態就行了。關鍵是咱們怎麼改這個牌數會更方便呢?

你可能會使用innerHTML,innerText?

CSS中的僞元素中的beforeafter裏面有個content屬性,而這個屬性是能夠跟元素上的自定義屬性綁定的。好比說

.card::after {
      content: attr(count);
      position: absolute;
      bottom: 2px;
      right: 5px;
      color: #999;
      font-size: 18px;
    }

那麼這個content的值就會跟隨元素的count屬性。因此咱們出牌的操做其實只須要更改這個屬性值就行了,而後根據值來作一些相應的類名添加和刪除便可。

適用人羣

  • 沒錢買記牌器的
  • 記憶力差的
  • 開電腦玩的

後記

使用這個記牌器之後,勝率並無高多少阿,淦!
我發現是運氣不行,別人把把超級加倍打🔨啊,天天登錄就領幾千豆,一把歸西!

後續可能寫個運氣更改器

|
|
|
|
|
|

固然不可能啦。淦!

源碼

源碼中已經添加諸多備註。複製粘貼便可,後續可能會放到博客上提供下載連接,不用複製那麼辛苦啦。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>記牌器</title>
  <style>
    #box {
      display: flex;
      justify-content: center;
      align-items: stretch;
      user-select: none;
    }

    .card {
      position: relative;
      border-radius: 10px;
      width: 60px;
      margin-top: 20px;
      height: 100px;
      background-color: #f5f5f5;
      margin-right: 10px;
      box-shadow: 0 5px 5px 0 #c9c9c9;
      font-size: 28px;
      padding-left: 10px;
    }

    .card--empty {
      color: #c3c3c3;
      opacity: 0.8;
    }

    .card--chosen {
      margin-top: 0;
    }

    .card::after {
      content: attr(count);
      position: absolute;
      bottom: 2px;
      right: 5px;
      color: #999;
      font-size: 18px;
    }

    .card--warning::after {
      color: red;
      font-weight: bold;
    }

    #btn-box {
      text-align: center;
      margin-top: 30px;
    }

    button {
      padding: 10px 30px;
      border-radius: 60px;
      background-color: orangered;
      color: #fff;
      font-size: 32px;
      border: 0;
      cursor: pointer;
      transition: all ease .3s;
      margin-right: 20px;
      outline: none;
    }

    button:hover {
      background-color: red;
      transform: scale(1.1);
    }

    .info {
      font-size: 14px;
      margin-top: 70px;
      color: #999;
    }
  </style>
</head>

<body>
  <div id="box"></div>
  <div class="info">
    <p>做者: <a href="http://www.leelei.info">leelei</a></p>
    <p>用法: 鼠標【點擊】直接出牌,或者【點擊滑動】出牌,右鍵單擊某張牌能夠增長牌數(用於防止點錯)</p>
    <p>適用於: 鬥地主新手,而且沒有錢買記牌器,而且常常開着電腦的時候順便手機玩鬥地主</p>
  </div>
</body>
<script>
  //常量
  const arr = [3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K', 'A', 2, '👻'];
  const nums = [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2];
  const TYPE_CHOSEN = ' card--chosen';
  const TYPE_WARNING = ' card--warning';
  const TYPE_EMPTY = ' card--empty';
  //儲存已經選擇
  let chosenArr = [];

  //添加類名
  function addClass(e, name) {
    if (e.className.indexOf(name) == -1) {
      e.className += name;
    }
  }
  //移除類名
  function removeClass(e, name) {
    e.className = e.className.replace(name, '')
  }
  //初始化卡片
  function initCards() {
    let box = document.getElementById('box');
    let frag = document.createDocumentFragment();
    for (let i = 0; i < 14; i++) {
      let ele = document.createElement('div');
      ele.className = 'card' + TYPE_WARNING;
      ele.innerText = arr[i];
      ele.setAttribute('count', nums[i]);
      ele.setAttribute('index', i);
      frag.append(ele)
    }
    box.append(frag);
  }

  //初始化輸入
  function initSlideInput() {
    let cards = document.getElementsByClassName('card');
    //鼠標按下時註冊
    box.addEventListener('mousedown', installSlideInput)
    //鼠標擡起卸載
    box.addEventListener('mouseup', uninstallSlideInput)
    box.addEventListener('mouseleave', uninstallSlideInput)
    //註冊滑動輸入
    function installSlideInput() {
      Array.from(cards).forEach((e) => {
        e.addEventListener('mouseenter', slideSelect)
        e.addEventListener('mouseleave', slideSelect)
      })
    }
    //卸載滑動輸入
    function uninstallSlideInput() {
      Array.from(cards).forEach((e) => {
        e.removeEventListener('mouseenter', slideSelect)
        e.removeEventListener('mouseleave', slideSelect)
      })
      submit();
    }
    //點擊輸入,一直有效
    Array.from(cards).forEach((e) => {
      e.addEventListener('click', function (event) {
        slideSelect(event);
        submit();
      })
    })
    //滑動輸入
    function slideSelect(event) {
      let idx = event.currentTarget.getAttribute('index');
      if (event.currentTarget.className.indexOf(TYPE_CHOSEN) == -1) {
        addClass(event.currentTarget,TYPE_CHOSEN)
        chosenArr.push(idx);
      }
    }
  }

  //重置所有牌的狀態,凸起->對齊
  function reset() {
    chosenArr = [];
    let cards = document.getElementsByClassName('card');
    Array.from(cards).forEach((v, i) => {
      removeClass(v,TYPE_CHOSEN)
    });
  }

  //出牌 
  function submit() {
    let cards = document.getElementsByClassName('card');
    chosenArr.forEach((v, i) => {
      cards[v].setAttribute('count', cards[v].getAttribute('count') - 1);
      removeClass(cards[v],TYPE_WARNING);
      if (cards[v].getAttribute('count') < 1) {
        //最小爲0,設置爲空狀態
        cards[v].setAttribute('count', 0);
        addClass(cards[v],TYPE_EMPTY)
      }
    });
    //重置狀態
    reset();
  }
  //右鍵加牌,防止出錯牌
  function addCardCount(e) {
    if (e.getAttribute('count') != undefined) {
      e.setAttribute('count', e.getAttribute('count') - 0 + 1);
    }
    if (e.getAttribute('count') >= nums[e.getAttribute('index')]) {
      e.setAttribute('count', nums[e.getAttribute('index')]);
      addClass(e,TYPE_WARNING)
    } else if (e.getAttribute('count') > 0) {
      removeClass(e,TYPE_EMPTY);
    }
  }

  window.onload = function () {
    document.oncontextmenu = function (event) {
      event.preventDefault();
      if (event.button == 2) {
        addCardCount(event.target)
      }
    };
    initCards();
    initSlideInput();
  }

</script>

</html>
相關文章
相關標籤/搜索