Home>

In order to let everyone understand the magic of vue.js,Learn about a data-driven concept of vue.js,This article uses vue to implement a digital jigsaw puzzle.The principle is not very complicated,Follow us to learn.

First look at the effect map:

Functional Analysis

Of course play it back,As a vue enthusiast, we should go deep into the game,Take a look at the implementation of the code.Next, let's analyze how to complete such a game.What functions need to be implemented.Below I directly list the features of this example:

1. Randomly generate a number grid of 1 ~ 15,Every number must appear and only once

2. After clicking on a number box,If one of them is empty,Swap the two

3. Each time the grid moves,We all need to check if it succeeds

4. Reorder puzzles after clicking the reset game button

The above is the main function of this example.It can be seen that the game functions are not complicated,We only need to break one by one, and then I will show the vue code of each function point.

Building a game panel

As a data-driven js framework, vue's html template should be bound to data in many cases.For example, the square grid of this game,We must not write to death here,code show as below:

<template>
  <div>
    <ul>
      <li
        :class="{" puzzle ":true," puzzle-empty ":! puzzle}"
        v-for="puzzle in puzzles"
        v-text="puzzle"
      <</li>
    </ul>
  </div>
</template>
<script>
export default {
  data () {
    return {
      puzzles:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
    }
  }
}
</script>

I omitted the css style part here,You don't need to worry about it first.In the above code, we wrote the numbers from 1 to 15 in an array.This is obviously not randomly ordered,Then we will implement the function of random sorting.

Randomly sort numbers

<template>
  <div>
    <ul>
      <li
        :class="{" puzzle ":true," puzzle-empty ":! puzzle}"
        v-for="puzzle in puzzles"
        v-text="puzzle"
      <</li>
    </ul>
  </div>
</template>
<script>
export default {
  data () {
    return {
      puzzles:[]
    }
  },  methods:{
    //reset rendering
    render () {
      let puzzlearr=[],        i=1
      //generate an array containing 1 to 15 numbers
      for (i;i<16;i ++) {
        puzzlearr.push (i)
      }
      //Randomly shuffle the array
      puzzlearr=puzzlearr.sort (() =>{
        return math.random ()-0.5
      });
      //page display
      this.puzzles=puzzlearr
      this.puzzles.push ("")
    },  },  ready () {
    this.render ()
  }
}

The above code,We use a for loop to generate an ordered array of 1 ~ 15,Then we use the sort method of native js to randomly shuffle the numbers,There is also a knowledge point here ismath.random () method.

Usesort () method for custom sorting,We need to provide a comparison function,Then returns a number that describes the relative order of the two values,The return value is as follows:

1. Returns a value less than 0, indicating that a is less than b

2. Returns 0, indicating that a is equal to b

3. Returns a value greater than 0, indicating that a is greater than b

is used here math.random () Generate a random number between 0 and 1,Subtract 0.5 again, this will have a half probability to return a value less than 0, a half probability to return a value greater than 0, which guarantees the randomness of the generated array,Realized the function of dynamically and randomly generated digital lattice.

have to be aware of is,We also inserted an empty string at the end of the array,Used to generate unique blank grids.

Swap block position

<template>
  <div>
    <ul>
      <li
        :class="{" puzzle ":true," puzzle-empty ":! puzzle}"
        v-for="puzzle in puzzles"
        v-text="puzzle"
        @ click="movefn ($index)"
      <</li>
    </ul>
  </div>
</template>
<script>
export default {
  data () {
    return {
      puzzles:[]
    }
  },  methods:{
    //reset rendering
    render () {
      let puzzlearr=[],        i=1
      //generate an array containing 1 to 15 numbers
      for (i;i<16;i ++) {
        puzzlearr.push (i)
      }
      //Randomly shuffle the array
      puzzlearr=puzzlearr.sort (() =>{
        return math.random ()-0.5
      });
      //page display
      this.puzzles=puzzlearr
      this.puzzles.push ("")
    },    //click on the box
    movefn (index) {
      //Get the click position and its values
      let curnum=this.puzzles [index],        leftnum=this.puzzles [index-1],        rightnum=this.puzzles [index + 1],        topnum=this.puzzles [index-4],        bottomnum=this.puzzles [index + 4]
      //swap values ​​with empty positions
      if (leftnum === "") {
        this.puzzles. $set (index-1, curnum)
        this.puzzles. $set (index, "")
      } else if (rightnum === "") {
        this.puzzles. $set (index + 1, curnum)
        this.puzzles. $set (index, "")
      } else if (topnum === "") {
        this.puzzles. $set (index-4, curnum)
        this.puzzles. $set (index, "")
      } else if (bottomnum === "") {
        this.puzzles. $set (index + 4, curnum)
        this.puzzles. $set (index, "")
      }
    }
  },  ready () {
    this.render ()
  }
}
</script>

1. Here we first add a click [email protected]="movefn ($index)" on the li of each grid, and get the position of the click box in the array through the $index parameter

2.Secondly, the index values ​​of the numbers in the array are index-4, index + 4, index-1, and index + 1.

3. When we find that there is an empty place on the top, bottom, left, and right, we assign the empty position to the number of the current click grid.Set the current clicked position to empty

Remarks:Why do we use the $set method instead of directly assigning values ​​with equal signs?This contains the knowledge of vue's responsive principle.

//Due to javascript limitations, vue.js cannot detect the following array changes:
//1. Set the element directly with the index,Such as vm.items [0]={};
//2. modify the length of the data,Such as vm.items.length=0.
//To solve the problem (1), vue.js extends the observation array,Added a $set () method to it:
//same as `example1.items [0]=...`, but can trigger view updates
example1.items. $set (0, {childmsg:"changed!"})

Detect if you succeeded

<template>
  <div>
    <ul>
      <li
        :class="{" puzzle ":true," puzzle-empty ":! puzzle}"
        v-for="puzzle in puzzles"
        v-text="puzzle"
        @ click="movefn ($index)"
      <</li>
    </ul>
  </div>
</template>
<script>
export default {
  data () {
    return {
      puzzles:[]
    }
  },  methods:{
    //reset rendering
    render () {
      let puzzlearr=[],        i=1
      //generate an array containing 1 to 15 numbers
      for (i;i<16;i ++) {
        puzzlearr.push (i)
      }
      //Randomly shuffle the array
      puzzlearr=puzzlearr.sort (() =>{
        return math.random ()-0.5
      });
      //page display
      this.puzzles=puzzlearr
      this.puzzles.push ("")
    },    //click on the box
    movefn (index) {
      //Get the click position and its values
      let curnum=this.puzzles [index],        leftnum=this.puzzles [index-1],        rightnum=this.puzzles [index + 1],        topnum=this.puzzles [index-4],        bottomnum=this.puzzles [index + 4]
      //swap values ​​with empty positions
      if (leftnum === "") {
        this.puzzles. $set (index-1, curnum)
        this.puzzles. $set (index, "")
      } else if (rightnum === "") {
        this.puzzles. $set (index + 1, curnum)
        this.puzzles. $set (index, "")
      } else if (topnum === "") {
        this.puzzles. $set (index-4, curnum)
        this.puzzles. $set (index, "")
      } else if (bottomnum === "") {
        this.puzzles. $set (index + 4, curnum)
        this.puzzles. $set (index, "")
      }
      this.passfn ()
    },    //check if you pass
    passfn () {
      if (this.puzzles [15] === "") {
        const newpuzzles=this.puzzles.slice (0, 15)
        const ispass=newpuzzles.every ((e, i) =>e === i + 1)
        if (ispass) {
          alert ("Congratulations, you succeeded!")
        }
      }
    }
  },  ready () {
    this.render ()
  }
}
</script>

We are inmovefn <code>is called in themethodpassfnmethod for detection,Whilepassfn Themethod involves two more points of knowledge:

(1) The slice method

Using the slice method, we intercept the first 15 elements of the array to generate a new array.Of course, the premise is that the next element of the array is empty

(2) Every method

Through the every method, we loop to check whether each element of the array after the interception is equal to its index + 1 value. If all are equal, it returns true.

If the pass is successful and the value of ispass is true, the alert "Congratulations, the pass is successful!" Prompt window, if not, do not prompt.

Reset game

Resetting the game is actually quite simple,Just add the reset button and callon it render method is fine:

<template>
  <div>
    <ul>
      <li
        :class="{" puzzle ":true," puzzle-empty ":! puzzle}"
        v-for="puzzle in puzzles"
        v-text="puzzle"
        @ click="movefn ($index)"
      <</li>
    </ul>
    <[email protected]="render">reset game</button>
  </div>
</template>
<script>
export default {
  data () {
    return {
      puzzles:[]
    }
  },  methods:{
    //reset rendering
    render () {
      let puzzlearr=[],        i=1
      //generate an array containing 1 to 15 numbers
      for (i;i<16;i ++) {
        puzzlearr.push (i)
      }
      //Randomly shuffle the array
      puzzlearr=puzzlearr.sort (() =>{
        return math.random ()-0.5
      });
      //page display
      this.puzzles=puzzlearr
      this.puzzles.push ("")
    },    //click on the box
    movefn (index) {
      //Get the click position and its values
      let curnum=this.puzzles [index],        leftnum=this.puzzles [index-1],        rightnum=this.puzzles [index + 1],        topnum=this.puzzles [index-4],        bottomnum=this.puzzles [index + 4]
      //swap values ​​with empty positions
      if (leftnum === "") {
        this.puzzles. $set (index-1, curnum)
        this.puzzles. $set (index, "")
      } else if (rightnum === "") {
        this.puzzles. $set (index + 1, curnum)
        this.puzzles. $set (index, "")
      } else if (topnum === "") {
        this.puzzles. $set (index-4, curnum)
        this.puzzles. $set (index, "")
      } else if (bottomnum === "") {
        this.puzzles. $set (index + 4, curnum)
        this.puzzles. $set (index, "")
      }
      this.passfn ()
    },    //check if you pass
    passfn () {
      if (this.puzzles [15] === "") {
        const newpuzzles=this.puzzles.slice (0, 15)
        const ispass=newpuzzles.every ((e, i) =>e === i + 1)
        if (ispass) {
          alert ("Congratulations, you succeeded!")
        }
      }
    }
  },  ready () {
    this.render ()
  }
}
</script>
<style>
@import url ("./assets/css/bootstrap.min.css");
body {
  font-family:arial, "microsoft yahei";
}
.box {
  width:400px;
  margin:50px auto 0;
}
.puzzle-wrap {
  width:400px;
  height:400px;
  margin-bottom:40px;
  padding:0;
  background:#ccc;
  list-style:none;
}
.puzzle {
  float:left;
  width:100px;
  height:100px;
  font-size:20px;
  background:#f90;
  text-align:center;
  line-height:100px;
  border:1px solid #ccc;
  box-shadow:1px 1px 4px;
  text-shadow:1px 1px 1px #b9b4b4;
  cursor:pointer;
}
.puzzle-empty {
  background:#ccc;
  box-shadow:inset 2px 2px 18px;
}
.btn-reset {
  box-shadow:inset 2px 2px 18px;
}
</style>

Here I added the css code together.

to sum up

  • Previous JS method to get the absolute path of the input file (recommended)
  • Next When the parameter value passed in the url contains&, the solution to the problem of automatic truncation of the url