自定義上傳圖片拼圖遊戲

吃飯call我發表於2018-11-20

1. 遊戲訪問連線

點選跳轉

2. 九宮格拼圖原理

  1. 圖例原理:

image

  1. 上圖的九宮格圖例,每個格子都有一個(x,y)的座標,假如格子9是空白格子,怎麼知道6和8是它的直接相鄰格子呢。這時候就體現出格子座標(x,y)的作用了, 使用公式:|(x6 - x9)| + |(y6 - y9)| = 1,將空白格子9的座標與格子6的座標進行對應座標的差值的絕對值的和等於1,就證明它們是直接相鄰格子,可進行移動互換。
  2. 詳細原理請點選:跳轉

3. 實現過程,程式碼分析

  1. flex實現九宮格佈局:
<style>
  .jigsaw {
    margin-top: 15px;
  }
  .jigsaw .ul{
    display: flex;
    flex-wrap: wrap; // 換行
  }
  
  .ul .child {
    width: 33.3%;
    color: rgba(255, 255, 255, 0.5);
    background-repeat: no-repeat;
  }
</style>
<section class='jigsaw'>
    <ul class="ul">
        <li class="child" data-x='1' data-y='1' style="order:1">1</li>
        <li class="child" data-x='2' data-y='1' style="order:2">2</li>
        <li class="child" data-x='3' data-y='1' style="order:3">3</li>
        <li class="child" data-x='1' data-y='2' style="order:4">4</li>
        <li class="child" data-x='2' data-y='2' style="order:5">5</li>
        <li class="child" data-x='3' data-y='2' style="order:6">6</li>
        <li class="child" data-x='1' data-y='3' style="order:7">7</li>
        <li class="child" data-x='2' data-y='3' style="order:8">8</li>
        <li class="child" data-x='3' data-y='3' style="order:9" id='empty'>9</li>
    </ul>
</section>
複製程式碼
  1. 現在的佈局是這樣子的:

image
3. 分析:每個li代表一個格子,自定義屬性data-x和data-y代表座標(x, y);而樣式order用於對格子進行移動排序;格子9新增id='empty'用於標識為空白格子。 4. 因為格子的寬度是通過百分比設定的,會根據不同螢幕寬度的變化而變化;而且我們需要正方形的小格子,所以格子的高度需要js動態計算:

// 設定格子的高度、背景圖片的尺寸
setChildStyle() {
  this.childWidth = window.getComputedStyle(this.oChild[0], false).width; // 獲取格子寬度
  console.log(this.childWidth);
  for (let i = 0; i < this.oChild.length; i++) {
    this.oChild[i].style.height = `${this.childWidth}`;
  }
}
複製程式碼
  1. 現在變成

image
6. 現在給每個格子設定背景圖片的尺寸(background-size),將格子的background-size的寬度設定成格子父節點ul的寬度,高度為auto,然後通過backgound-position進行定位,用格子的背景拼湊成一張完整的圖片

setChildStyle() {
  this.childWidth = window.getComputedStyle(this.oChild[0], false).width;
  console.log(this.childWidth);
  for (let i = 0; i < this.oChild.length; i++) {
     this.oChild[i].style.height = `${this.childWidth}`;
     this.oChild[i].style.backgroundSize = `${this.ulWidth} auto`;
     this.setBgpositon(this.oChild[i]);
  }
}
複製程式碼
  1. 其實每個格子的背景圖片都是同一張,只不過是通過background-position 對背景圖片進行定位,讓每個格子只顯示圖片背景的九分之一,
// 設定背景圖在格子的位置
setBgpositon(chiObj) {
  let x = chiObj.getAttribute('data-x') - 1;
  let y = chiObj.getAttribute('data-y') - 1;
  chiObj.style.backgroundPosition = `${-x*parseInt(this.childWidth)}px ${-y*parseInt(this.childWidth)}px`;
}
複製程式碼

如下圖視:

image

  1. 設定預設背景圖片後:
// 設定格子的背景圖片
setBgImg(imgUrl) {
  for (let i = 0; i < this.oChild.length - 1; i++) {
    this.oChild[i].style.backgroundImage = `url(${imgUrl})`;
  }
}
複製程式碼

image

  1. 接下來把格子擼成可移動的,與空白格子直接相鄰的格子都可以與空白格子換位,一開始的order樣式就起作用了。點選格子,首先比較該格子是否與空白格子直接相鄰,如果是就交換格子的data-x、data-y和order值進行換位:
childEvent() {
  let that = this;
  let oEmptyChild = document.getElementById('empty'); // 獲取空白的格子物件
  this.oUl[0].addEventListener('click', function(ev){
    let target = ev.target;
    let targetX, targetY, targetOrder;
    let iEmptyX, iEmptyY, iEmptyOrder;
    if (target.className != 'child' ) return false;
    iEmptyX = oEmptyChild.getAttribute('data-x');
    iEmptyY = oEmptyChild.getAttribute('data-y');
    iEmptyOrder = window.getComputedStyle(oEmptyChild, false).order;
    targetX = target.getAttribute('data-x');
    targetY = target.getAttribute('data-y');
    targetOrder = window.getComputedStyle(target, false).order;
    if (Math.abs(targetX - iEmptyX) + Math.abs(targetY - iEmptyY) == 1) {
      // data-x data-y order 值互換
      [iEmptyX, targetX] = [targetX, iEmptyX];
      [iEmptyY, targetY] = [targetY, iEmptyY];
      [iEmptyOrder, targetOrder] = [targetOrder, iEmptyOrder];

      oEmptyChild.setAttribute('data-x', iEmptyX);
      oEmptyChild.setAttribute('data-y', iEmptyY);
      oEmptyChild.style.order = iEmptyOrder;
      target.setAttribute('data-x', targetX);
      target.setAttribute('data-y', targetY);
      target.style.order = targetOrder;
    }
  }, false);
}
複製程式碼
  1. 接下來是把上傳的img圖片設定成格子背景,通過監聽input type='file'的change事件來獲取圖片檔案files:
imgEvent() {
  let that = this;
  this.oFile.addEventListener('change', function(){
    let imgUrl = window.URL.createObjectURL(this.files[0]); // 該方法將files轉換成img可訪問的本地路徑
    that.oImg.setAttribute('src', imgUrl);
    that.oImg.onload = function() {
      that.setBgImg(imgUrl); // 重新設定格子背景
    }
  }, false);
}
複製程式碼
  1. 原始碼地址 點選訪問

  2. (完)

相關文章