1

I am attempting to code a puzzle using HTML, CSS, and JavaScript. Currently my code accepts an image and then sets that image to the background of a div element. The JavaScript then appends 16 child div elements into the html file. I want my code to move each image segment from the parent element to its respective child.

I have a div element in my HTML:

<div id="image"></div>

The JavaScript code appends 16 child div elements each with a unique id:

$(document).ready(function(){
    let $url;
    $('#gameImage').keypress(function(e){
        if(e.keyCode == 13){
            $url = $('#gameImage').val();
            console.log($url);
            $('#gameImage').val('');
            $('#image').css("background-image", `url(${$url})`);
            for(let i=0;i<16;i++){
                console.log(i);
                $('#image').append('<div class="piece" id= "segment'+i+'"></div>')
            }
        }
    });
})

I want to be able to click on these child elements and scramble around the pieces of the puzzle however the image is a background image for the parent element.

I have tried following some of the suggestions from this post Cutting an Image into pieces through Javascript however I cannot move the images if set background-position for each piece. Other solutions mentioned using canvas but I am not able to use canvas to make a movable div element.

Rob
  • 14,746
  • 28
  • 47
  • 65
tdammon
  • 599
  • 1
  • 7
  • 26
  • 1
    @RokoC.Buljan Yes this is exactly what I am trying to do. – tdammon Oct 02 '18 at 22:53
  • Since you're trying to do something like https://en.wikipedia.org/wiki/15_puzzle (just, using images) the accepted CSS answer suggested by https://stackoverflow.com/questions/8912917/cutting-an-image-into-pieces-through-javascript is the simplest. What's stopping you from it's implementation? – Roko C. Buljan Oct 02 '18 at 22:56
  • @RokoC.Buljan In my code there are not designated divisions for each piece. I did this because I want to be able to change the difficulty and create a puzzle with more pieces. – tdammon Oct 02 '18 at 22:58
  • Also, what do you mean by scramble? Where's your click listener that *scrambles* the pieces? – Roko C. Buljan Oct 02 '18 at 22:59
  • @RokoC.Buljan I haven't gotten that far in the code yet. Before I add mix the pieces I need to associate each piece with part of the image. – tdammon Oct 02 '18 at 23:00
  • If that's the case, say you have a rectangular image, you simply create N divisions, you shuffle the X,Y coordinates and you can directly assign each piece (child DIV) an inline style that will move the background-position to -XYpx. At the same time you can assign that DIV it's scrumbled index, so you'll know that the user *won (or whatever)* the puzzle once all the divs are ordered back by index – Roko C. Buljan Oct 02 '18 at 23:00
  • @RokoC.Buljan I'm not sure what you mean by assign each piece an inline style to move the background-position. – tdammon Oct 02 '18 at 23:03
  • I'll try to put together a quick example – Roko C. Buljan Oct 02 '18 at 23:07

1 Answers1

1
  • Set a number of slices (4 × 4 in this demo).
  • Get each box size
  • Foreach and push into array your DIV elements
  • Calculate the children CSS backgroundPosition
  • Use a shuffle function
  • Populate your puzzle

const slices = 4,
  img = "https://i.stack.imgur.com/QpnpY.jpg",  // Desired image
  $puzzle = $("#puzzle"),                       // Puzzle square selector
  
  // Careful editing below this line...
  shuffle = a => {
    //stackoverflow.com/a/6274381/383904
    let j, x, i;
    for (i = a.length - 1; i > 0; i--) {
      j = Math.floor(Math.random() * (i + 1));
      x = a[i];
      a[i] = a[j];
      a[j] = x;
    }
    return a;
  },
  pieces = slices * slices,
  size = $puzzle.width(), // so same height?
  boxSize = size / slices;


// Create boxes
let boxes = [];
for (let i = 0; i < pieces; i++) {

  const x = -~~(i % slices) * boxSize, // Background position x
        y = -~~(i / slices) * boxSize; // y

  boxes.push($("<div>", {
    'class': 'box',
    data: {
      index: i
    },
    css: {
      width: boxSize,
      height: boxSize,
      backgroundImage: `url(${img})`,
      backgroundPosition: `${x}px ${y}px`
    },
    html: `<b>${i}</b><br> x ${x}<br> y ${y}`,
    on: {
      click() {
        const index = $(this).data('index'); // Get clicked element data-index
        console.clear(); console.log( `INDEX: ${index}` ); // Do s/t with index
      }
    }
  }));
}

// Shuffle boxes
shuffle(boxes); // Comment this line to see unshaffled boxes

// Append boxes
$puzzle.append(boxes);
#puzzle {
  width: 400px;
  height: 400px;
  border: 1px solid #000;
}

#puzzle .box {
  float: left;
  box-shadow: inset 0 0 0 1px #000;
  color: #fff;
}
<div id="puzzle"></div>
<script src="//code.jquery.com/jquery-3.1.0.js"></script>

The above should help to get you started.
On each item click you can get the element's data-index. Now, after two clicks, you can swap those two indexes in your boxes array and re-populate your $puzzle the same way.
The puzzle should than game-over once the all the DIV indexes are in order 0 to 15.

Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • Thanks Roko. This will fix my issue. I was trying to set the image to the puzzle div when I should have been setting partial images to each child div. – tdammon Oct 02 '18 at 23:45