슬라이딩 퍼즐은 그림을 동일한 부분으로 나누고 순서를 섞은 다음(아래 그림) 서로 밀어서 완전한 그림을 이루는 것을 의미합니다.
직소 퍼즐을 구현하려면 순서를 무작위로 섞는 방법, 두 그림의 위치를 바꾸는 방법 등을 고려해야 합니다. 그러나 Flexbox 레이아웃을 사용한 후에는 이에 대해 생각할 필요가 없습니다. Flexbox는 매우 강력합니다. Flexbox에 대한 소개를 보려면 여기를 클릭하세요.
이 게임에서 사용되는 것은 Flexbox 레이아웃의 order 속성입니다. order 속성을 사용하여 Flex 항목의 순서를 제어할 수 있습니다.
여기서는 9개의 캔버스 요소를 사용하여 이미지를 9개의 동일한 부분으로 나눕니다. 배경 이미지 위치 지정과 같은 다른 방법을 사용할 수도 있습니다.
<div class="wrap"> <canvas></canvas> <canvas></canvas> <canvas></canvas> <canvas></canvas> <canvas></canvas> <canvas></canvas> <canvas></canvas> <canvas></canvas> <canvas></canvas> </div>
9제곱 그리드에 국한되지 않고 16제곱 그리드 등으로 제한되지 않으면 위 요소를 동적으로 생성할 수 있습니다.
다음은 섞인 순서로 9개의 사진을 생성하는 코드입니다.
var drawImage = function (url) { return new Promise(function (resolve, reject) { var img = new Image(); img.onload = function () { resolve(img); }; img.src = url; }) }; drawImage("2.jpg").then(function (img) { var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; var random = arr.sort(function() {return Math.random() > 0.5}); [].forEach.call(document.querySelectorAll("canvas"), function (item, i) { item.width = $(".wrap").clientWidth / 3; item.height = $(".wrap").clientHeight / 3; item.style.order = random[i]; var ctx = item.getContext("2d"); ctx.drawImage(img, img.width * (i % 3) / 3, img.height * Math.floor(i / 3) / 3, img.width / 3, img.height / 3, 0, 0, item.width, item.height); }); });
위의 키 코드는 다음과 같습니다.
item.style.order = random[i];
숫자의 순서를 섞고 각 캔버스 요소의 순서 속성에 무작위로 할당하면 브라우저가 자동으로 정렬합니다.
두 사진의 위치를 바꾸는 방법은 다음과 같습니다.
var order1 = item.style.order; var order2 = target.style.order;
양쪽의 주문 속성 값만 교환하면 됩니다.
전체 코드
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" /> <meta content="yes" name="apple-mobile-web-app-capable" /> <meta content="black" name="apple-mobile-web-app-status-bar-style" /> <meta content="telephone=no" name="format-detection" /> <title></title> <style> html, body { height: 100%; } body { margin: 0; padding: 0; overflow: hidden; } .wrap { display: flex; flex-wrap: wrap; width: 100%; height: 100%; overflow: hidden; } .wrap canvas { width: 33.3333%; height: 33.3333%; border: 1px solid red; box-sizing: border-box; } </style> </head> <body> <div class="wrap"> <canvas data-value="1"></canvas> <canvas data-value="2"></canvas> <canvas data-value="3"></canvas> <canvas data-value="4"></canvas> <canvas data-value="5"></canvas> <canvas data-value="6"></canvas> <canvas data-value="7"></canvas> <canvas data-value="8"></canvas> <canvas data-value="9"></canvas> </div> <script> var $ = function (el) { return document.querySelector(el); }; var touchMove, touchEnd; var drawImage = function (url) { return new Promise(function (resolve, reject) { var img = new Image(); img.onload = function () { resolve(img); }; img.src = url; }) }; drawImage("2.jpg").then(function (img) { var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; var random = arr.sort(function() {return Math.random() > 0.5}); [].forEach.call(document.querySelectorAll("canvas"), function (item, i) { item.width = $(".wrap").clientWidth / 3; item.height = $(".wrap").clientHeight / 3; item.style.order = random[i]; var ctx = item.getContext("2d"); ctx.drawImage(img, img.width * (i % 3) / 3, img.height * Math.floor(i / 3) / 3, img.width / 3, img.height / 3, 0, 0, item.width, item.height); }); }); document.addEventListener("touchstart", function (e) { var target = e.target; if (e.target.tagName.toLowerCase() !== "canvas") { return; } var ctx = target.getContext("2d"); var image = ctx.getImageData(0, 0, target.width, target.height); var obj = target.cloneNode(true); obj.getContext("2d").putImageData(image, 0, 0); var top = target.getBoundingClientRect().top, left = target.getBoundingClientRect().left; obj.style.cssText = "position: absolute; top: " + top + "px; left: " + left + "px"; document.body.appendChild(obj); var point = {"x": e.touches[0].pageX, "y": e.touches[0].pageY}; document.addEventListener("touchmove", touchMove = function (e) { obj.style.cssText = "position: absolute; top:" + (e.touches[0].pageY - point.y + top) + "px; left: " + (e.touches[0].pageX - point.x + left) + "px"; }); document.addEventListener("touchend", touchEnd = function (e) { var pos = {"x": e.changedTouches[0].pageX, "y": e.changedTouches[0].pageY}; [].forEach.call(document.querySelectorAll(".wrap canvas"), function (item, i) { var offset = item.getBoundingClientRect(); if (pos.x > offset.left && pos.x < (offset.left + item.width) && pos.y > offset.top && pos.y < (offset.top + item.height)) { var order1 = item.style.order; var order2 = target.style.order; if (obj.parentNode) { document.body.removeChild(obj); } item.style.order = order2; target.style.order = order1; } }); document.removeEventListener("touchmove", touchMove); document.removeEventListener("touchend", touchEnd); }) }) </script> </body> </html>
테스트할 때는 모바일 터치 이벤트만 지원하므로, 구글 에뮬레이터나 휴대폰을 이용해 여는 것이 가장 좋습니다.
코드에는 기본 기능만 구현되어 있고, 완전한 기능은 구현되지 않았습니다.