var c=document.getElementById("cvs");
var ctx=c.getContext("2d");
var imgArray = [];
for (var i=0;i<data.length;i++){
var drawRepeat = Math.floor((data[i]/divider));
imgArray[i] = [];
for (var j=0;j<drawRepeat;j++){
//alert(j);
var xPos = ((i*30)+10);
var yPos = 250-((j*30)+30);
imgArray[i][j] = new Image();
imgArray[i][j].src="assets/orange.png";
imgArray[i][j].onload = function(){
ctx.drawImage(imgArray[i][j],xPos,yPos);
};
}
}
I want to draw multiple images with a for loop. weirdly when i place an alert() with the for loop it works? but if i comment it away it will only display 1 of the image.
Is there any solution to this?
That's the problem related to closures. I'll explain after giving the code.
var c = document.getElementById("cvs");
var ctx = c.getContext("2d");
var imgArray = [];
for (var i = 0; i < data.length; i++) {
var drawRepeat = Math.floor((data[i] / divider));
imgArray[i] = [];
for (var j = 0; j < drawRepeat; j++) {
//alert(j);
(function (_i, _j) {
var xPos = ((_i * 30) + 10);
var yPos = 250 - ((_j * 30) + 30);
imgArray[_i][_j] = new Image();
imgArray[_i][_j].src = "assets/orange.png";
imgArray[_i][_j].onload = function () {
ctx.drawImage(imgArray[_i][_j], xPos, yPos);
};
})(i, j);
}
}
UPDATE:
The reason I am doing this weird stuff is that the function registered to onload event will be called asynchronously - that is the loop will continue and the function will be called when the image loads, and not at that very moment. So what happens is that this loop continues, and once it's completed (note that this may occur even before completion), i's value will be data.length - 1. Now, some image is loaded, and then the function is called. This function refers to i (in the code you gave), which by now is data.length - 1. And so, only the last image gets drawn. I know this is confusing - even I felt it confusing when I first stumbled upon closures. I recommend you read some good article on them and you'll see what the problem is.
What we did was to create a scope (experts might find a problem with this expression) by creating an anonymous function which is called immediately, and is passed the values of i and j as it's parameters - _i and _j (Note that we could've used the same names, but to avoid confusion, I didn't use the same names). Now, these are local to the function and aren't altered. So, when ith image is loaded, it's onload function is called which draws the _ith image in the array.
I have failed to explain it well, so as I said, please read some article on closures.
Preload the images, instead of using a onload handler.
var c=document.getElementById("cvs");
var ctx=c.getContext("2d");
var img = new Image();
img.src="assets/orange.png";
img.onload = function(){
for (var i=0;i<data.length;i++){
var drawRepeat = Math.floor((data[i]/divider));
for (var j=0;j<drawRepeat;j++){
//alert(j);
var xPos = ((i*30)+10);
var yPos = 250-((j*30)+30);
ctx.drawImage(img,xPos,yPos);
}
}
}
I managed to solved the problem with onload by placing the onload outside the for loops and putting drawImage command inside the onload function and in the for loops. Works as well.
Related
I am a beginer. I want to create a pixel art site. For this I try to develope my javascript code. Now I am on the way to simplify the code by setting the different rectangles using var as object and array to avoid to type milles of lines. Than I think to create at the second part an array constructor with defining coords(other x, other y) for every single rectangle in 2D array.
At the moment I don't relise why the first part of the code is not working. Can you please suggest your mind? Thanks a lot in advance.
Here is my code (link on JS Bin):
var canvas;
var ctx;
var x = 0;
var y = 0;
var w = 10; // Width=10px
var h = w; // Heigth=10px
function init() {
canvas = document.querySelector('#myCanvas');
ctx = canvas.getContext('2d');
draw();
}
// Create a rect by path method for restoring the buffer
var rect;
function draw(){
ctx.beginPath();
ctx.rect(x,y,w,h);
}
var c = ['#66757F', '#F7F7F7', '#CCD6DD']; // Setting a color palette as an array
for (var i=0; i<c.length; i++){
c[i]=ctx.fillStyle();
}
// Define colored rectangles as the Objects
var r1 = {rect;[0]}
var r2 = {rect;[1]}
var r3 = {rect;[2]}
ctx.fill();
// Setting three rectangle by diagonal
var r=[r1,r2,r3];// Array of setted rectangles
function draw(){
for (var j=0; j<r.length; j++){
r[j]=ctx.moveTo(x+w*j,y+h*j);
}
}
for (var j=0; j<r.length; i++){
r[j]=ctx.moveTo(x+w*j,y+h*j);
}
You typed 'i++' when using the letter 'j'.
Not sure whether this solves the problem.
Why do you use Math.abs in
var w = Math.abs(-10); // Width=10px
Isn't it easier to set 'var w' to 10 ?
var w = 10;
Is what you're looking for how to create classes and make objects from that class?
If so this is how you would create a class and make objects.
//This will hold all of your objects.
var listOfObjects = [];
//This is a class. You can create objects with it.
function myClass() {
//location
this.X = 0;
this.Y = 0;
//size
this.width = 5;
this.height = 5;
}
function CreateNewObject() {
//This will create and add an object of myClass to your array.
//Now you can loop through the array and modify the values as you wish.
listOfObjects.push(new myClass());
}
I can't figure out why the co-ordinates for the canvas lines aren't being digested using the arrays: any suggestions?
I'm trying to create an algorithm for randomly generated sets of lines which eventually link together from where the last one ended. like a snake that gets longer if you like.
var c = document.getElementById("playground");
var ctx = c.getContext("2d");
//global scope
var i;
var c1 = []; //c is short for collect
var c2 = [];
var c3 = [];
var c4 = [];
var initiate = function(){ //the buttom that triggers the program
var clock = function(){
/* if(i){
alert(i);
}*/
i+=1; //increment each time the..
//function gets called.
var a = Math.round(Math.random()*200);
var b = Math.round(Math.random()*200);
var c = Math.round(Math.random()*200);
var d = Math.round(Math.random()*200);
c1.push(a);
c2.push(b);
c3.push(c);
c4.push(d);
ctx.beginPath();
ctx.moveTo(c1[i], c2[i]); //Here is where the issue seems to be? they don't run.
ctx.lineTo(c3[i], c4[i]);
ctx.stroke();
//if(c1.length===10){
//alert(c1);
//}
}; //end of clock
setInterval(clock,80);
}; //end of parent function
You are initializing i as var i;. When you increment it, it results in NaN, so just initialize it as var i = 0; or the like instead.
EDIT: Also why your problem is at that line is because you are trying to access the NaNth element of an array, which JavaScript doesn't like.
So, I have an <img> tag that has an onclick attribute. The onclick calls a function called analyze(this), with this being the image.
The analyze function does some things to the image that aren't entirely relevant, except for the fact that it draws it onto the <canvas> element (using the drawImage function).
But now, I want to also pick the color I just clicked on in the image. I am currently using the method answered here (the answer with 70+ votes, not the chosen one): How do I get the coordinates of a mouse click on a canvas element?
But, I think I might be doing this wrong. I have the image drawn and my functions called (and those all work), but the color picking part isn't being called. I think that this is because I didn't actually capture the event. This is generally how my code looks:
<img onclick="javascript:analyze(this);" />
function analyze(img_elem) {
// This is getting the canvas from the page and the image in it
var canvaselement = document.getElementById('canvas').getContext('2d'),
img = new Image();
img.onload = function () {
canvaselement.drawImage(img, 0, 0, 250, 250);
...
canvaselement.onClick = function () {
var coords = canvaselement.relMouseCoords(event);
pick(img, canvaselement, coords); // pass in coordinates
}
}
img.src = img_elem.src;
}
function relMouseCoords(event) {
var totalOffsetX = 0;
var totalOffsetY = 0;
var canvasX = 0;
var canvasY = 0;
var currentElement = this;
do {
totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft;
totalOffsetY += currentElement.offsetTop - currentElement.scrollTop;
}
while (currentElement = currentElement.offsetParent)
canvasX = event.pageX - totalOffsetX;
canvasY = event.pageY - totalOffsetY;
return {
x: canvasX,
y: canvasY
}
}
function pick(img, canvaselement, coords) {
var pickedColor = "";
canvaselement.drawImage(img, 0, 0, 250, 250);
xx = coords.x;
yy = coords.y;
var imgData = canvas.getImageData(xx, yy, 1, 1).data;
pickedColor = rgbToHex(imgData);
//alert(pickedColor);
return pickedColor;
}
So, the code never gets to the pick function. I have a feeling that it's because I didn't actually capture the onclick event. I'm also not even sure if this is the right way to get the coordinates on the canvas, I'm just sort of hoping that I even get to that part of the debugging process at this point.
Thanks for your help!
The problem is probably that you're assigning canvaselement to the results of getContext('2d') and not to the element itself, which you will need for the click event binding. Create two variables, one for the DOM element itself and one for the context, something like:
var canvaselement = document.getElementById('canvas'),
canvaselementctx = canvaselement.getContext('2d');
...
canvaselement.onClick = function() {
var coords = canvaselementctx.relMouseCoords(event);
...
}
You have a couple of errors in the code but the reason the code you got from the linked post is that you forgot to include the prototype definition it uses:
HTMLCanvasElement.prototype.relMouseCoords = relMouseCoords;
Now you can call relMouseCoords on the canvas element:
/// event name in lower case
canvaselement.onclick = function () {
var coords = canvaselement.relMouseCoords(event);
//...
However, you will still get problems as you don't use a canvas context for the drawing calls.
function analyze(img_elem) {
// This is getting the canvas from the page and the image in it
var canvaselement = document.getElementById('canvas').getContext('2d'),
/// get context like this
ctx = canvaselement.getContext('2d'),
img = new Image();
img.onload = function () {
/// use context to draw
ctx.drawImage(img, 0, 0, 250, 250);
//...
I am trying to write a javascript program that renders an 8x8 grid of dirt tiles on an HTML5 canvas. However, when I run this code it throws up error messages when running the draw_terrain() function and it appears to be a problem with the blockArray.length component. Can someone explain to me how to fix this problem? Thanks in advance.
//Define initial canvas variables and images
var canvas;
var ctx;
var WIDTH = 800;
var HEIGHT = 800;
var dirt = new Image();
dirt.src = 'dirt.png';
//Function called to initialise canvas variables and run draw on interval
function init(){
canvas = document.getElementById('myCanvas');
ctx = canvas.getContext('2d');
return setInterval(draw, 15);
}
//Function that generates an 8x8 Array called blockArray
function gen_terrain(){
var blockArray = new Array(8);
for(var i=0; i<blockArray.length; i++) {
blockArray[i] = new Array(8);
for(var j=0; j<blockArray[i].length; j++){
blockArray[i][j] = 0;
};
};
}
//Function that returns a random number between a min and max
function randomRange (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function draw_terrain(){
for(var i=0; i<blockArray.length; i++) {
for(var j=0; j<blockArray[i].length; j++){
ctx.drawImage(dirt,(n-1)*32,(j-1)*32);
};
};
}
function draw(){
draw_terrain();
}
gen_terrain();
init();
Your problem, as other people have explained is that the variable you are using the build the array will not exist by the time the draw occurs. Just place your array declaration outside of the function and your issue will go away.
See comment below:
function init(){
canvas = document.getElementById('myCanvas');
ctx = canvas.getContext('2d');
return setInterval(draw, 15);
}
//Function that generates an 8x8 Array called blockArray
var blockArray = []; // <== has to be global (outside function).
function gen_terrain(){
// Not here -> var blockArray = [];
for(var i=8; i--;) {
blockArray[i] = [0,0,0,0,0,0,0,0];
};
}
Example
First time poster here but definitely not a first time reader.
My question is aimed directly at this portion of code I have. I am currently learning how HTML 5 canvases work and am designing my own RPG style game for a University project. After looking around I found some good tutorials on this guys blog, I have followed his code and triple checked it but images are now showing up.
I tried putting an alert() before and after when the image is called to the canvas under drawMap(). It works before the image is drawn but not after, leading me to believe it is something to do with my image rendering. Can someone double check my code and see what is going on? It's driving me insane!
<canvas id="game-viewport" width="760" height="440"></canvas>
<script>
window.onload = init;
var map = Array([0,0],[0,0],[0,0],[0,0]);
var tileSize = 40;
tileTypes = Array("grass.png");
tileImage = new Array();
var loaded = 0;
var loadTimer;
function loadImage(){
for(i = 0; i < tileTypes.length; i++){
tileImage[i] = new Image();
tileImage[i].src = "./game/lib/icons/own_icons/" + tileTypes[i];
tileImage[i].onload = function(){
loaded++;
}
}
}
function loadAll(){
if(loaded == tileTypes.length){
clearInterval(loadTimer);
drawMap();
}
}
function drawMap(){
var mapX = 80;
var mapY = 10;
for(i = 0; i < map.length; i++){
for(j = 0; j < map[i].length; j++){
var drawTile = map[i][j];
var xPos = (i - j) * tileSize;
var yPos = (i + j) * tileSize;
ctx.drawImage(tileImage[drawTile], xPos, yPos);
}
}
}
function init(){
var canvas = document.getElementById('game-viewport')
var ctx = canvas.getContext('2d');
loadImage();
loadTimer = setInterval(loadAll, 100);
}
</script>
The only problem is that ctx is not defined in your drawMap function.
Either pass ctx in to the function as an argument or make it a global variable.
I was lazy and did the second, but you should really do the first. Working code:
http://jsfiddle.net/YUddC/
You really should have the Chrome debugger (or whatever browser you use) on 100% of the time you're developing.. If you did, you'd see an error saying that ctx is not defined in drawMap. If you're using Chrome and press F12 to open developer tools and go to the scripts tab, you'd see this:
Which makes the problem pretty clear!