Created
September 2, 2012 16:43
-
-
Save jonbro/3601339 to your computer and use it in GitHub Desktop.
html sprite packer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <!-- | |
| instructions: | |
| drag a set of images onto the canvas. | |
| it will create a spritesheet from these images. | |
| credits: | |
| http://robertnyman.com/2011/03/10/using-html5-canvas-drag-and-drop-and-file-api-to-offer-the-cure/ | |
| http://www.blackpawn.com/texts/lightmaps/ | |
| http://www.html5rocks.com/en/tutorials/file/dndfiles/ | |
| --> | |
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>Canvas Sprite Packing</title> | |
| <style> | |
| canvas { | |
| position: relative; | |
| border: 1px solid #000; | |
| } | |
| </style> | |
| <script type="text/javascript" src="http://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script> | |
| </head> | |
| <body> | |
| <div id="container"> | |
| <header role="banner"> | |
| <h1>Canvas Sprite Packing</h1> | |
| </header> | |
| <div role="main"> | |
| <section id="main-content"> | |
| <canvas id="my-canvas" width="1024" height="1024">I am canvas</canvas> | |
| <br/> | |
| <textarea id="datafile"></textarea> | |
| <script> | |
| (function () { | |
| var canvas = document.getElementById("my-canvas"), | |
| context = canvas.getContext("2d"), | |
| img = document.createElement("img"), | |
| mouseDown = false, | |
| brushColor = "rgb(0, 0, 0)", | |
| hasText = true, | |
| clearCanvas = function () { | |
| if (hasText) { | |
| context.clearRect(0, 0, canvas.width, canvas.height); | |
| hasText = false; | |
| } | |
| }; | |
| // Adding instructions | |
| context.fillText("Drop an image onto the canvas", 240, 200); | |
| context.fillText("Click a spot to set as brush color", 240, 220); | |
| // Image for loading | |
| img.addEventListener("load", function () { | |
| //clearCanvas(); | |
| context.drawImage(img, 0, 0); | |
| }, false); | |
| // To enable drag and drop | |
| canvas.addEventListener("dragover", function (evt) { | |
| evt.preventDefault(); | |
| }, false); | |
| // Handle dropped image file - only Firefox and Google Chrome | |
| canvas.addEventListener("drop", function (evt) { | |
| clearCanvas(); | |
| var files = evt.dataTransfer.files; | |
| if (files.length > 0) { | |
| var loadCount = 0; | |
| var images = new Array(); | |
| for(var i=0;i<files.length;i++){ | |
| console.log('adding file: '+i); | |
| var file = files[i]; | |
| if (typeof FileReader !== "undefined" && file.type.indexOf("image") != -1) { | |
| loadCount++; | |
| var getImgLoader = function(){ | |
| var img = document.createElement("img"); | |
| img.addEventListener("load", function () { | |
| loadCount--; | |
| if(loadCount==0){ | |
| console.log('building atlas'); | |
| buildAtlas(images); | |
| } | |
| }, false); | |
| img.readerOnLoad = function (evt) { | |
| img.src = evt.target.result; | |
| }; | |
| return img; | |
| } | |
| var reader = new FileReader(); | |
| // Note: addEventListener doesn't work in Google Chrome for this event | |
| var img = getImgLoader(); | |
| img.filename = file.name; | |
| images.push(img); | |
| reader.onload = img.readerOnLoad; | |
| reader.readAsDataURL(file); | |
| } | |
| } | |
| } | |
| evt.preventDefault(); | |
| }, false); | |
| var Rect = function(x, y, w, h){ | |
| this.x = x; | |
| this.y = y; | |
| this.w = w; | |
| this.h = h; | |
| }; | |
| var ImgNode = function(rect){ | |
| this.rect = rect; | |
| this.child = new Array(); | |
| this.isLeaf = function(){ | |
| return !this.child[0] && !this.child[1]; | |
| } | |
| this.insertImg = function(img){ | |
| if(!this.isLeaf()){ | |
| var newNode = this.child[0].insertImg(img); | |
| if(newNode){ | |
| return newNode; | |
| }else{ | |
| return this.child[1].insertImg(img); | |
| } | |
| }else{ | |
| console.log('no longer a leaf'); | |
| if(this.img){ | |
| return false; | |
| } | |
| if(img.width > this.rect.w || img.height > this.rect.h){ | |
| return false; | |
| } | |
| if(img.width == this.rect.w && img.height == this.rect.h){ | |
| return this; | |
| } | |
| // otherwise we need to split the node | |
| var dw = this.rect.w - img.width; | |
| var dh = this.rect.h - img.height; | |
| if(dw>dh){ | |
| this.child[0] = new ImgNode(new Rect(this.rect.x, this.rect.y, img.width, this.rect.h)); | |
| this.child[1] = new ImgNode(new Rect(this.rect.x+img.width, this.rect.y, this.rect.w-img.width, this.rect.h)); | |
| }else{ | |
| this.child[0] = new ImgNode(new Rect(this.rect.x, this.rect.y, this.rect.w, img.height)); | |
| this.child[1] = new ImgNode(new Rect(this.rect.x, this.rect.y+img.height, this.rect.w, this.rect.h - img.height)); | |
| } | |
| return this.child[0].insertImg(img); | |
| } | |
| } | |
| }; | |
| var removePadding = function(img, completeCallback){ | |
| // just a hack to make this syncronous | |
| var complete = false; | |
| var newCanvas = document.createElement('canvas'); | |
| newCanvas.width=img.width; | |
| newCanvas.height=img.height; | |
| document.body.appendChild(newCanvas); | |
| newCanvas.getContext('2d').drawImage(img, 0, 0); | |
| // determine the width and height of the image | |
| var minX = img.width; | |
| var minY = img.height; | |
| var maxX = 1; | |
| var maxY = 1; | |
| var imageData = newCanvas.getContext('2d').getImageData(0,0,img.width, img.height); | |
| console.log('canvas size', imageData.width, imageData.height); | |
| for (x = 0; x < imageData.width; x++) { | |
| for (y = 0; y < imageData.height; y++) { | |
| var index = (x + y * imageData.width) * 4; | |
| if(imageData.data[index+3]>0){ | |
| maxX = Math.max(x, maxX); | |
| maxY = Math.max(y, maxY); | |
| minX = Math.min(x, minX); | |
| minY = Math.min(y, minY); | |
| } | |
| } | |
| } | |
| console.log('minimums', minX, minY); | |
| newCanvas.width = maxX+2-minX; | |
| newCanvas.height = maxY+2-minY; | |
| newCanvas.getContext('2d').drawImage(img, -minX, -minY); | |
| img.onload = function(){ | |
| img.offX = minX; | |
| img.offY = minY; | |
| completeCallback(); | |
| }; | |
| img.src = newCanvas.toDataURL("image/png"); | |
| document.body.removeChild(newCanvas); | |
| return img; | |
| }; | |
| var buildAtlas = function(images){ | |
| var output = new Array(); | |
| var RootNode = new ImgNode(new Rect(0,0,1024,1024)); | |
| var removePaddingCount = images.length; | |
| for (var i = images.length - 1; i >= 0; i--) { | |
| output[i] = { | |
| filename: images[i].filename, | |
| sourceSize: { | |
| w:images[i].width, | |
| h:images[i].height | |
| } | |
| }; | |
| images[i] = removePadding(images[i], function(){ | |
| removePaddingCount--; | |
| if(removePaddingCount==0){ | |
| for (var i = images.length - 1; i >= 0; i--) { | |
| var node = RootNode.insertImg(images[i]); | |
| if(node !== false){ | |
| node.img = images[i]; | |
| output[i].frame = { | |
| x: node.rect.x, | |
| y: node.rect.y, | |
| w: node.rect.w, | |
| h: node.rect.h | |
| }; | |
| output[i].sourceSize.x = images[i].offX; | |
| output[i].sourceSize.y = images[i].offY; | |
| context.drawImage(images[i], node.rect.x, node.rect.y); | |
| } | |
| }; | |
| // output to the datafile | |
| document.getElementById("datafile").value = JSON.stringify(output); | |
| } | |
| }); | |
| } | |
| }; | |
| // Save image | |
| var saveImage = document.createElement("button"); | |
| saveImage.innerHTML = "Save canvas"; | |
| saveImage.addEventListener("click", function (evt) { | |
| window.open(canvas.toDataURL("image/png")); | |
| evt.preventDefault(); | |
| }, false); | |
| document.getElementById("main-content").appendChild(saveImage); | |
| })(); | |
| </script> | |
| </section> | |
| </div> | |
| </div> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment