Vue實現拼圖小遊戲

需要的主要參數
在這裏插入圖片描述
App.vue

<template>
    <div>
        <!-- 子組件通過自己觸發next事件與父組件進行通信 -->  
        <d-puzzle v-bind="puzzleConfig[level]" @next = "handleNext"></d-puzzle>
    </div>
</template>
<script>
import DPuzzle from './Puzzle'
export default {
    components:{
        DPuzzle
    },
    data() {
        return {
            level : 0,  //難易程度
            puzzleConfig :[
                {row: 3, col: 3, img: './image/0.jpg'},
                {row: 4, col: 4, img: './image/1.jpg'},
                {row: 5, col: 5, img: './image/2.jpg'},
                {row: 6, col: 6, img: './image/3.jpg'},
                {row: 8, col: 8, img: './image/4.jpg'},
            ]
        }
    },
    methods:{
        // 父組件監聽到當前事件成功,通過handleNext控制關卡
        handleNext(){
            this.level ++;
            if(this.level == this.puzzleConfig.length){
                const answerFlag = window.confirm('已經是最後一關了,是否重新開始?');
                if(answerFlag){
                    this.level = 0;
                }
            }
            // console.log('next');
            
        }
    }
}
</script>

Puzzle.vue

<template>
    <div class="puzzle" :style="{width : width + 'px', height : height + 'px'}">
        <div
        class="puzzle__block"
         v-for="(item, index) in blockPoints"
         :key="item.id"
         :style="{
             width: blockWidth + 'px',
             height: blockHeight + 'px',
             left: item.x + 'px',
             top: item.y + 'px',
             backgroundImage:`url(${img})`,
             backgroundPosition:`-${correctPoints[index].x}px  -${correctPoints[index].y}px`,
             opacity:index === blockPoints.length - 1 && 0
         }"
         @click="handleClick"
         :ref="index === blockPoints.length - 1 ? 'empty' : 'block'"
         :data-correctX = "correctPoints[index].x"
         :data-correctY = "correctPoints[index].y"
        >

        </div>
    </div>
</template>
<script>
export default {
    props: {
        width: {
            type: Number,
            default: 500
        },
        height: {
             type: Number,
            default: 500
        },
        row: {
            type: Number,
            default: 3
        },
        col: {
            type: Number,
            default: 3
        },
        img: {
            type: String,
            required: true
        }
    },
    computed: {
        //小塊的寬度
        blockWidth (){
            return this.width / this.col;
        },
        //小塊的高度
        blockHeight (){
            return this.height / this.row;
        },
        //
        correctPoints(){
            const { row,blockWidth, blockHeight } = this;
            const arr = [];
            for(let i = 0; i < row; i++){
                for(let j = 0; j < row; j++){
                    arr.push({
                        x : j * blockWidth,
                        y: i * blockHeight,
                        id: new Date().getTime() + Math.random() *100
                    })
                }   
            }
            return arr;
        },
        blockPoints (){
          const points = this.correctPoints;
          const length = points.length;
          const lastEle = points[length - 1];
          const newArr = [...points];
          newArr.length = length - 1;
          newArr.sort(() => Math.random() -0.5);
          newArr.push(lastEle);
          return newArr;
         }
    },
    methods: {
        handleClick(e){
            const blockDom = e.target;
            const emptyDom = this.$refs.empty[0];
            if(!this.isAdjacent(blockDom,emptyDom)){
                return;
            }
            const {left , top} = blockDom.style;
            blockDom.style.left = emptyDom.style.left;
            blockDom.style.top = emptyDom.style.top;
            emptyDom.style.left = left;
            emptyDom.style.top = top;
            const winFlag = this.checkWin();
            if(winFlag){
                // console.log('success'); 
                this.winGame(emptyDom);
            }
     },
     //判斷是否可以滿足交換條件
     isAdjacent(blockDom,emptyDom){
         const {left:domLeft, top:domTop, width, height } = blockDom.style;
         const {left:emptyLeft, top: emptyTop} = emptyDom.style;
         const xDis = Math.floor(Math.abs(parseFloat(domLeft) - parseFloat(emptyLeft)));
         const yDis = Math.floor(Math.abs(parseFloat(domTop) - parseFloat(emptyTop)));
         const flag = (domLeft === emptyLeft && yDis ===parseInt(height)) 
                              || (domTop === emptyTop && xDis === parseInt(width));
         return flag;   
     },
    //  判斷成功,每一個小塊的現座標和原座標都相等
     checkWin() {
         const blockDomArr = this.$refs.block;
         return blockDomArr.every(dom => {
                const {left:domLeft, top:domTop} = dom.style;
                const {correctx:correctX, correcty:correctY} = dom.dataset;
                const  flag =  parseInt(domLeft) === parseInt(correctX) && parseInt(domTop) === parseInt(correctY); 
                // console.log(flag);
                return flag;
            })
        },
        //拼圖成功
        winGame(emptyDom) {
            setTimeout(()=>{
                alert('you are so great!');
                emptyDom.style.opacity = 1;
                setTimeout(() => {
                     this.goToNextLevel();
                },300)
            },300)
        },
        //進入下一關
        goToNextLevel(){
            console.log('aaa');
            const answerFlag = window.confirm('繼續下一關?');
            if(answerFlag){
                this.$emit('next');
            }
        }
    }
}
</script>
<style>
    .puzzle{
        border:1px solid black;
        position: relative;
    }
    .puzzle__block{
        box-sizing: border-box;
        background-color: red;
        position: absolute;
        border: 1px solid white;
        transition: all .3s;
    }
</style>

效果圖:

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章