Fabric.js add images order - javascript

I'm trying to add images to canvas strictly, but images are added randomly.
What's the problem?
Here is my simple code.
var canvas = new fabric.Canvas("preview");
var imgs = ['bgBottom','bgTop', 'bgLevel', 'bgCircle'];
for (var i=0; i<imgs.length;i++){
var url = imgs[i]+'.png';
fabric.Image.fromURL(url, function (oImg) {
canvas.add(oImg)
})
}

fabric.Image.fromURL loads the image in the background and runs the anonymous function you pass to it once image load is complete which adds it to the canvas. The order that the browser loads the images will vary and you can't rely on it being in a specific order.
Check out this jsfiddle that shows loading an array of images and making sure they're displayed in a set order. This works by adding the image to the canvas before it's loaded; it just doesn't render until the image is available to be displayed.
The code from the jsfiddle:
var SelfLoadingImage = fabric.util.createClass(fabric.Object, {
initialize: function(src) {
this.image = new Image();
this.image.src = src;
this.image.onload = (function() {
this.width = this.image.width;
this.height = this.image.height;
this.loaded = true;
this.setCoords();
this.fire('image:loaded');
canvas.renderAll();
}).bind(this);
},
_render: function(ctx)
{
if (this.loaded) {
ctx.drawImage(this.image, -this.width / 2, -this.height / 2);
}
}
});
var canvas = new fabric.Canvas("preview");
var imgs = [
'http://icons.iconarchive.com/icons/fasticon/ifunny/128/dog-icon.png', // dog
'http://33.media.tumblr.com/avatar_14ee6ada72a4_128.png', // cat
'http://upload.wikimedia.org/wikipedia/commons/b/bc/Nuvola_devices_mouse.png' // mouse
];
for (var i=0; i<imgs.length;i++){
var url = imgs[i];
var img = new SelfLoadingImage(url);
canvas.add(img);
}

Related

Moving image on canvas on the X axis only

The Problem
I am finding it rather difficult to get my head around this, I am attempting to move an image using the mouse along the X axis only. I am finding it hard to even move the image at all and the many tutorials I have looked at arnt really helping me. Here is what I am trying to say:
As you can see by my beautiful image above I only want to image to move left and right at the bottom of the page.
The Code and the Question
Here is my first attempt, when I try this all the images loaded on the canvas no longer appear making it very hard for me to understand why it isnt working.
<script type="text/javascript">
//Referencing the canvas
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var width = canvas.getAttribute('width');
var height = canvas.getAttribute('height');
//Images
var bggameImage = new Image();
var playerImage = new Image();
var enemyImage = new Image();
var projectileImage = new Image();
var livesImage = new Image();
//Canvas dimensions
var width = 480;
var height = 320;
//Loading in the backgroundImage
bggameImage.src = "Images/bggameImage.png";
bggameImage.onload = function(){
context.drawImage(bggameImage, 0, 0);
}
//Loading in the playerImage
playerImage.src = "Images/playerImage.png";
playerImage.onload = function(){
context.drawImage(playerImage, 165, 240);
}
//Loading in the projectileImage
projectileImage.src = "Images/projectileImage.png";
projectileImage.onload = function(){
context.drawImage(projectileImage, 65, 240);
}
var playerImage = {
x:176,
y:74,
}
function init() {
playerImage.src = "Images/playerImage.png";
//Moving player
myCanvas.addEventListener("mousemove", function (e) {
var bounding_box = myCanvas.getBoundingClientRect();
playerImage = (e.clientX - bounding_box.left) * (myCanvas.width / bounding_box.width) - playerImage.width / 2;
playerImage = (e.clientY - bounding_box.top) * (myCanvas.height / bounding_box.height) - playerImage.height / 2;
}
)
</script>
The whole "function init()" part is what I have just tried but I thought I would include this anyway, I understand that I am loading in the playerImage twice.
You're using the same variable name twice (playerImage), so your image is being overwritten. You're using it for the image and also to store the position. Change the playerImage that's storing x and y to be playerPosition or something like that. Update that variable on your mouse event and then render the image according to that variable's values.
Ultimately, you're going to have to look at a game loop using setTimeout or requestAnimationFrame. So, this will become crucial at that stage. And yes, you shouldn't be loading the player image twice either. Do all of that at the start and only start your game when all your assets have successfully loaded.
For instance...
var playerImage;
var alienImage;
var bulletImage;
var assetCount = 0;
function loadAssets() {
playerImage = new Image();
playerImage.onload = checkAssetsLoaded;
playerImage.src = "assets/images/Brush01.png";
alienImage = new Image();
alienImage.onload = checkAssetsLoaded;
alienImage.src = "assets/images/Brush02.png";
bulletImage = new Image();
bulletImage.onload = checkAssetsLoaded;
bulletImage.src = "assets/images/Brush03.png";
}
function checkAssetsLoaded(event) {
assetCount++;
console.log("An asset has loaded!: " + assetCount);
if (assetCount == 3) {
startGame();
}
}
function startGame() {
// Start your game initialization logic here.
console.log("Game starting!");
}

Handling Async Calls with Canvas and ForEach

I am trying to understand how I can utilize angular's $q library to display images which are based on a canvas drawing and then converted using .toDataURL();
Basically I want to:
Loop over a images ($scope.targetImages)
Draw them on a canvas
Convert them to images using .toDataURL() and store it in ($scope.outputImages);
Display the images using ng-repeat
The problem is, that the function .toDataURL() can take some time before executed, thus resulting in a delayed call of step 4, and thus nothing being displayed.
I have tried the following, but it still resolves before all the images are converted.
As I have it now, when I call drawCanvas() for the second time, then the images are shown.
// 1
$scope.targetImages= {}
drawCanvas().then(function(data){
console.log("done: " + new Date())
console.log(data)
$scope.outputImages = data;
$scope.previewMode = false; // switch views, display canvas, remove preview
});
function drawCanvas() {
var defer = $q.defer();
var targetImages = {}
angular.forEach($scope.targetImages , function(imageObj, key) {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.src = imageObj.nativeURL;
img.onload = start
// 2
function start() {
ctx.drawImage(img, 0, 0, img.width, img.height);
outputImages[key] = {
IID: key,
dataURL: canvas.toDataURL()
}
} // start
}); // for loop target images
defer.resolve(outputImages);
return defer.promise;
} // draw canvas
And displayed as:
<img ng-show="!previewMode" ng-src="{{image.dataURL || ''}}" style="width: 100vw; max-width: 600px;">
First, define a function that draws an image to the canvas and returns a promise for the result:
function drawToCanvas(nativeURL) {
return $q(function (resolve) {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.src = nativeURL;
img.onload = function () {
ctx.drawImage(img, 0, 0, img.width, img.height);
resolve(canvas.toDataURL());
};
});
}
The solution then becomes:
$scope.targetImages = [];
function drawCanvas() {
// pResults is an array of promises
var pResults = $scope.targetImages.map(function (imageObj) {
return drawToCanvas(imageObj.nativeURL);
});
return $q.all(pResults);
}
drawCanvas().then(function(data) {
// data is an array of imageUrls
console.log("done: " + new Date())
console.log(data)
$scope.outputImages = data;
$scope.previewMode = false;
// switch views, display canvas, remove preview
});
To simplify, I have changed $scope.targetImages and $scope.outputImages to be arrays instead of objects, but it shouldn't be too hard to go back to using objects for them if that's what you need.

why aren't my images showing up

Hi I'm trying to learn how to use the EaselJS libraries, eventually combining with some of the EXTJS libs butt I'm having trouble putting all my code in the .Js files. My js file looks like this
var democanvas = document.createElement('canvas');
document.body.appendChild(democanvas);
democanvas.height = "400";
democanvas.width = "600";
function init() {
var canvas = document.getElementById("democanvas");
var stage = new createjs.Stage('canvas');
var im = new createjs.Bitmap("dbz.jpg");
// im.regX - im.image.width *.2;
// im.regY - im.image.height *.2;
stage.addChild(im);
stage.update();
im.addEventListener("click", function(){
var seed = new createjs.Bitmap("seed.jpg");
seed.alpha = 0.5;
seed.x = window.event.clientX;
seed.y = window.event.clientY;
stage.addChild(seed);
stage.update();
}); //end seed evenlistener */
} //end functin init()
this doesn't work, but if I comment out the whole document.createElement('canvas') section and I apply
<body onLoad="init();">
<canvas id="demoCanvas" width="1000" height="1000">
alternate content
</canvas>
to my index.html file It will start working :( I included the .js within the body tag of te index html
Edit:::::::::::::::::::::: this is my current code, still not showing anything unless I add the canvas to the html page :(
window.onload = function (){
var demoCanvas = document.createElement('canvas');
//document.body.appendChild(democanvas);
demoCanvas.height = "400";
demoCanvas.width = "600";
}
var stage;
function init() {
stage = new createjs.Stage("demoCanvas");
var text = new createjs.Text("Hello World", "bold 86px Arial", "#ff7700");
stage.addChild(text);
stage.update();
}
init();
Another thing - since you are using image paths, and not fully loaded images as Bitmaps sources, its possible the images are not ready when you update the stage. You can either put onload handlers on your image(s) to update the stage, or preload them first.
var image = new Image();
image.onload = handleLoad;
image.src = "src.jpg";
function handleLoad(event) { stage.update(); }
// You can also use the Bitmap
var bitmap = new createjs.Bitmap("src.jpg");
bitmap.image.onload = handleLoad;
You can also tick the stage to update it constantly. This is a quick way to see if thats the problem.
createjs.Ticker.addEventListener("tick", stage);
// OR
createjs.Ticker.on("tick", stage);
try the below :
document.body.onload = function(){
var democanvas = document.createElement('canvas');
document.body.appendChild(democanvas);
democanvas.height = "400";
democanvas.width = "600";
function init() {
var canvas = document.getElementById("democanvas");
var stage = new createjs.Stage('canvas');
var im = new createjs.Bitmap("dbz.jpg");
// im.regX - im.image.width *.2;
// im.regY - im.image.height *.2;
stage.addChild(im);
stage.update();
im.addEventListener("click", function(){
var seed = new createjs.Bitmap("seed.jpg");
seed.alpha = 0.5;
seed.x = window.event.clientX;
seed.y = window.event.clientY;
stage.addChild(seed);
stage.update();
}); //end seed evenlistener */
} //end functin init()
init();
}
Firstly you need to use the console of your navigator to see javascript's errors before any question.
You will see this error : Uncaught TypeError: Cannot call method 'appendChild' of null
That because you load document.body.appendChild(democanvas); before the html is load so your document.body is null.
This is the way:
function init() {
var democanvas = document.createElement('canvas');
document.body.appendChild(democanvas);
democanvas.height = "400";
democanvas.width = "600";
var canvas = document.getElementById("democanvas");
var stage = new createjs.Stage('canvas');
var im = new createjs.Bitmap("dbz.jpg");
// im.regX - im.image.width *.2;
// im.regY - im.image.height *.2;
stage.addChild(im);
stage.update();
im.addEventListener("click", function(){
var seed = new createjs.Bitmap("seed.jpg");
seed.alpha = 0.5;
seed.x = window.event.clientX;
seed.y = window.event.clientY;
stage.addChild(seed);
stage.update();
}); //end seed evenlistener */
} //end functin init()
I had the same problem and I solved it by using a preloader.
You can use the PreloadJS preloader like this:
var preloader = new createjs.LoadQueue(true);
preloader.addEventListener("complete",this.handleComplete.bind(this));
var manifest = [
{src:"./images/yourIMAGE.PNG"},
//another image
];

How do I handle many images in my HTML5 canvas?

I'm very new to Html5 canvas and Javascript. I'm trying this :
function animate() {
var image1 = new Image();
image.src = /path
var image2 = new Image();
image2.src = /path
for(;;)
{
//change value of x and y so that it looks like moving
context.beginPath();
context.drawImage(<image>, x, y );
context.closePath();
context.fill();
}
}
EDIT:
And I call the animate function each 33ms :
if (playAnimation) {
// Run the animation loop again in 33 milliseconds
setTimeout(animate, 33);
};
If I follow the answer given here, I get the image struck and its not moving any further.
Update: Based on new information in the question, your problem (restated) is that you want to either
wait for all images to load first, and then start animating with them, or
start animating and only use an image if it is available.
Both are described below.
1. Loading many images and proceeding only when they are finished
With this technique we load all images immediately and when the last has loaded we run a custom callback.
Demo: http://jsfiddle.net/3MPrT/1/
// Load images and run the whenLoaded callback when all have loaded;
// The callback is passed an array of loaded Image objects.
function loadImages(paths,whenLoaded){
var imgs=[];
paths.forEach(function(path){
var img = new Image;
img.onload = function(){
imgs.push(img);
if (imgs.length==paths.length) whenLoaded(imgs);
}
img.src = path;
});
}
var imagePaths = [...]; // array of strings
loadImages(imagePaths,function(loadedImages){
setInterval(function(){ animateInCircle(loadedImages) }, 30);
});
2. Keeping track of all images loaded so far
With this technique we start animating immediately, but only draw images once they are loaded. Our circle dynamically changes dimension based on how many images are loaded so far.
Demo: http://jsfiddle.net/3MPrT/2/
var imagePaths = [...]; // array of strings
var loadedImages = []; // array of Image objects loaded so far
imagePaths.forEach(function(path){
// When an image has loaded, add it to the array of loaded images
var img = new Image;
img.onload = function(){ loadedImages.push(img); }
img.src = path;
});
setInterval(function(){
// Only animate the images loaded so far
animateInCircle(loadedImages);
}, 100);
And, if you wanted the images to rotate in a circle instead of just move in a circle:
Rotating images: http://jsfiddle.net/3MPrT/7/
ctx.save();
ctx.translate(cx,cy); // Center of circle
ctx.rotate( (angleOffset+(new Date)/3000) % Math.TAU );
ctx.translate(radius-img.width/2,-img.height/2);
ctx.drawImage(img,0,0);
ctx.restore();
Original answer follows.
In general, you must wait for each image loading to complete:
function animate(){
var img1 = new Image;
img1.onload = function(){
context.drawImage(img1,x1,y1);
};
img1.src = "/path";
var img2 = new Image;
img2.onload = function(){
context.drawImage(img2,x2,y2);
};
img2.src = "/path";
}
You may want to make this code more DRY by using an object:
var imgLocs = {
"/path1" : { x:17, y:42 },
"/path2" : { x:99, y:131 },
// as many as you want
};
function animate(){
for (var path in imgLocs){
(function(imgPath){
var xy = imgLocs[imgPath];
var img = new Image;
img.onload = function(){
context.drawImage( img, xy.x, xy.y );
}
img.src = imgPath;
})(path);
}
}

Javascript Canvas Element - Array Of Images

I'm just learning JS, trying to do things without jQuery, and I want to make something similar to this however I want to use an array of images instead of just the one.
My image array is formed like this
var image_array = new Array()
image_array[0] = "image1.jpg"
image_array[1] = "image2.jpg"
And the canvas element is written like this. (Pretty much entirely taken from the Mozilla site)
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image();
img.src = 'sample.png';
img.onload = function(){
for (i=0;i<5;i++){
for (j=0;j<9;j++){
ctx.drawImage(img,j*126,i*126,126,126);
}
}
}
}
It uses the image "sample.png" in that code but I want to change it to display an image from the array. Displaying a different one each time it loops.
Apoligies if I've not explained this well.
Just iterate over the array, and position the images by using its width and height properties:
function draw() {
var ctx = document.getElementById('canvas').getContext('2d'),
img, i, image_array = [];
image_array.push("http://sstatic.net/so/img/logo.png");
image_array.push("http://www.google.com/intl/en_ALL/images/logo.gif");
// ...
for (i = 0; i < image_array.length; i++) {
img = new Image();
img.src = image_array[i];
img.onload = (function(img, i){ // temporary closure to store loop
return function () { // variables reference
ctx.drawImage(img,i*img.width,i*img.height);
}
})(img, i);
}
}
Check this example.

Categories