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);
});
Related
so I am trying to make thisgame called GO and I need to place circles in the edges like this. I've made the grid and now need to place the circles in there. I 'v tried different things like using the arc in canvas with mouse position and placing it but it isn't working something else I tried is to make an array that checks where the lines cross but it still didn't do anything. but i may have done it wrong. I'm not sure what's wrong so hope you guys could help me find a way to place the circles on the board every time I click the mouse. I've deleted the things that didn't work and this is my code now:
const canvas = document.getElementById("myCanvas")
const ctx = canvas.getContext("2d")
//canvas.style.border = "1px solid black"
let w = ctx.canvas.width
let h = ctx.canvas.height
goBoard = []
goCheckBoard = []
function drawGrid(w, h) {
for (x = 0; x <= w; x += 40) {
for (y = 0; y <= h; y += 40) {
ctx.moveTo(x, 0);
ctx.lineTo(x, h);
ctx.stroke();
ctx.moveTo(0, y);
ctx.lineTo(w, y);
ctx.stroke();
}
}
}
drawGrid(400, 400)
this just makes the grid
one example i've tried is this:
mouseClicked = function () {
ctx.beginPath()
ctx.strokeStyle = "black"
ctx.ellipse(mouseX, mouseY, 20, 20);
ctx.stroke()
};
another:
function rtn(n, r){
return Math.round(n/r)*r;
}
canvas.onclick = function(event){
ellipse(rtn(event.clientX, 40), rtn(event.clientY, 40), 180, 180);
};
im not sure if that is the correct way to do it
maybe make an array of all the possible places to place the circle but im not sure how
here is a fiddle if you want to test it out https://jsfiddle.net/pwe0vx7o/
you first need to change the ellipse(...) to ctx.ellipse(...), because ellipse isn't a global function, but a metod of the CanvasRenderingContext2D.
Through the docs I also found that this function takes 7 arguments so added ones for radiusX, radiusY, rotation, startAngle, endAngle.
Here is also a fiddle for the finished result: https://jsfiddle.net/c6udn9gf/8/
This will get you close, but you need to figure out something with your rounding function.
canvas.onclick = function (event) {
ctx.beginPath();
const x = rtn(event.clientX, 40);
const y = rtn(event.clientY, 40);
ctx.ellipse(x - 20, y - 20, 10, 10, 0, 0, 360);
ctx.fill();
};
I've created a grid of several distorted rectangles made with Bezier curves. Each rectangle has its own color on the picture.
Let's say, I want to add hover effect for each of these rectangles, therefore I need to know its dimensions. Since I can fill or stroke the figure I assume that there is some way to get them, but I'm not sure.
Here is the example of the rectangles:
So the question is, is there some method in the canvas API with which I can achieve the desired effect?
Yes you can use isPointInPath(Path2D, x, y) method.
Note that if you don't use the Path2D object, you can also call it just with isPointInPath(x, y), but then it will check on the currently being drawn path (declared with beginPath()).
var ctx = canvas.getContext('2d');
var myPath = new Path2D();
myPath.bezierCurveTo(50, 100, 180, 10, 20, 10);
myPath.lineTo(50, 100);
function draw(hover) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = hover ? 'red' : 'green';
ctx.fill(myPath);
}
canvas.onmousemove = function(e) {
var x = e.clientX - canvas.offsetLeft,
y = e.clientY - canvas.offsetTop;
var hover = ctx.isPointInPath(myPath, x, y)
draw(hover)
};
draw();
<canvas id="canvas"></canvas>
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);
}
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
I want to be able to draw an array of balls, I can push the balls into the array (when clicking the canvas).
I can draw 1 of the balls on the canvas if I create it with ball = new Ball, but as soon as I try to draw from inside the array it breaks.
I draw inside a for loop with balls[i].draw
Here's a jsFiddle.
And here's the relevant code:
function init(){
defaultBall();
for(i=0;i<balls.length;i++){
balls[i].draw;
}
ball.draw();
}
function Ball(X, Y, Radius, Color){
this.X = X || 0;
this.Y = Y || 0;
this.radius = 5;
this.color = Color;
}
Ball.prototype.draw = function(){
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.X, this.Y, this.radius, 10, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
}
var balls = [];
function defaultBall(){
addBall(new Ball(50, 50, 5, '#6B7E00'));
addBall(new Ball(150, 50, 5, '#6B7E00'));
}
function addBall(ball){
balls.push(ball);
}
var ball = new Ball(50, 50, 5, '#6B7E00');
How should I formulate the loop to be able to draw the balls?
Help appriciated.
Maybe
balls[i].draw;
should be
balls[i].draw();
on line 4
Your init method isn't getting called again. If you change
function addBall(ball){
balls.push(ball);
ball.draw();
}
to
function addBall(ball){
balls.push(ball);
}
you'll see the new balls. You need to have some sort of mechanism that calls init (aka draw) again.
And as Frits pointed out you will need to use draw() instead of draw.
Your code runs just fine, it is just that there is a small error with your balls loop.
defaultBall();
for(i=0;i<balls.length;i++){
balls[i].draw;
}
ball.draw();
You forgot to use the parenthesis to call the draw method when looping through your balls. The fix is simple:
defaultBall();
for(i=0;i<balls.length;i++){
balls[i].draw();
}
ball.draw();
Note that now your balls will show up as intended. When the fiddle is edited with this new fix for looping through the balls you can see two green balls show up.