Creation of a jigsaw puzzle using HTML5 Canvas and KineticJS – step3: jigsaw generation
Here we go with the thir part of the jigsaw puzzle generator made with KineticJS. In the first step we splitted an image into pieces, in the second step we created a jigsaw piece shape, and now it’s time to cut an image into randomly generated pieces.
The code is still uncommented but the concept is:
* Any piece has four sides
* Each side can have a “hill” or a “hole”, with the exception of sides on puzzle borders which are straight
* Starting from the upper left piece, we randomly decide if left and bottom sides will be “hill” or “hole”
* Accordingly, leftmost and upper pieces (if any) will have “hole” to host “hill” or “hill” to host “hole”
This is the code:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
<!DOCTYPE html> <html> <head> <style type="text/css"> body{ margin:0px; overflow:hidden; } #container{ background-color: #888888; } </style> <script type="text/javascript" src="kinetic.js"></script> <script type="text/javascript"> function drawImage(imageObj) { var piecesArray=new Array(); var horizontalPieces = 5; var verticalPieces = 4; var imageWidth = imageObj.width; var imageHeight = imageObj.height; var pieceWidth = Math.round(imageWidth/horizontalPieces); var pieceHeight = Math.round(imageHeight/verticalPieces); var stage = new Kinetic.Stage({ container: "container", width: window.innerWidth, height: window.innerHeight }); var layer = new Kinetic.Layer(); for(i=0;i<horizontalPieces;i++){ piecesArray[i]=new Array(); for(j=0;j<verticalPieces;j++){ piecesArray[i][j] = new Object(); piecesArray[i][j].right=Math.floor(Math.random()*2); piecesArray[i][j].down=Math.floor(Math.random()*2); if(j>0){ piecesArray[i][j].up=1-piecesArray[i][j-1].down; } if(i>0){ piecesArray[i][j].left=1-piecesArray[i-1][j].right; } piecesArray[i][j].shape=new Kinetic.Shape({ drawFunc: function(i,j,pieceWidth,pieceHeight,tileObj){ return function(canvas) { var offsetX=pieceWidth*i; var offsetY=pieceHeight*j; var x8=Math.round(pieceWidth/8); var y8=Math.round(pieceHeight/8); var context = canvas.getContext(); context.beginPath(); context.moveTo(offsetX,offsetY); if(j!=0){ context.lineTo(offsetX+3*x8,offsetY); if(tileObj.up==1){ context.quadraticCurveTo(offsetX+2*x8,offsetY-2*y8,offsetX+4*x8,offsetY-2*y8); context.quadraticCurveTo(offsetX+6*x8,offsetY-2*y8,offsetX+5*x8,offsetY); } else{ context.quadraticCurveTo(offsetX+2*x8,offsetY+2*y8,offsetX+4*x8,offsetY+2*y8); context.quadraticCurveTo(offsetX+6*x8,offsetY+2*y8,offsetX+5*x8,offsetY); } } context.lineTo(offsetX+8*x8,offsetY); if(i!=horizontalPieces-1){ context.lineTo(offsetX+8*x8,offsetY+3*y8); if(tileObj.right==1){ context.quadraticCurveTo(offsetX+10*x8,offsetY+2*y8,offsetX+10*x8,offsetY+4*y8); context.quadraticCurveTo(offsetX+10*x8,offsetY+6*y8,offsetX+8*x8,offsetY+5*y8); } else{ context.quadraticCurveTo(offsetX+6*x8,offsetY+2*y8,offsetX+6*x8,offsetY+4*y8); context.quadraticCurveTo(offsetX+6*x8,offsetY+6*y8,offsetX+8*x8,offsetY+5*y8); } } context.lineTo(offsetX+8*x8,offsetY+8*y8); if(j!=verticalPieces-1){ context.lineTo(offsetX+5*x8,offsetY+8*y8); if(tileObj.down==1){ context.quadraticCurveTo(offsetX+6*x8,offsetY+10*y8,offsetX+4*x8,offsetY+10*y8); context.quadraticCurveTo(offsetX+2*x8,offsetY+10*y8,offsetX+3*x8,offsetY+8*y8); } else{ context.quadraticCurveTo(offsetX+6*x8,offsetY+6*y8,offsetX+4*x8,offsetY+6*y8); context.quadraticCurveTo(offsetX+2*x8,offsetY+6*y8,offsetX+3*x8,offsetY+8*y8); } } context.lineTo(offsetX,offsetY+8*y8); if(i!=0){ context.lineTo(offsetX,offsetY+5*y8); if(tileObj.left==1){ context.quadraticCurveTo(offsetX-2*x8,offsetY+6*y8,offsetX-2*x8,offsetY+4*y8); context.quadraticCurveTo(offsetX-2*x8,offsetY+2*y8,offsetX,offsetY+3*y8); } else{ context.quadraticCurveTo(offsetX+2*x8,offsetY+6*y8,offsetX+2*x8,offsetY+4*y8); context.quadraticCurveTo(offsetX+2*x8,offsetY+2*y8,offsetX,offsetY+3*y8); } } context.lineTo(offsetX,offsetY); canvas.fillStroke(this); } }(i,j,pieceWidth,pieceHeight,piecesArray[i][j]), fill:{ image:imageObj }, stroke: "#000000", strokeWidth: 4, lineCap: "round", draggable: true }); piecesArray[i][j].shape.on("mousedown", function(){ this.moveToTop(); }); layer.add(piecesArray[i][j].shape); } } stage.add(layer); } function jigsaw(){ var imageObj = new Image(); imageObj.src = "brave.jpg"; imageObj.onload = function(){ drawImage(this); } } </script> </head> <body onload="jigsaw()"> <div id="container"></div> </body> </html> |
And you can see the final result here.
Pick and drag pieces around
Is there something more you would like to see about this topic? Maybe how to rotate and join pieces?
These HTML Templates can be easily edited with any HTML editor including the simplest one - Notepad.
They include .html files which makes them available for HTML editing.














This post has 6 comments
Harack
Seems to lag a bit more than it should when dragging pieces.
Vitor
Hi! How many marriage purposes do you receive each week, Emanuele? This is one! :D
shark255
maybe offtop: do you know any solutions how to use kineticjs with box2d library togeter?
ami
hi,
nice, very nice, but if I try to unvisible the stroke, I show a small gaps between the piece, how can I fix it?
angelo
your example doesn’t show nothing except a skeleton jigsaw board without pictures and also the lesson/post n.4 it doesn’t scroll at all. I loaded your page on firefox 20.0 and chrome latest version with same result. I suggest to have a look.
Thanks a lot
angelo
Sorry on firefox this example is shown but not in chrome while n.4 doesn’t scroll at all in both browsers.