Draw part of an image inside html5 canvas - javascript

I am trying to draw the first Green tank inside my canvas but i think i am missing something in my code
jsfiddle:
draw: function () {
tankImg.onload = function () {
ctx.drawImage(tankImg, this.Pos * this.w, 0, this.w, this.h, this.x, this.y, this.w, this.h);
};
}
Can anyone help me please?

Ok, first of all thanks so much for the fiddle - makes everything a billion times easier.
There are two three errors in your code - the first is the tankImg.onload function. The onload function only fires once - when the image first loads.
What you want is tankImg.complete which will return true if loaded and false if not.
Secondly, you cannot assign 'ch - this.h' for the y property because 'this' isn't defined until, well, you finish defining it. What you can do is set the y value in your draw code.
Thirdly in javascript you can't do var cw, ch = 400;
$(document).ready(function () {
var cw = 400;
var ch = 400;
var ctx = $("#MyCanvas")[0].getContext("2d");
var tankImg = new Image();
tankImg.src = "http://oi62.tinypic.com/148yf7.jpg";
var Fps = 60;
var PlayerTank = {
x: cw / 2,
w: 84,
h: 84,
Pos: 2,
draw: function () {
ctx.drawImage(tankImg, this.Pos * this.w, 0, this.w, this.h, this.x, ch - this.h, this.w, this.h);
}
};
var game = setInterval(function () {
if (tankImg.complete) {
PlayerTank.draw();
PlayerTank.x++;
}
}, 1000 / Fps);
});
Here is the completed jsfiddle with bonus movement for fun.
Edit: Ken's version of handling the .onload is much better.

There are a few minor issues with the code which prevents:
Image to be loaded properly
Some uninitialized variables
Some inaccessible variables
The first variable that is undefined (producing NaN for the drawImage() method) is:
var cw; /// is not defined
The second more important issue is the way you handle the image loading.
You are currently setting the onload handler inside the draw method and re-setting it for each frame. Here the onload will probably not be caught and using the draw code inside it will at best only draw the image once as onload is only called once.
Change the code around a little like this:
var tankImg = new Image();
tankImg.onload = start; /// add the onload handler before setting src.
tankImg.src = "http://oi62.tinypic.com/148yf7.jpg";
Then move your invoke of setInterval to start:
var game; /// keep this var outside (in global)
function start() {
game = setInterval(function () {
PlayerTank.draw();
}, 1000 / Fps);
}
The third issue is that this.h is not defined when you try to use it. Add a manual value (as in the modified fiddle) or wait until draw() is called to calculate it.
Now it should work:
Modified fiddle here

Related

Can we use a predefined function inside a custom function in JavaScript?

//making custom function for a fish
drawFish=function (centerX,centerY){
//using draw function for animation
draw=function(){
var bodyLength = 115;
var bodyHeight = 74;
var bodyColor = color(162, 0, 255);
noStroke();
fill(bodyColor);
// body
ellipse(centerX, centerY, bodyLength, bodyHeight);
// tail
var tailWidth = bodyLength/4;
var tailHeight = bodyHeight/2;
triangle(centerX-bodyLength/2, centerY,
centerX-bodyLength/2-tailWidth, centerY-tailHeight,
centerX-bodyLength/2-tailWidth, centerY+tailHeight);
// eye
fill(33, 33, 33);
ellipse(centerX+bodyLength/4, centerY, bodyHeight/5, bodyHeight/5);
centerX++;
};
};
//calling custom function
drawFish(146,208);
drawFish(207,212);
drawFish(305,306);
drawFish(114,309);
drawFish(300,100);
I am trying to make a simple animation of 5 fishes in a tank moving horizontally. I wrote the code for one fish and then I make it a custom function so I can call it as many times as I want. Then I put the
draw =function(){} for animation to move the fishes. This code works until I put the draw() function into it otherwise only one fish remains and others disappear. How can we use the draw function to make an animation inside this custom function so that it works on every fish.
You can try using the operator new. I am not entirely sure if it will work in the way you coded it, but you can do something like:
function drawFish(centerX, centerY) {
this.centerX = centerX;
this.centerY = centerY;
// draw your fish here using centerX and centerY as it's X and Y
}
var fish = new drawFish(0,0);
var fish1 = new drawFish(50,50);
function Loop() {
fish.centerX++ ; fish.centerY++ ;
fish1.centerX++; fish1.centerY++;
console.log('Fish position:', fish.centerX, fish.centerY)
console.log('Fish1 position:',fish1.centerX, fish1.centerY);
}
setInterval(Loop, 500)

Reusable canvas code?

I need to use several canvas-es with different values (see data-percent) with same reusable code block but "animation" makes it a little bit tricky. Im not sure how to make it reusable. Copy-pasting the same code over and over again is obviously a wrong move, I usually avoid it at any cost.
First thing is obviously to remove id and use class instead, then I could select all the canvas-es:
<canvas class="circle-thingy" width="120" height="120" data-percent="75"></canvas>
<canvas class="circle-thingy" width="120" height="120" data-percent="23"></canvas>
<canvas class="circle-thingy" width="120" height="120" data-percent="89"></canvas>
var allCircles = document.getElementsByClassName('circle-thingy');
But now comes the trickier part.. How about canvas JavaScript code? There's probably a very easy solution but I can't see it! Terrible time to quit smoking I guess (as always), brain is like shut down.
What I tried: for loop with allCircles list. Problem is that I cannot use setInterval and clearTimeout with this approach. Dynamic variable names? How do I reference them later?
Here's my code with a single circle, try it.
// Get canvas context
var ctx = document.getElementById('my-circle').getContext('2d');
// Current percent
var currentPercent = 0;
// Canvas north (close enough)
var start = 4.72;
// Dimensions
var cWidth = ctx.canvas.width;
var cHeight = ctx.canvas.height;
// Desired percent -> comes from canvas data-* attribute
var finalPercent = ctx.canvas.getAttribute('data-percent');
var diff;
function circle() {
diff = ((currentPercent / 100) * Math.PI * 2 * 10).toFixed(2);
ctx.clearRect(0, 0, cWidth, cHeight);
ctx.lineWidth = 3;
// Bottom circle (grey)
ctx.strokeStyle = '#eee';
ctx.beginPath();
ctx.arc(60, 60, 55, 0, 2 * Math.PI);
ctx.stroke();
// Percent text
ctx.fillStyle = '#000';
ctx.textAlign = 'center';
ctx.font="900 10px arial";
ctx.fillText(currentPercent + '%', cWidth * 0.5, cHeight * 0.5 + 2, cWidth);
// Upper circle (blue)
ctx.strokeStyle = '#0095ff';
ctx.beginPath();
ctx.arc(60, 60, 55, start, diff / 10 + start);
ctx.stroke();
// If has desired percent -> stop
if( currentPercent >= finalPercent) {
clearTimeout(myCircle);
}
currentPercent++;
}
var myCircle = setInterval(circle, 20);
<canvas id="my-circle" width="120" height="120" data-percent="75"></canvas>
Feel free to use this code snippet in your own projects.
You can use bind to solve this.
Create a helper function that will start animation for given canvas:
function animateCircle(canvas) {
var scope = {
ctx: canvas.getContext('2d')
// other properties, like currentPercent, finalPercent, etc
};
scope.interval = setInterval(circle.bind(scope), 20);
}
Change your circle function to refer variables from this instead of global ones:
function circle() {
// your old code with corresponding changes
// e.g.
var ctx = this.ctx; // references corresponding scope.ctx
// or
this.currentPercent++; // references corresponding scope.currentPercent
}
Working JSFiddle, if something is not clear.

javascript canvas setTimeout console TypeError: is not a function

I'm trying to execute a function 'draw' when a button is clicked (button id is also draw). When clicking the button the function runs successfully, however the repeated iterations do not run. I've therefore narrowed the issue to the setTimeout. I get the following message "draw is not a function". This is confusing because it definitely IS a function. Any help is appreciated. Thanks.
var draw1 = document.getElementById('draw');
draw1.addEventListener('click',draw);
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var x = 0;
var y = 0;
function draw(aaa,bbb){
ctx.save();//save the cavas state if required
ctx.clearRect(0,0,100,100)//clear canvas for redrawing
ctx.fillStyle = 'rgba(0,200,0,1)';//style a green box
ctx.fillRect (x, 20, 50, 50);//draw the rectangle
ctx.restore();//restore the canvas state if saved
x += 5;//increment the x position by some numeric value
console.log(x);
console.log(y);
var loopTimer = setTimeout('draw(' + x +','+ 0 +')',200);
}
Don't use strings for function call. It is a really bad practice.
Change it to an anonymous function:
var loopTimer = setTimeout(function() {
draw(x, 0);
}, 200);
As long as your x and y are global and you don't use arguments, you can remove them from your draw method and call it even simpler, without closures:
function draw(){
ctx.save();//save the cavas state if required
ctx.clearRect(0,0,100,100)//clear canvas for redrawing
ctx.fillStyle = 'rgba(0,200,0,1)';//style a green box
ctx.fillRect (x, 20, 50, 50);//draw the rectangle
ctx.restore();//restore the canvas state if saved
x += 5;//increment the x position by some numeric value
console.log(x);
console.log(y);
var loopTimer = setTimeout(draw, 200);
}

Why is my canvas not clearing correctly?

I'm currently working on a little experimental project with HTML5 Canvas.
Basically, at the moment i'm just trying to make the canvas clear as expected. The only thing that my code does as the moment is generate a lines that are broken in the middle. However, at the moment i'm trying to make one line, and then remove that line and add another line at another different position without showing the first line.
I would of thought that this code would work:
poles(20); // this number is the x position at which the line (pole) will be generated
ctx.clearRect(0, 0, WIDTH, HEIGHT);
poles(140)
In all technically, this should show only the second pole because the canvas should have been cleared after the first pole was generated. But hit still shows both.
When I tried only:
poles(20);
ctx.clearRect(0, 0, WIDTH, HEIGHT);
The canvas was blank which told me that the clearing worked correctly.
I tried one more thing:
poles(20);
ctx.clearRect(0, 0, WIDTH, HEIGHT);
setTimeout(function () {
poles(140)
}, 1000);
In this case, both poles did show up but not until after 1 second which told me that the poles function is causing both to be generated again even though the function doesn't loop:
function poles(x) {
var bottomH = getRandomInt(20, 180)
// using seperate rectangles will make a break
rect(40, 220 - bottomH, x, 0); // first section of line
rect(40, bottomH, x, HEIGHT - bottomH); // second section of line
}
I hope someone can explain to me how come my poles function is cause both of the poles to reappear.
You can view the example here.
For Reference, the main code is:
var canvas = document.getElementById("canvas"),
WIDTH = canvas.width,
HEIGHT = canvas.height;
var ctx = canvas.getContext("2d");
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function rect(w, h, x, y) {
ctx.rect(x, y, w, h);
ctx.fill();
}
function poles(x) {
var bottomH = getRandomInt(20, 180); // determine where the line break will be
rect(40, 220 - bottomH, x, 0);
rect(40, bottomH, x, HEIGHT - bottomH);
}
poles(20);
ctx.clearRect(0, 0, WIDTH, HEIGHT);
setTimeout(function () {
poles(140)
}, 1000);
The problem lies with your rect function. Specifically, with the usage of the .rect() member of the ctx. The rect member creates a path, which you then fill with ctx.fill() - only problem is, it doesn't close the path, so it remains open and is added to when the second call to poles comes along.
You can either close the path before exiting the rect function, or even more simply, avoid paths altogether by defining and filling the rectangle in a single call, by using ctx.fillRect.
The following change makes the code function as expected:
function rect(w, h, x, y)
{
// ctx.rect(x, y, w, h);
// ctx.fill();
ctx.fillRect(x,y,w,h);
}

Function hooking with drawing to canvas etc

Hey guys In another question about function pushing-Here
I was then given this code -
var postDrawHooks = [];
var draw = function(){
// do stuff
postDrawHooks.forEach(function(hook){hook()});
}
var playerUpdate = function(){...};
postDrawHooks.push(playerUpdate);
Which works very nicely, and allows you to push an outide function into another one like your game loop without having to edit your engine.js file for every game if you want to add an update to an object just push the hook. Anyways I need a way to push drawing like Context2D.drawImage(blah blah blah); now when I try to push this like above below I get the error trying to draw from null.
post_draw_render.push(context2D.drawImage(player.tilesheet, player.frameX,player.frameY, 46, 45, player.PosX, player.PosY+1, 46, 45));
I can't even use Context2D.drawImage anywhere oustide of my draw function and I don't know why any Ideas?
engine -
//engine vars//
function init() {
canvas = document.getElementById('canvas');
context2D = canvas.getContext('2d');
setInterval(draw, 1000/FPS);
}
function draw() {
post_draw_function.forEach(function(hook){hook()});
gameloop();
context2D.clearRect(0, 0, canvas.width, canvas.height);
post_draw_render.forEach(function(hook){hook});
//Basic Draw - context2D.drawImage(playerImg, player.PosX, player.PosY);
//Tilesheet draw - context2D.drawImage(tilesheet, sx, sy, sw, sh, dx, dy, dw, dh);
context2D.fillStyle = "white";
context2D.font = 'bold 25px Times New Roman';
}
The object you are pushing into the array is not a function, it's the return value of a call to the drawImage function.
I think you want
post_draw_render.push(function () {
context2D.drawImage(player.tilesheet, player.frameX,player.frameY, 46, 45, player.PosX, player.PosY+1, 46, 45);
});

Categories

Resources