I know EaselJS has an awesome way of managing their sprites, but is it even possible to make it easier?
The following link convinced me to use texture atlasing : http://www.altdevblogaday.com/2012/09/17/building-an-html5-game-dont-shrug-off-atlases/
Few pointers why I want this so badly :
Flash CS6 Generate Spritesheet feature creates the TextureAtlas ready for use
Improves load time
Saves bandwidth
Better Dev time
I have no idea how to kick start this, any help would be appreciated.
Ok, I'll expand on my comment a little.
When you create a display element in EaselJS, you give it a source image:
srcImage = new Image();
srcImage.src = "http://whatever.com/image.png";
...
sprite1 = new Bitmap(srcImage);
By default, the instance of the Bitmap is the whole image. However, you can pinpoint the Bitmap down to a region of that image. For instance, suppose the image contains a row of 32x32 tiles:
sprites = [];
for(i=0; i<8; i++) {
sprites[i] = new Bitmap(srcImage);
sprites[i].sourceRect = { x:i*32, y:0, width:32, height:32 };
}
You've now got eight sprites, each referring to a different 32x32 region of the source image.
You can also change it dynamically:
sprCursor = new Bitmap(srcImage);
sprCursor.sourceRect = ( x:0, y:0, width:32, height:32 };
...
if(cursorOverButton) {
sprCursor.sourceRect = { x:32, y:0, width:32, height:32 };
// or more efficiently, sprCursor.sourceRect.x = 32;
}
I imagine that's pretty much how EaselJS works behind the scenes to handle animated sprites.
Related
It is possible that I can change the image color of png image by php/js functions?
I would like to change color only on non-transparent area. An example image is available here:
I would like to change colors only on the visible t-shirt not on all area.
Please post your actual transparent PNG input file. In lieu of that, in ImageMagick command line, this I think should work to colorize the white to sky-blue.
convert image.png \
\( -clone 0 -alpha off -fill skyblue -colorize 100 \) \
\( -clone 0 -alpha extract \) \
-compose multiply -composite \
result.png
I do not know PHP Imagick that well. So I will leave my answer to someone to translate to Imagick.
ImageMagick could be a good route to take (check out PHP.net's Imagick reference, and phpimagick.com), as it'll do the operation on the server, and 'theoretically' work no matter what, (e.g., even if the client's JS is turned off).
If you're not able to use it (e.g., your hosting's limited), an option via JavaScript is to use an offscreen canvas, draw the images to it, generate a PNG from it, and then finally add it to the page.
Here's an example of some working code:
// shirtPath: the url to the shirt
// overlayPath: the url to the overlay image
// callback: a callback that is given the image once it's generated
function generateShirtOverlay(shirtPath,overlayPath,callback) {
// load the images
var loadedImages = 0;
function imageLoaded() {
loadedImages++;
if(loadedImages == 2) {
composeImages();
}
}
var shirtImage = new Image(),
overlayImage = new Image();
// set the image onload callbacks
shirtImage.onload = imageLoaded;
overlayImage.onload = imageLoaded;
// load the images
shirtImage.src = shirtPath;
overlayImage.src = overlayPath;
var generatedImage = new Image();
function composeImages() {
// get image sizes
var width = shirtImage.width,
height = shirtImage.height,
overlayWidth = overlayImage.width,
overlayHeight = overlayImage.height;
// create a canvas
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
// draw the shirt to it
var context = canvas.getContext("2d");
context.drawImage(shirtImage,0,0);
// set the global composite operator to multiply
// this overlays the image nicely, keeping the
// dark areas on the shirt dark
context.globalCompositeOperation = "multiply";
// draw the overlay image, centering it if it's
// not the same size
context.drawImage(overlayImage,
(width - overlayWidth) / 2,
(height - overlayHeight) / 2
);
// a bit of masking magic! for details, see:
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
context.globalCompositeOperation = "destination-in";
// draw the shirt again; this clips the overlay image
// at the shirt's edges
context.drawImage(shirtImage,0,0);
// finally, extract a PNG from the canvas
var png = canvas.toDataURL("image/png");
generatedImage.onload = returnComposedImage();
// and load it to an image object
generatedImage.src = png;
}
function returnComposedImage() {
if(typeof callback == "function")
callback(generatedImage);
}
}
This function could be called to generate the image. Ultimately, it'll call a callback, providing the generated image. Then, since it's an Image object, you can add it right to the page, e.g.:
function addImageToPage(image) {
document.body.appendChild(image);
}
generateShirtOverlay("images/shirt.png", "images/design1.png", addImageToPage);
Please note, though, the disadvantage of client-side rendering is that not all browsers support all these canvas features. In addition, it's a little more overhead on the client-side. Fortunately, though, as it's not an extremely complex manipulation, this method uses only image blend modes, and doesn't need to iterate pixels.
You can use the PHP imagick library. I think it's the best way.
Imagick class
For example :
<?php
$imageInPath = __DIR__. '\\image-before.png';
$imageOutPath = __DIR__. '\\image-after.png';
$overlayColor = '#F00'; // Red for example
$fuzz = 0;
$overlay = new Imagick( $imageInPath ); // Create imagick object
$overlay->opaquePaintImage ( $overlay->getImagePixelColor(0, 0) , $overlayColor , $fuzz , true ); // Fill the opaque area
$image = new Imagick($imageInPath); // Create imagick object
$image->compositeImage($overlay, Imagick::COMPOSITE_DARKEN , 0, 0); // Compose the original image with the overlay
$image->writeImage( $imageOutPath ); // Save the image to the given path
?>
This snippet assumes that the first pixel (x=0, y=0) is transparent.
You can change the way to apply the overlay on the picture by using another COMPOSITE constant. You can get the full list of imagick constants here : Imagick constants
Don't forget to install Imagick library and to enable it in the php.ini file to get it work.
I'm trying to send images from desk to webapp, on the desk part I have a button that takes a screen-shot then compares it with the one taken before, it calculates the difference between them then generate a new Bitmap based on this difference, then it sends this new Bitmap to the webapp which draws it on a canvas, this all happens in real-time using socket.io.
My problem is, let's assume I use Google Chrome on my screen and took a screen-shot, then I opened cmd.exe & took another screenshot, assuming that cmd.exe size is 300 * 100, and starts at half of the screen, the new generated bitmap will contains the cmd.exe screen-shots but rest of the screen (the uncover parts of Google Chrome) will be black, which is perfect for my case since i want to reduce bandwidth usage, now what I want to do, is take the difference bitmap (blob) on the JavaScript side and draw the difference on the previous screen-shot, which will make it look like I transferred the whole screen-shot, If i simply parse the blobs on the canvas, It will make a black screen with the cmd.exe on the center of it, here is my current code :
socket.on("image up", (bin) => {
var ctx = canvas[0].getContext('2d');
var img = new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0);
}
var urlCreator = window.URL || window.webkitURL;
var binaryData = [];
binaryData.push(bin);
img.src = urlCreator.createObjectURL(new Blob(binaryData));
});
Any suggestions ? Thanks in advance
I have a question about saving the png to the canvas.
It saves me a picture, but it saves me just what draws. As I loaded the background that no longer sees.
var dataURL = this.canva.toDataURL();
document.getElementById(id).src = dataURL;
background images I have written out so
function leapController()
{
var tracks = new Array();
tracks[0] = {link: 'png/1.png'};
tracks[1] = {link: 'png/2.png'};
tracks[2] = {link: 'png/3.png'};
tracks[3] = {link: 'png/4.png'};
tracks[4] = {link: 'png/5.png'};
I would like to enroll my whole image, along with the background and drawing element
You give precious little code in your question, but the idea is when you're ready to save the canvas you can use Compositing to draw the background image behind the existing pixels.
To get you started ... here's some code applying the idea:
function save(desiredBackgroundIndex){
// set context compositing to draw behind existing pixels
context.globalCompositeOperation='destination-over';
// Draw the desired background on the canvas BEHIND existing pixels
// Assumes tracks[] is in scope
context.drawImage(tracks[desiredBackgroundIndex],0,0);
// always clean up! Change compositing mode back to default
context.globalCompositeOperation='source-over';
// set #id's src to the canvas's data url
document.getElementById(id).src = this.canva.toDataURL();
}
// USAGE example: add png/3.png to the canvas and save the canvas's data url to #id
save(2);
The way I'm resizing images now is by sticking it into a canvas element and then scaling the context of the canvas. The problem is, when I'm resizing many images the UI basically freezes. Is there anyways I can move this resizing step to a web worker? Problem I'm having is that you can't use document.createElement('canvas') or Image(), two functions crucial to this implementation.
It is possible. However, because canvas isn't available in a worker, you would have to use your own/3rd party code to manipulate the image data in the worker.
For example, you could use
https://github.com/nodeca/pica, which quite handily does its processing in a web worker if web workers are supported.
A rough example of using this to resize image from an img element to a canvas element...
<button onclick="resize()">Resize</button>
<img id="original" src="my-image.jpg">
<canvas id="resized">
With Javascript
function resize() {
// Find the original image
var originalImg = document.getElementById("original");
// Create an empty canvas element of the same dimensions as the original
var originalCanvas = document.createElement("canvas");
originalCanvas.width = originalImg.width;
originalCanvas.height = originalImg.height;
// Copy the image contents to the canvas
var ctx = originalCanvas.getContext("2d");
ctx.drawImage(originalImg, 0, 0);
// Set the target dimensions
var resizedCanvas = document.getElementById("resized");
resizedCanvas.width = originalCanvas.width / 2;
resizedCanvas.height = originalCanvas.height / 2;
// Resize (using web workers if supported)
pica.resizeCanvas(originalCanvas, resizedCanvas, {}, function(err) {
// Do something on finish/error
});
}
Which can be seen at https://plnkr.co/edit/yPRjxqQkHryqeZKw4YIH?p=preview
Unfortunately, you cannot use integrated browser functions for that. Instead, you need to obtain pixel data:
var data = ctx.getImageData(0,0,canvas.width, canvas.height);
You need to send those to worker. You can use transfer mode for the array:
worker.postMessage( {
name: "image_data",
data: data.data,
width: data.width,
height: data.height
},
[data.data] // this tells browser to transfer data to web worker
);
I modified function from some other answer so that it can scale image using the image data array. It's quite limited, as the scale is only allowed to be integer - that means you can't scale down: https://jsfiddle.net/n3drn8v9/5/
I recommend googling some libraries for this, rather than reinventing the wheel.
I am trying to create an application like this using JavaScript and canvas.
I have created a draft using kineticjs as the canvas library. The teeth are all png images and the lines are drawn by kinetic js using the Line object.
A friend of mine suggested I draw everything as one large svg (teeth and lines) where each tooth and line is another distinguished path load it on canvas with distinguished id's and manipulate them using the id's.
I tried reading the kineticjs docs and fabricjs object but didn't find something like that. I mean having the svg stored and loading it on canvas using fabricjs or kineticjs. Then the library should parse it and create the svg on canvas which I can manipulate them by id's.
Is it even possible? I am very new to this svg graphics. Is it even possible the way I am thinking it?
I am currently loading Teeth as Images using KineticJS
Periodontogram.prototype.initializeImageObjects = function (){
var obj = this;
if (this.DEBUG){
console.log("initializing ImageObjects:")
}
var check = function (item){return item !== undefined;}
var url = this.options.url;
var imageObj;
var image;
this.options.imageFilenames.forEach(function (filename, index){
if (obj.DEBUG){
console.log("Loading "+filename+" image");
}
var src = url+'/'+filename;
imageObj = new Image();
imageObj.src = src;
imageObj.onload = function (){
if (obj.DEBUG){
console.log("Image "+src+" successfully loaded");
}
image = new Kinetic.Image({
x:0,
y:0,
id: filename.split('.')[0],
width: this.width,
height: this.height,
image:this
});
obj.imageObjects[index] = this;
obj.teethImages[index] = image;
if (obj.imageObjects.filter(check).length === obj.options.imageFilenames.length ) {
if (obj.DEBUG){
console.log("All Images loaded successfully");
console.log("ready to call rest methods")
}
obj.positionImages();
obj.createLineGroups();
obj.createLabelGroups()
obj.createStage();
obj.drawOnScreen();
$("#"+obj.container).width(obj.stage.getWidth());
$("#"+obj.container).height(obj.stage.getHeight ());
obj.callback();
$.event.trigger({
type:'finishLoading',
obj: obj
});
}
};
});
}
I want all this be replaced by loading a whole svg, that consist of teeth and lines.
FabricJS will convert, decompose & recompose an existing single SVG URL/string into a Fabric.PathGroup. Then you can use mySvgGroup.getObjects() to fetch the individual tooth paths which you can manipulate as desired.
http://fabricjs.com/fabric-intro-part-3/#deserialization
KineticJS will accept an individual SVG tooth's path data and display it on canvas.
http://kineticjs.com/docs/Kinetic.Path.html
Still, it's probably more flexible to create individual SVG paths for each tooth and feed those individual SVGs into either KineticJS or FabricJS.
Then you can manipulate them by id's as you desire:
Place each individual tooth using [x,y] coordinates,
Resize without pixelization,
Put each tooth in a Group and make annotations/drawings onto the tooth,