How to show items from inventory - javascript

How to make something like this to work? I'm really new to programming and cant figure it out how to to make my inventory visible?
Getting this error:
Uncaught TypeError: Cannot read property 'img' of undefined
image.(anonymous function).onload
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var image = {};
function loadImage(name) {
image[name] = new Image();
image[name].onload = function () {
//resourceLoaded();
};
image[name].src = "images/" + name;
}
var items = {
knife: {
name: "Knife",
img: "knife.png"
},
sword: {
name: "Sword",
img: "sword.png"
}
};
var inventory = []; //empty inventory
inventory.push(items.knife); //pickup a knife
for (var i = 0, len = inventory.length; i < len; i++) {
loadImage(inventory[i].img);
image[inventory[i].img].onload = function() {
ctx.drawImage(image[inventory[i].img], 0, 0);
}
}
Trying many things but cant understand anything better than this one :( Because im new to programing

There's no block scope in JavaScript, only function scope. The i variable is the same everywhere in your snippet. So when the image is loaded the i variable has the value of 2 (inventory.length).
To do what you intend to do, you have to introduce a function scope. One way to do it would be to do this :
function createCallback(i) {
loadImage(inventory[i].img);
image[inventory[i].img].onload = function() {
ctx.drawImage(image[inventory[i].img], 0, 0);
}
}
for (var i = 0, len = inventory.length; i < len; i++) {
createCallback(i);
}

You're trying to do the same thing twice in your for loop. That is, you should only have loadImage(inventory[i].img) in your for loop and move the ctx.drawImage call to within the loadImage() function:
function loadImage(name) {
image[name] = new Image();
image[name].onload = function () {
//resourceLoaded();
ctx.drawImage(image[name], 0, 0);
};
image[name].src = "images/" + name;
}
This will draw the images on top of each other, though, if you add more to the inventory array.

Related

tick method in javascript for canvas

I'm trying to make a tick method for this code.
When I try to put a while loop or time interval it just goes blank.
I want the tick method to call this function without the canvas going blank.
How would i make that tick method
function setup(){
var canvas = document.getElementById('my_canvas');
var ctx = canvas.getContext('2d');
canvas.width = 800;
canvas.height = 600;
var gun = new Image();
var badguy = new Image();
var wall1 = 200;
var ground = new Image();
var back = new Image();
var back2 = new Image();
var back3 = new Image();
var wall = new Image();
var wall2 = new Image();
back.onload = function() {
ctx.drawImage(back, 0, 0, 800, 300);
};
back2.onload = function() {
ctx.drawImage(back2, mountainplace, 0, mtnsize1, 300);
};
back3.onload = function() {
ctx.drawImage(back3, mountainplace2, 0, mtnsize2, 300);
};
ground.onload = function() {
ctx.drawImage(ground, groundplace, 300, 1980, 200);
};
wall.onload = function() {
ctx.drawImage(wall, place2, 250, size2, 100);
};
wall2.onload = function() {
ctx.drawImage(wall2, place, 250, size, 100);
};
badguy.onload = function() {
ctx.drawImage(badguy, badguyplace, 250, 100, 100);
};
gun.onload = function() {
ctx.drawImage(gun, 0, 100, 400, 400);
};
back2.src = "moutain1.png";
back3.src = "moutain2.png";
back.src = "backing.png";
ground.src = "ground1.jpg";
wall.src = "wall.png";
wall2.src = "wall2.png";
badguy.src = "santa2.png";
gun.src = "gun1.png";
};
You need to clarify the question because I'm not sure what exactly you are trying to achieve.
I assume you put some code at the end of your setup() function that performs some operations on the images. But before you can do it you need to wait for the images to load.
BTW: another problem with your code is that the images will be drawn on the canvas in the order in which they load, which may be unpredictable. You probably want to avoid this too.
A solution to your problem (or at least to what I think your problem is) is to first start loading the images and then only perform further operations after they have all been loaded.
You can use the following code to do this:
function makeAllLoadedHandler(image_files_count, on_all_loaded) {
return function() {
--image_files_count;
if (image_files_count == 0) {
// All images loaded, call the function.
on_all_loaded();
}
}
}
function loadAllImages(image_files, on_all_loaded) {
var images = {};
var callback = makeAllLoadedHandler(image_files.length, function() { on_all_loaded(images); } );
for (var i = 0; i < image_files.length; ++i) {
var image = new Image;
image.src = image_files[i];
image.onload = callback;
images[image_files[i]] = image;
}
}
The loadAllImages() function takes an array of image file names and a function to call when all the images have been loaded.
You can use it like this in your code:
function setup() {
var image_files = [
"mountain1.png",
"mountain2.png",
"backing.png",
"ground1.jpg",
"wall.png",
"wall2.png",
"santa2.png",
"gun1.png" ];
loadAllImages(image_files, onAllImagesLoaded);
}
function onAllImagesLoaded(images) {
// Draw your images and perform all the other tasks on them.
// The 'images' object stores each of the Image object under a key that is
// its file name.
ctx.drawImage(images['backing.png'], 0, 0, 800, 300);
// ...
// Do other stuff with the images.
}
You just call the setup() function like you used to and then the onAllImagesLoaded function will be called some time later when all the images are available. You continue your processing in there.
I hope this helps. Although it's possible that your problem is completely different ;)

Javascript function works for a while, then freezes the browser

I'm using this JS code to make a banner:
var images = ["../../images/g11.jpg","../../images/g9.jpg","../../images/g10.jpg"];
var titulos = ["title1","title2","title3"];
var resumos = ["ddd","aaa","bbb"];
var noticias = ["190","204","200"];
var total = 3;
var indice = 0;
function rotate() {
document.getElementById('imageb').src = images[indice];
document.getElementById('titulob').innerHTML = titulos[indice];
document.getElementById('resumob').innerHTML = resumos[indice];
document.getElementById('noticiab').value = noticias[indice];
indice++;
if (indice > total - 1) indice = 0;
}
function banner() {
rotate();
setTimeout(banner, 5000);
}
It works how expected, but after some loops it freezes the browser. Pretty sure I'm not using setTimeout properly. Any ideas?
Edit:
Working so far:
function rotate(indice) {
document.getElementById('imageb').src = images[indice];
document.getElementById('titulob').innerHTML = titulos[indice];
document.getElementById('resumob').innerHTML = resumos[indice];
document.getElementById('noticiab').value = noticias[indice];
}
function banner(indice) {
var f1 = function() { banner(indice); };
var total = 3;
rotate(indice);
indice++;
if (indice > total - 1) indice = 0;
setTimeout(f1, 5000);
}
I'm posting this as a CW because it's a total guess.
Completely FWIW, here's how I'd minimally change that code: Live Copy | Live Source
(function() {
var entries = [
{
img: "../../images/g11.jpg",
titulo: "title1",
resumo: "ddd",
noticia: "190"
},
{
img: "../../images/g9.jpg",
titulo: "title2",
resumo: "aaa",
noticia: "204"
},
{
img: "../../images/g10.jpg",
titulo: "title3",
resumo: "bbb",
noticia: "200"
}
];
var indice = 0;
function rotate() {
var entry = entries[indice];
document.getElementById('imageb').src = entry.img;
document.getElementById('titulob').innerHTML = entry.titulo;
document.getElementById('resumob').innerHTML = entry.resumo;
document.getElementById('noticiab').value = entry.noticia;
indice = (indice + 1) % data.length;
}
function banner() {
rotate();
setTimeout(banner, 5000);
}
banner();
})();
Changes:
Put everything in a scoping function to avoid creating global variables.
Use an array of objects rather than parallel arrays.
Use the array's length rather than a separate total variable.
Use the remainder trick for getting the wrap-around on the indice variable.
I added a call to banner(); at the end to get things started, but I assume you have that and just didn't show it.
But again, I don't see any reason your code shouldn't be working as is, other than the possibility of some weird global variable conflict.

A function to preload images - need to draw them now, but how?

I'm dabbling with canvas. And I'm a little lost on something.
I have this function:
function preloadimages(arr) {
var newimages = []
var arr = (typeof arr != "object") ? [arr] : arr
for (var i = 0; i < arr.length; i++) {
newimages[i] = new Image()
newimages[i].src = arr[i]
}
}
And I call it like so:
preloadimages(['images/background.png', 'images/hero.png', 'images/monster.png']);
The only problem is, I don't know how to then draw them again later.
If I was preloading one image inside my js I would say:
var bgOk = false;
var bg = new Image();
bg.onload = function () {
bgOk = true;
};
bg.src = "images/background.png";
and then further down when I wanted it drawn I would say:
if (bgOk) {
context.drawImage(bg, 0, 0);
}
And that would be that. The problem is I have made a preloader class, I don't really know how now to call in just the image I want to draw now, or even how to implement the bgOk idea so that if it loaded ok, I can draw it, and if not, leave it alone.
Could someone advise me on this? I'm basically just trying to go more class based rather than the dirty great mess I normally have with a huge javascript file that is ugly and not as maintainable.
This seems to be a complicated problem, but in reality isn't as bad as it looks. If you want to use pre-existing code, or just want to look at something for ideas you can have a look at: http://thinkpixellab.com/pxloader/ This library was used in the HTML5 version of Cut The Rope.
A simple custom implementation could be something like the following:
function loadImages(arr, callback) {
this.images = {};
var loadedImageCount = 0;
// Make sure arr is actually an array and any other error checking
for (var i = 0; i < arr.length; i++){
var img = new Image();
img.onload = imageLoaded;
img.src = arr[i];
this.images[arr[i] = img;
}
function imageLoaded(e) {
loadedImageCount++;
if (loadedImageCount >= arr.length) {
callback();
}
}
}
And then you can call it like this:
var loader = loadImages(['path/to/img1', 'path/to/img2', 'path/to/img3'], function() {
ctx.drawImage(loader.images['path/to/img1']); // This would draw image 1 after all the images have been loaded
// Draw all of the loaded images
for (var i = 0; i < loader.images.length; i++) {
ctx.drawImage(loader.images[i]);
}
});
If you want more details on asset loading you can have a look at the asset loading section of Udacity's HTML5 Game Development course https://www.udacity.com/course/cs255
A function I use:
function ImageLoader(sources, callback)
{
var images = {};
var loadedImages = 0;
var numImages = 0;
// get num of sources
for (var src in sources) {
numImages++;
}
for (var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if (++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
You call it like so:
var sources = {
bg: path/to/img.png,
title: path/to/img/png
};
var _images = {};
isReady = ImageLoader(sources, function(images) {
_images = images;
});
And then to access your images
_images.bg;
Example: drawImage(_images.bg, 0, 0);

Instantiating JavaScript module pattern

I'm trying to create multiple instances of a slider on a page. Each slider should know which slide it’s currently viewing. It seems that when I update the slide property, I change it for the class, and not the instance. This suggests to me that I'm not instantiating properly in my public init() function. Where am I going wrong?
var MySlider = (function() {
'use strict';
var animating = 0,
slides = 0, // total slides
slider = null,
slide = 0, // current slide
left = 0;
function slideNext(e) {
if ((slide === slides - 1) || animating) return;
var slider = e.target.parentNode.children[0],
x = parseFloat(slider.style.left);
animate(slider, "left", "px", x, x - 960, 800);
slide++;
}
return {
init: function() {
var sliders = document.querySelectorAll('.my-slider'),
l = sliders.length;
for (var i = 0; i < l; i++) {
sliders[i] = MySlider; // I don't think this is correct.
slider = sliders[i];
buildSlider(slider);
}
}
}
})();
Based on your comment, I think this is more like what you want:
MySlider = (function () {
Slider = function (e) {
this.e = e;
// other per element/per slider specific stuff
}
...
var sliders; // define this out here so we know its local to the module not init
return {
init: function () {
sliders = document.querySelectorAll('.my-slider');
var l = sliders.length;
for (var i = 0; i < l; i++) {
sliders[i] = new Slider(sliders[i]); //except I'd use a different array
slider = sliders[i];
buildSlider(slider);
}
}
})();
This way, you are associating each element with it's own element specific data but you have a containing module within which you can operate on your collection of modules.
It seems that when I update the slide property, I change it for the class, and not the instance.
You are correct. That code is run only when the MySlider class is defined. If you want an instance variable, you need to declare it inside the returned object, ie, part of your return block:
var MySlider = (function(param) {
return {
slider: param,
init: function() {
var sliders = document.querySelectorAll('.my-slider'),
l = sliders.length;
for (var i = 0; i < l; i++) {
sliders[i] = MySlider; // I don't think this is correct.
slider = sliders[i];
buildSlider(slider);
}
}
});

jquery / javascript - storing reference to array - not array values

I'm trying to combine a few similar functions into a single functions, which need to make calls to different arrays / variables, but I'm not quite getting it right. Here's my code:
var initialPreloadArray = ['scenes/icons_orange.png','scenes/icons_blue.png','scenes/icons_green.png','site/pedestal_h.png']; //These must be loaded before we advance from the intro screen
var initialPreloadCounter = 0;
var secondaryPreloadArray = ['site/restart-black.png','site/back_black.png','interludes/city.png','interludes/town.png','interludes/country.png']; //These must be loaded before we can advance from the initial decision scene
var secondaryPreloadCounter = 0;
var vehiclesPreloadArray = ['vehicles/vehicles.png','site/close.png']; //These must be loaded before we can display the vehicles
var vehiclesPreloadCounter = 0;
var arrName; //Store the variable name of the array for the stage of preloading we're in
var arrCounter; //Stores the variable name of the counter for the stage of preloading we're in
function setPreloadStage(preloadStage){
if (preloadStage == initial){
arrName = initialPreloadArray;
arrCounter = initialPreloadCounter;
} else if (preloadStage == 'secondary'){
arrName = secondaryPreloadArray;
arrCounter = secondaryPreloadCounter;
} else if (preloadStage == 'vehicles'){
arrName = vehiclesPreloadArray;
arrCounter = vehiclesPreloadCounter;
}
preloadImages(preloadStage);
}
//Recurse through scene xml and populate scene array
function preloadImages(preloadStage) {
console.log(arrName[arrCounter]);
var img = new Image();
img.src = 'images/' + arrName[arrCounter];
if(!img.complete){
jQuery(img).bind('error load onreadystatechange', imageComplete(preloadStage));
} else {
imageComplete(preloadStage);
}
//$j.preloadCssImages({statusTextEl: '#textStatus', statusBarEl: '#status'});
}
function imageComplete(preloadStage){
arrCounter++;
var preloadLength = arrName.length-1;
if (arrName && preloadLength && arrName[arrCounter]) {
if (preloadLength == arrCounter){
if (preloadStage == 'initial'){
initialImagesLoaded();
} else if (preloadStage == 'secondary'){
secondaryImagesLoaded();
} else if (preloadStage == 'vehicles'){
vehiclesLoaded();
}
}
preloadImages(preloadStage);
}
}
Anybody have an idea what I'm doing wrong?
Actually, here’s an even more obvious problem:
jQuery(img).bind('error load onreadystatechange', imageComplete(preloadStage));
You would have to do this:
jQuery(img).bind('error load onreadystatechange', function () {
imageComplete(preloadStage)
});
I suggest that you should use an array to manage state.
define an array holding your stages, like this:
var stages = [
{
label : 'initial',
imgs : [ 'img/whoobee.png', ...more here...],
doneSoFar: 0,
allDone: function(){}
},
{ label : 'secondary', imgs : .....},
{ label : 'whatever', imgs : ....}
];
NB: You will need to set the "allDone" fn for each stage appropriately.
Then a fn that kicks off one stage:
function kickoffPreloadOneStage(stage) {
console.log ("preloading stage " + stage.label);
preloadNextImage(stage);
}
function preloadNextImage(stage) {
var img = new Image();
img.src = 'images/' + stage.imgs[stage.doneSoFar];
if(!img.complete){
jQuery(img).bind('error load onreadystatechange', function() {
imageComplete(preloadStage);
});
}
else {
imageComplete(preloadStage);
}
}
function imageComplete(stage){
stage.doneSoFar++;
var preloadLength = stage.imgs.length-1;
if (stage.doneSoFar == preloadLength) {
stage.allDone(); // call the allDone function. may want to pass stage back
}
else {
preloadNextImage(stage);
}
}
To do all stages, use code like this:
var i;
for(i=0; i < stages.length; i++) {
kickoffPreloadOneStage(stages[i]);
}
You can also go OO, defining those functions as members of a Stage() class, but ....what I suggested is a reasonable simplification without getting too complicated.

Categories

Resources