SoFunction
Updated on 2025-04-10

Write a card game based on JavaScript

Preface

First, split the game requirements into three parts:

  • Flip card animation
  • Generate a random distribution array
  • Click Event

Flip card animation

If our box model is not a two-dimensional plane, but a three-dimensional volume, so that it can have both positive and negative sides, then when we do it, do we just need to turn it over and over? Let's think about the way to turn it into three dimensions. Later I discovered this property:

transform: translateZ(1px);

By using it, the elements inside the box and the bottom of the box can be heightened.

<!-- html --> 
<div class="card">
    <div class="top">I'm the front~</div>
</div>

Just give the sub-box called "top" a "distance from the father" and then pre-flip the parent box called "card" by 180 degrees.rotateY(180deg), wait until it clicks and flips it backtransform: rotateY(0)That's it.

.card{
  ...
  height: 100%;
  width: 100%;
  position: relative;
  transform-style: preserve-3d;
  transform: rotateY(180deg);
  transition: all 600ms;
  background: pink;
  &.select {
    transform: rotateY(0);
  }
  .top{
    ...
    height: 100%;
    width: 100%;
    position: absolute;
    top: 0;
    left: 0;
    box-sizing: border-box;
    background: white;
    border: 2px solid #b6a6dc;
    transform: translateZ(1px);
  }
}

Generate a random distribution array

Let’s first talk about the situation where each element can appear evenly (equal times) in an ideal environment. Let’s talk about how to maximize uniformity when it cannot occur uniformly.

Random algorithm under uniform elements

This algorithm in-brain model is provided by Mr. Sita (θ).

Suppose we need a total of 20 elements and 5 different types of grids, each grid appears 4 times. We have a collection of elements to be allocated:

const total = 20
const icons = ['a', 'b', 'c', 'd', 'e']
// => Get the set Wconst W = ['a', 'a', 'a', 'a',
           'b', 'b', 'b', 'b',
           'c', 'c', 'c', 'c',
           'd', 'd', 'd', 'd',
           'e', 'e', 'e', 'e']

Confusing collection

There is a pointer p that starts with subscript 0, and is responsible for filling in the pattern in an array grid with length 20. The pattern is to randomly take an element from the set w, delete the element after taking it, and move p to the next grid, iterate until it is completed.

function createRandomList(W: string[], total: number) {
    const list: any[] = []
    function it(time: number): any {
        if (time === 0) return list
        // Randomly subscript the collection element every time        const randomNum = (() * ())
        (W[randomNum]) // Add random elements to the new array        (randomNum, 1) // Delete elements in the collection        return it(--time)
    }
    return it(total)
}

Let's make this method more flexible so that its return result can be specified in the format:

// fn is not required to pass the item, and the original data is returned by defaultfunction createRandomList(W: string[], total: number, fn: (<T>(icon: string, index?: number) => T) = icon => icon) {
    const list: any[] = []
    // Iterator    function it(time: number): any {
        if (time === 0) return list
        // Randomly subscript the collection element every time        const randomNum = (() * ())
        (fn(W[randomNum], total-time)) // Pass the element and subscript into fn, and add the calculation result of fn to the new array        (randomNum, 1) // Delete elements in the collection        return it(--time)
    }
    return it(total)
}

Random algorithm under uneven elements

const W = []

Inhomogeneous elements are actually the element distribution rules in the set W have changed, and the obfuscation algorithm is still unaffected. After that, let's think about how to define a "maximum uniformity in inhomogeneity". Divide the set W into two parts: the maximum evenly distributed part + the random part

The maximum evenly distributed part, which means that each element in the icons can appear the same maximum number of times. You can get it like this:

  • Icons number x2, how many grids are needed to get the complete pairing
  • Total number of grids / Complete number of grids at a time, get the maximum number of times that can be fully matched n
  • Loop n times, add icons x 2 to W each time
// Get the maximum number of repetitionsconst times = (total / ( * 2))
for (let index = 0; index < times; index++)
    (...icons, ...icons)

The rest is the part that needs to be distributed randomly, which means that some elements can appear here twice, while the rest will not appear.

  • Total number of grids % icons x2, get the remaining unallocated grids
  • Unassigned grid / 2 means the number of elements n that need to be randomly removed from icons, this n must be less than the number of icons
  • Randomly take n numbers from icons. You can use the method of deleting the number from the original set for each number and repeating n times.
  • Add n numbers x2 to W

Article (3) Does it sound familiar? It seems to have been done before, and it is true that it was written beforecreateRandomListFunctions, W sets become icons, and total becomes the number n required.

// The number of unallocated grids remainsconst lastCount = total % ( * 2)
// Get n numbers randomly from iconsconst lastList = createRandomList(icons, lastCount / 2)
(...lastList, ...lastList)

Together, it is the method to create W:

function createW(icons: string[], total: number) {
    const times = (total / ( * 2))
    const lastCount = total % ( * 2)
    const W = []
    for (let index = 0; index < times; index++)
        (...icons, ...icons)
    const lastList = createRandomList(icons, lastCount / 2)
    (...lastList, ...lastList)
    return W
}

Generate the final array

Completely generate random array code:

function createW(icons: string[], total: number) {
    const times = (total / ( * 2))
    const lastCount = total % ( * 2)
    const W = []
    for (let index = 0; index < times; index++)
        (...icons, ...icons)
    const lastList = createRandomList(icons, lastCount / 2)
    (...lastList, ...lastList)
    return W
}
function createRandomList(W: string[], total: number, fn: (<T>(icon: string, index?: number) => T) = icon => icon) {
    const list: any[] = []
    function it(time: number): any {
        if (time === 0) return list
        const randomNum = (() * ())
        (fn(W[randomNum], total-time))
        (randomNum, 1)
        return it(--time)
    }
    return it(total)
}

// ['a', 'b', 'c', "d"] => ['c', 'd'...x15...'b', 'c', 'a']
createRandomList(createW(icons, total), total)

Click Event

With random arrays in out-of-order, isn't it easy to click a little bit? First, let the generated array attributes be richer to help us display the content.

type CardItem = { icon: string; isDel: boolean; isSelect: boolean, index: number }

let list: CardItem[] = []

// isSelect attribute determines whether it is flipped, isDel attribute determines whether it has been eliminated, icon attribute marks element attribute, index is used to quickly find the position of the click element in the arraylist = createRandomList(createW(icons, total), total, (icon: string, index) =&gt; ({ icon, isDel: false, isSelect: false, index }))

Now you can use the generated array to display it. Next, we write a click event, and the receiving parameter is the array element of the click:

// isLock is used to lock the animation before it is completed.function handlerTap(card: CardItem) {
    if (isLock) return
    list[].isSelect = true
    const selectors = (item =&gt;  &amp;&amp; !)
    // If element < 2 is selected, return directly, and then the process will not be left.    if ( &lt;= 1) return
    isLock = true
    const [item1, item2] = selectors
    //Operate after the flip animation is completed    setTimeout(() =&gt; {
        // If the selected element is the same, the elimination property is equal to true        if ( === ) {
            list[].isDel = true
            list[].isDel = true
        }
        //Flip all cards over the back        list = (item =&gt; ({...item, isSelect: false}))
        isLock = false
        // Determine whether all cards have been flipped        if ((item =&gt; )) ( "your win!")
    }, 800)
}

Complete code

100 rows in total).

<script lang="ts">
    type CardItem = { icon: string; isDel: boolean; isSelect: boolean, index: number }
    const icons = ['a', 'b', 'c', "d"]
    const total = 20
    let list: CardItem[] = []
    let isLock = false

    function handlerTap(card: CardItem) {
        if (isLock) return
        list[].isSelect = true
        const selectors = (item =>  && !)
        if ( <= 1) return
        isLock = true
        const [item1, item2] = selectors
        setTimeout(() => {
            if ( === ) {
                list[].isDel = true
                list[].isDel = true
            }
            list = (item => ({...item, isSelect: false}))
            isLock = false
            if ((item => )) ( "your win!")
        }, 800)
    }
    function createW(icons: string[], total: number) {
        const times = (total / ( * 2))
        const lastCount = total % ( * 2)
        const W = []
        for (let index = 0; index < times; index++)
            (...icons, ...icons)
        const lastList = createRandomList(icons, lastCount / 2)
        (...lastList, ...lastList)
        return W
    }
    function createRandomList(W: string[], total: number, fn: (<T>(icon: string, index?: number) => T) = icon => icon) {
        const list: any[] = []
        function it(time: number): any {
            if (time === 0) return list
            const randomNum = (() * ())
            (fn(W[randomNum], total-time))
            (randomNum, 1)
            return it(--time)
        }
        return it(total)
    }

    list = createRandomList(createW(icons, total),
            total,
            (icon: string, index) => ({ icon, isDel: false, isSelect: false, index }))
</script>
<div class="game-box">
    {#each list as item}
        <div class="grid">
            {#if !}
                <div class="card { && 'select'}" on:click="{() => handlerTap(item)}">
                    <div class="top">{}</div>
                </div>
            {/if}
        </div>
    {/each}
</div>
<style lang="less">
    .game-box{
      margin: 10px auto 0;
      width: 90vw;
      height: 80vh;
      display: grid;
      grid-template-columns: repeat(4, calc(100% / 4 - 3px));
      grid-template-rows: repeat(5, calc(100% / 5 - 3px));
      grid-row-gap:3px;
      grid-column-gap: 3px;
      .card{
        height: 100%;
        width: 100%;
        box-sizing: border-box;
        position: relative;
        transform-style: preserve-3d;
        transform: rotateY(180deg);
        transition: all 600ms;
        background: pink;
        &.select {
          transform: rotateY(0);
        }
        .top{
          height: 100%;
          width: 100%;
          position: absolute;
          top: 0;
          left: 0;
          box-sizing: border-box;
          display: flex;
          justify-content: center;
          align-items: center;
          background: white;
          border: 2px solid #b6a6dc;
          transform: translateZ(1px);
        }
      }
    }
</style>

The above is the detailed content of writing a card-flip game based on JavaScript. For more information about JavaScript card-flip game, please follow my other related articles!