I am getting no output. I'm trying to understand getInterval as I am new to JS, but can't work out why I don't get the lines displayed.
var Canvas = {
canvas : undefined,
ctx : undefined
};
var Mouse = {
x : [0],
y : [0]
};
function Drawing(width, colour){
this.width = width;
this.colour = colour;
Drawing.prototype.output = function(ctx){
ctx.strokeStyle = this.colour;
ctx.lineWidth = this.width;
for (var i = 0; i < Mouse.x.length-1; i++) {
ctx.beginPath();
ctx.moveTo(Mouse.x[i], Mouse.y[i]);
ctx.lineTo(Mouse.x[i+1], Mouse.y[i+1]);
ctx.stroke();
}
}
}
Canvas.start = function () {
function catchAction(evt) {
Mouse.x[Mouse.x.length] = evt.pageX;
Mouse.y[Mouse.y.length] = evt.pageY;
}
Canvas.canvas = document.getElementById("myCanvas");
Canvas.canvas.width = Canvas.canvas.height = 600;
Canvas.ctx = Canvas.canvas.getContext('2d');
let drawing = new Drawing(10, 'red');
Canvas.canvas.addEventListener("mousedown", catchAction, false);
Canvas.canvas.addEventListener("touchstart", catchAction, false);
window.setInterval(drawing.output(Canvas.ctx), 500);
};
document.addEventListener( 'DOMContentLoaded', Canvas.start);
Also I am getting a Violation: Added non-passive event listener.
The first argument to setInterval should be a string if you are going to pass in code like that.
https://developer.mozilla.org/en-US/docs/Web/API/setInterval .
Try changing it to a function like this:
window.setInterval(() => {drawing.output(Canvas.ctx)}, 500);
also try this for you other error:
document.addEventListener( 'DOMContentLoaded', (e) => {Canvas.start()});
NOTE: I haven't tested either suggestion, but I'm 99% certain the setInterval one is correct.
Related
cancelAnimationFrame() does not seem to work when called inside an object's method. I have tried binding the this value to the callback function (as demonstrated on MDN with setTimeout) but I received a TypeError when using cancelAnimationFrame(). I then tried setting the this value to a local variable called _this and called cancelAnimationFrame() again. That time, I did not receive an error but the animation itself is still playing. How do I cancel the animation?
I have recreated the issue I am having below. If you open a console window, you will see that the animation is still running.
function WhyWontItCancel() {
this.canvas = document.createElement("canvas");
this.canvas.width = 200;
this.canvas.height = 10;
document.body.appendChild(this.canvas);
this.draw = this.canvas.getContext("2d");
this.draw.fillStyle = "#f00";
this.position = 0;
};
WhyWontItCancel.prototype.play = function() {
if (this.position <= 190) {
this.draw.clearRect(0, 0, 400, 10);
this.draw.fillRect(this.position, 0, 10, 10);
this.position += 2;
} else {
//window.cancelAnimationFrame(this.animation.bind(this));
var _this = this;
window.cancelAnimationFrame(_this.animation);
console.log("still running");
}
this.animation = window.requestAnimationFrame(this.play.bind(this));
};
var animation = new WhyWontItCancel();
animation.play();
Seems that you miss two things here. First, this.animation = window.requestAnimationFrame(this.play.bind(this)); line is invoked always when play() is called. Contrary to what you might think, cancelAnimationFrame only removes the previously requested RAF call. Strictly speaking, it's not even necessary here. Second, you don't have to bind on each RAF call; you might do it just once:
function AnimatedCanvas() {
this.canvas = document.createElement("canvas");
this.canvas.width = 200;
this.canvas.height = 10;
document.body.appendChild(this.canvas);
this.draw = this.canvas.getContext("2d");
this.draw.fillStyle = "#f00";
this.position = 0;
this.play = this.play.bind(this); // takes `play` from prototype object
};
AnimatedCanvas.prototype.play = function() {
if (this.position <= 190) {
this.draw.clearRect(0, 0, 400, 10);
this.draw.fillRect(this.position, 0, 10, 10);
this.position += 2;
this.animationId = window.requestAnimationFrame(this.play);
}
};
You might want to add cancel into your prototype to be able to stop your animation, for example:
AnimatedCanvas.prototype.cancel = function() {
if (this.animationId) {
window.cancelAnimationFrame(this.animationId);
}
};
... but the point is, it's not useful in the use case described in the question.
i can't get the image to move in the canvas. tried many things and failed please help
var canvas = document.getElementById("mainCanvas");
canvas.width = document.body.clientWidth;
canvas.height = document.body.clientHeight;
canvas.style.width = canvas.width + "px";
canvas.style.height = canvas.height + "px";
start here
var player = function(img){
x=10,
y=10,
width=20,
height=20
}
add the player
window.addEventListener("keydown", function(e){
keys[e.keyCode] = true;
}, false);
window.addEventListener("keyup", function(e){
delete keys[e.keyCode];
}, false);
function game(){
update();
render();
}
function update(){
if(keys[37]) player.y--;
if(keys[38]) player.y++;
if(keys[39]) player.x--;
if(keys[40]) player.x++;
};
i need to know how to get the player to move
I would suggest changing your code logic. First, use requestAnimationFrame() to register method to be fired that will render/update your canvas automatically only when it is needed. Read the basic details here.
I do not know how your logic is implemented, the source you posted is rather incomplete, but suggesting you change var player into object instead of function (so player.x++ will work):
function render() {
//your render code here
player1.draw();
requestAnimationFrame(render);
}
function Player(img){
this.x = 10;
this.y = 10;
this.height = 20;
this.width = 20;
this.img = img;
}
Player.prototype.draw = function() {
canvas.drawImage(this.img);
};
//create instance of Player
player1 = new Player(handleToYourPlayerImage);
// start it
requestAnimationFrame(render);
(Don't forget to add your keyboard handling code to that.)
i have been having trouble with reading a mouse position on a canvas. The code is working (semi) correctly as it reads the position when clicking he canvas in IE but only on one frame, in chrome it is just displaying the value as 0.
Here is the full code:
<script>
var blip = new Audio("blip.mp3");
blip.load();
var levelUp = new Audio("levelUp.mp3");
levelUp.load();
var canvas = document.getElementById('game');
var context = canvas.getContext('2d');
context.font = '18pt Calibri';
context.fillStyle = 'white';
//load and draw background image
var bgReady = false;
var background = new Image();
background.src = 'images/background.jpg';
background.onload = function(){
bgReady = true;
}
var startMessage = 'Click the canvas to start';
//load plane image
var planeReady = false;
var planeImage = new Image();
planeImage.src = 'images/plane.png';
planeImage.onload = function() {
planeReady = true;
}
//load missile image
var missileReady = false;
var missileImage = new Image();
missileImage.src = 'images/missile-flipped.gif';
missileImage.onload = function() {
missileReady = true;
}
//initialise lives and score
var score = 0;
var lives = 3;
var missilesLaunched = 0;
var missileSpeed = 5;
var level = 1;
var missileX = 960;
var missileY = Math.random() * 500;
if (missileY > 480) {
missileY = 480;
}
function getMousePos(canvas, event) {
return {
x: input.x - rect.left,
y: input.y - rect.top
};
}
function update_images(event) {
var pos = getMousePos(canvas.getBoundingClientRect(), mouseInput);
planeImage.y = pos.y;
missileX = missileX - missileSpeed;
if (missileX < - 70) {
missilesLaunched++;
missileX = 960;
missileY = Math.random() * 500;
if (missileY > 480) {
missileY = 480;
}
blip.play();
score = missilesLaunched;
if (score % 5 == 0) {
missileSpeed = missileSpeed + 2;
level++;
levelUp.play();
}
}
}
function reload_images() {
if (bgReady = true) {
context.drawImage(background, 0, 0);
}
if (planeReady = true) {
context.drawImage(planeImage, 10, planeImage.y);
}
if (missileReady = true) {
context.drawImage(missileImage, missileX, missileY);
}
context.fillText('Lives: ' + lives, 200, 30);
context.fillText('Score: ' + score, 650, 30);
context.fillText('Level: ' + missileSpeed, 420, 30);
context.fillText('Position: ' + missileImage.y, 420, 70);
}
function main(event) {
var mouseInput = { x: 0, y: 0 };
document.addEventListener("mousemove", function (event) {
mouseInput.x = event.clientX;
mouseInput.y = event.clientY;
});
update_images(event);
reload_images();
if (lives > 0) {
window.requestAnimationFrame(main);
}
else {
}
}
function start() {
context.drawImage(background, 0, 0);
context.fillText('Click the canvas to start', 350, 250);
function startMain(event) {
game.removeEventListener("click", startMain);
main(event);
}
canvas.addEventListener("mousedown", startMain);
}
start();
</script>
Joe, you should actually be capturing the mouse position every time you click...
...but you're actually also starting a new game (without stopping the old one), every time you click, too.
First problem: starting game engine several times to draw on the same instance of the canvas
Solution:
In your start function, you need to remove the mousedown event listener, after you've triggered it.
function start () {
// ... other setup
function startMain (event) {
canvas.removeEventListener("click", startMain);
main(event);
}
canvas.addEventListener("click", startMain);
}
Now it will only listen for the first click, before starting, and will only start once.
Second Problem: mouse doesn't update as expected
Solution: two issues here...
...first, you are passing event into main on first call...
...after that, you're passing main into requestAnimationFrame.
requestAnimationFrame won't call it with an event, it will call it with the number of microseconds (or ms or some other unit as a fractional precision of ms) since the page was loaded.
So the first time you got main({ type: "mousedown", ... });.
The next time you get main(4378.002358007);
So lets refactor the startMain we had above, so that main never ever collects an event, just a time.
function startMain ( ) {
canvas.removeEventListener("click", startMain);
requestAnimationFrame(main);
}
The next problem is that even if you were getting just events, you're only ever capturing a click event (which as we mentioned earlier, fires a new copy of the game logic).
Your solution is to separate the code which catches mouse events from the code which reads mouse position.
var mouseInput = { x: 0, y: 0 };
document.addEventListener("mousemove", function (event) {
mouseInput.x = event.clientX;
mouseInput.y = event.clientY;
});
function getMousePos (rect, input) {
return {
x : input.x - rect.left,
y : input.y - rect.top
};
}
// currently in updateImages (should not be there, but... a different story)
var pos = getMousePos(canvas.getBoundingClientRect(), mouseInput);
You've got other problems, too...
You're calling getMousePos and passing in game at the moment. I don't see where game is defined in your JS, so either you're making game somewhere else (begging for bugs), or it's undefined, and your app blows up right there.
You should really be building this with your console / dev-tools open, in a hands-on fashion, and cleaning bugs in each section, as you go.
I'm trying to create a simple Sprite class for use in a canvas game, but the image I specify when I create a new instance of the class never calls onload or onerror.
What's happening, and how do I get it to load and draw the image?
Here's the script:
var cvs = document.getElementById("c");
var ctx = cvs.getContext("2d");
var imagesToLoad = 1 ;
var imagesLoaded = 0;
var gameState = 0;
//START OF IMAGE STUFF//
function imageOnload() {
imagesLoaded+=1;
console.log("Images: loaded "+imagesLoaded+" overall.");
}
function loadLoop() {
if (imagesLoaded == imagesToLoad) {
gameState = 1;
}
}
function loop1() {
ctx.drawImage(circle.image);
}
function gameLoop() {
if(gameState==0){
loadLoop();
}
if(gameState==1){
loop1();
}
}
function Sprite(positionsArray){
this.x = positionsArray[0];
this.y = positionsArray[1];
this.dx = positionsArray[2];
this.dy = positionsArray[3];
this.angle = positionsArray[4];
this.rotating = positionsArray[5];
};
circle = new Sprite(new Array(0, 0, 0, 0, 0, 0));
circle.image = new Image().onload="imageOnload()".src="circle.png".onerror="console.log(\"Nope.\")";
console.log("circle?");
setInterval(function(){gameLoop()}, 10);
//END OF IMAGE STUFF//
Corrections:
No quotes on image.onload function definition
No parens on image.onload function definition
image.onerror requires a function definition or anonymous function
Put image.onerror before image.src
Here's some example code:
circle.image=new Image();
circle.image.onload=imageOnLoad;
circle.image.onerror=function(){console.log("Image failed to load")};
circle.image.src="koolaidman.png";
function imageOnLoad(){
ctx.drawImage(circle.image,0,0); // or do other stuff
}
I'm working on a proof of concept on an HTML5 canvas. I was able to get this working like a charm in Chrome and IE9, but in Firefox 4 I'm getting constant flicker as it redraws the canvas. I've tried a few techniques mentioned on this site like double buffering but I'm still getting a large amount of flicker. Any insight on this would be appreciated!
<!DOCTYPE HTML>
<html>
<head>
<style type="text/css">
body
{
margin:0px;
padding:0px;
text-align:center;
}
canvas
{
outline:0;
border:1px solid #000;
margin-top: 10px;
margin-left: auto;
margin-right: auto;
}
</style>
<script type="text/javascript">
var canvasWidth = 640;
var thisXPos = 0;
var canvasHeight = 820;
var thisYPos = 0;
var canvas = null;
var context = null;
var gLoop = null;
var rain = [];
var rainImg = "images/raindrop.gif";
var bgImg = null;
var i;
for (i = 0; i < howManyLetters; i++)
{
rain.push([Math.floor(Math.random() * canvasWidth), Math.floor(Math.random() * canvasHeight),rainImg]);
}
var DrawRain = function()
{
for (i = 0; i < 10; i++)
{
thisXPos = rain[i][0];
thisYPos = rain[i][1];
imgSrc = rain[i][2];
letterImg = new Image();
letterImg.setAtX = thisXPos;
letterImg.setAtY = thisYPos;
letterImg.onload = function()
{
context.drawImage(this, this.setAtX, this.setAtY);
}
letterImg.src = imgSrc;
}
};
var MoveRain = function(e)
{
for (i = 0; i < 10; i++)
{
if ((rain[i][1] - 5) > canvasHeight)
{
randomnumber = Math.floor(Math.random()*26);
rain[i][0] = Math.random() * canvasWidth;
rain[i][1] = 0;
rain[i][2] = rainImg;
}
else
{
rain[i][1] += e;
}
}
};
var clear = function()
{
context.beginPath();
context.rect(0, 0, canvasWidth, canvasHeight);
context.closePath();
bgImg = new Image();
bgImg.src = "images/bg.png";
bgImg.onload = function()
{
context.drawImage(bgImg,0,0);
}
}
var GameLoop = function()
{
context.save();
clear();
MoveRain(1);
DrawRain();
context.restore();
gLoop = setTimeout(GameLoop, 10);
}
function loadGame()
{
canvas = document.getElementById("gameCanvas");
context = canvas.getContext("2d");
canvas.width = canvasWidth;
canvas.height = canvasHeight;
GameLoop();
}
</script>
</head>
<body onload="loadGame();">
<canvas id="gameCanvas"></canvas>
</body>
</html>
I have distilled your example down to this:
http://jsfiddle.net/sPm3b/6/
And it works very fast in Firefox and Chrome.
So we know that the problem lies in the images.
You need to optimize how they are created and loaded. Right now, each clear() creates a new image and waits for it to load! That image should be created only once, in your loadGame() and then reused over and over.
Same exact deal with letterImg in DrawRain(). Move the creation of it to loadGame()
That will probably fix the problem.
EDIT:
like this:
At the top add:
var letterImg = new Image();
var bgImg = new Image();
Then
function loadGame()
{
canvas = document.getElementById("gameCanvas");
context = canvas.getContext("2d");
canvas.width = canvasWidth;
canvas.height = canvasHeight;
bgImg.src = "images/bg.png";
letterImg.src = "images/raindrop.gif";
// optional: wait for them to load here
GameLoop();
}
Then drawRain, for example, would look like this:
var DrawRain = function()
{
for (i = 0; i < 10; i++)
{
thisXPos = rain[i][0];
thisYPos = rain[i][1];
context.drawImage(letterImg, thisXPosX, thisYPos); // letterImg was already constructed, no need to make it again
}
};
In complement to Simon Sarris response. I've used a 'double canvas' technique to avoid screen fickering with heavy canvas.
The way it works is always have 2 version of the canvas, one in DOM, one outside, and always draw on the one which is not in DOM. I use it with a redraw queue.
here's a part of a working code
(...)
clear: function() {
//rotating on 2 canvas, one for draw (outside DOM) one for show
var self = this;
if (null == self.canvasbackup) {
var tmpcanvas = self.canvas.clone(true);
self.canvasbackup = self.canvas;
self.canvas=tmpcanvas;
} else {
var tmpcanvas = self.canvasbackup;
self.canvasbackup = self.canvas;
self.canvas=tmpcanvas;
}
self.ctx = self.canvas[0].getContext('2d');
self.ctx.clearRect( 0, 0, self.options.width, self.options.height );
jQuery.each(self.elements,function(idx,elt){
// custom function: my elements need to know which canvas they depends on
elt.reconnectCanvas(self.canvas,self.ctx);
});
},
inDOM: function() {
var self = this;
if(null==self.canvasbackup) {
//1st time need to get all things in DOM
self.canvas.appendTo(self.div);
self.div.appendTo(self.container);
} else {
// remove current shown canvas
self.connectHuman();
self.canvasbackup.remove();
// loosing some events here...
self.canvas.appendTo(self.div);
// div is already in DOM, we are in redraw
}
},
redraw: function() {
var self = this;
self.clear();
jQuery.each(self.elements,function(idx,elt){
elt.draw();
elt.enddraw();
});
self.inDOM();
}
(...)