I have a pong game that I made 6 months ago when I was much worse at programming. The only thing is it flickers like CRAZY, which ruins it for me.
Here is some code which is involved in the drawing and clearing of the canvas, so may be behind the flickering:
canvas: document.getElementById( "canvas" ),
// Get our 2D context for drawing
ctx: canvas.getContext( "2d" ),
// Frames-per-second
FPS: 30,
draw: function() {
if (preGameContent.isStartScreen === false && endOfGame.isEndOfGame === false) {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.Banana1.draw();
this.Banana2.draw();
this.Banana3.draw();
this.displayScore();
this.player1Paddle.draw();
this.player2Paddle.draw();
this.ball.draw();
}
}
update: function() {
if (preGameContent.isStartScreen === false && endOfGame.isEndOfGame === false) {
this.player1Paddle.updatePaddle();
this.player2Paddle.updatePaddle();
this.ball.updateBall();
if (this.ball.x < 0) {
if (this.is2PlayerGame === true) {
this.pointScored.startNextSet("player2");
this.setUp("right");
}
else {
this.pointScored.startNextSet("computer");
this.setUp("right");
}
}
if (this.ball.x + this.ball.width > this.canvas.width) {
gameController.pointScored.startNextSet("player1");
this.setUp("left");
}
}
}
tick: function() {
if (preGameContent.isStartScreen === false
&& endOfGame.isEndOfGame === false) {
gameController.draw();
gameController.update();
}
}
setInterval( gameController.tick, 1000 / gameController.FPS );
Do you see anything that can be done to this to reduce flicker? Thanks.
EDIT
Check out how i was redrawing each image every interval by creating a NEW image in its draw method:
//This class is to construct anything with an image
//vx and vy are for the velocity along the x and y axis
function Item(xStartPos, yStartPos, vx, vy, width, height, imgSrc) {
this.x = xStartPos;
this.y = yStartPos;
this.vx = vx;
this.vy = vy;
this.width = width;
this.height = height;
this.imgSrc = imgSrc;
//this function draws the image to the canvas
this.draw = function() {
var self = this;
var img = new Image();
img.src = self.imgSrc;
img.onload = function(){
gameController.ctx.drawImage(img, self.x, self.y);
};
};
//this function updates the position of the object on the canvas
this.update = function() {
// Divide velocity by gameController.FPS before adding it
// onto the position.
this.x += this.vx / gameController.FPS;
this.y += this.vy / gameController.FPS;
// wall collision detection
//stop the object from going through the top and bottom walls,
//but not the side walls, so the ball can go through them
if ( (this.y) < 0 ) {
this.y = 0;
}
if ( (this.y + this.height) > gameController.canvas.height) {
this.y = gameController.canvas.height - this.height;
}
};
};
EDIT
So i did this:
function Item(xStartPos, yStartPos, vx, vy, width, height, imgSrc) {
this.x = xStartPos;
this.y = yStartPos;
this.vx = vx;
this.vy = vy;
this.width = width;
this.height = height;
this.img = new Image();
this.imgSrc = imgSrc;
this.img.src = imgSrc;
//this.loaded = false;
//img.onload = function() { this.loaded = true; }
//this function draws the image to the canvas
this.draw = function() {
//if (this.loaded)
gameController.ctx.drawImage(this.img, this.x, this.y);
};
Which makes it draw all items nicely except the paddles and ball which do not draw at all
It is hard to say where exactly it flickers without looking at the whole solution, but I would blame setInterval(). The way to go is to use requestAnimationFrame technique described here: http://www.html5canvastutorials.com/advanced/html5-canvas-animation-stage/
I was able to achieve very smooth animations by using this code template:
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
function animate() {
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
// update
// clear
context.clearRect(0, 0, canvas.width, canvas.height);
// draw stuff
// request new frame
requestAnimFrame(function() {
animate();
});
}
animate();
In general, setInterval() is fairly bad, use setTimeout() after each re-draw, since interval might come at the worst timing possible.
Note, depending on required browser versions support, you might want to drop setTimeout alltogether, as requestAnimationFrame is universally supported by now.
Update
A simple fix for the image loading without using extra tools within the code would be this, although it might run some animation cycles until all images will be loaded:
function Item(.. imgSrc) {
...
this.img = new Image();
this.imgSrc = imgSrc;
var self = this;
this.loaded = false;
img.onload = function() { self.loaded = true; }
//this function draws the image to the canvas
this.draw = function() {
if (this.loaded)
gameController.ctx.drawImage(this.img, this.x, this.y);
};
Related
I am trying to make a game, where I draw images to a canvas. Here is a small code snippet:
class Sprite {
constructor(x, y, width, height, src, xDirection, yDirection) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.src = src;
this.xDirection = xDirection;
this.yDirection = yDirection;
}
update() {
var image = new Image();
image.src = this.src;
context.imageSmoothingEnabled = false;
context.drawImage(image, this.x, this.y, this.width, this.height);
this.x += this.xDirection;
this.y += this.yDirection;
}
}
const player = new Sprite(0, 0, 100, 100, "Images/Game/sprite-right.png", 0, 0);
const mainInterval = setInterval(() => {
player.draw();
}, 1);
This is only a small code snippet and I am also drawing many more images to the screen in the main interval. In Google Chrome and in Firefox, this works perfectly, as all of the images are correctly drawn to the screen. However, in Safari, the image is not drawn. I tried changing the 'update' method to this...
update() {
var image = new Image();
image.src = this.src;
image.onload = () => {
context.imageSmoothingEnabled = false;
context.drawImage(image, this.x, this.y, this.width, this.height);
this.x += this.xDirection;
this.y += this.yDirection;
};
}
...this works, but since I am drawing lots of things to the screen in the main interval, all of the images appear to sort of 'flash' and flicker. I need help with coming up with a way of drawing lots of images to the screen, without them flickering (bearing in mind that the method at the top only doesn't work in Safari, as far as I know). Thanks very much.
PrimeCubed
Not exactly sure why it isn't working on Safari but here are a few things that can be improved:
It's better to load all the images before you actually start drawing them.
You should use requestAnimationFrame for drawing frames.
Call the draw function only when all the resources have loaded. window.onload = draw;
Here is a snippet with these changes and this works on Safari too.
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
var image = new Image();
image.src = "https://placekitten.com/100/100";
class Sprite {
constructor(x, y, width, height, image, xDirection, yDirection) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.image = image;
this.xDirection = xDirection;
this.yDirection = yDirection;
}
update() {
context.imageSmoothingEnabled = false;
context.drawImage(this.image, this.x, this.y, this.width, this.height);
this.x += this.xDirection;
this.y += this.yDirection;
}
}
const player = new Sprite(0, 0, 100, 100, image, 0, 0);
function draw() {
player.update();
requestAnimationFrame(draw);
}
window.onload = draw;
<canvas />
I am new to Canvas and want to loop background Image in the below smoke effect. On searching, I have found an example that how we can loop background Image in canvas Link to looping animation so I tried integrating the looping code with the smoke effect but no success. Any help will be appreciated.
// Create an array to store our particles
var particles = [];
// The amount of particles to render
var particleCount = 60;
// The maximum velocity in each direction
var maxVelocity = 2;
// The target frames per second (how often do we want to update / redraw the scene)
var targetFPS = 33;
// Set the dimensions of the canvas as variables so they can be used.
var canvasWidth = window.innerWidth;
var canvasHeight = window.innerHeight;
// borders for particles on top and bottom
var borderTop = 0.01 * canvasHeight;
var borderBottom = 0.99 * canvasHeight;
// Create an image object (only need one instance)
var imageObj = new Image();
var looping = false;
var totalSeconds = 0;
// Once the image has been downloaded then set the image on all of the particles
imageObj.onload = function() {
particles.forEach(function(particle) {
particle.setImage(imageObj);
});
};
// Once the callback is arranged then set the source of the image
imageObj.src = "https://image.ibb.co/fdpeJF/Smoke.png";
// A function to create a particle object.
function Particle(context) {
// Set the initial x and y positions
this.x = 0;
this.y = 0;
// Set the initial velocity
this.xVelocity = 0;
this.yVelocity = 0;
// Set the radius
this.radius = 5;
// Store the context which will be used to draw the particle
this.context = context;
// The function to draw the particle on the canvas.
this.draw = function() {
// If an image is set draw it
if (this.image) {
this.context.drawImage(this.image, this.x - 128, this.y - 128);
// If the image is being rendered do not draw the circle so break out of the draw function
return;
}
// Draw the circle as before, with the addition of using the position and the radius from this object.
this.context.beginPath();
this.context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
this.context.fillStyle = "rgba(0, 255, 255, 1)";
this.context.fill();
this.context.closePath();
};
// Update the particle.
this.update = function() {
// Update the position of the particle with the addition of the velocity.
this.x += this.xVelocity;
this.y += this.yVelocity;
// Check if has crossed the right edge
if (this.x >= canvasWidth) {
this.xVelocity = -this.xVelocity;
this.x = canvasWidth;
}
// Check if has crossed the left edge
else if (this.x <= 0) {
this.xVelocity = -this.xVelocity;
this.x = 0;
}
// Check if has crossed the bottom edge
if (this.y >= borderBottom) {
this.yVelocity = -this.yVelocity;
this.y = borderBottom;
}
// Check if has crossed the top edge
else if (this.y <= borderTop) {
this.yVelocity = -this.yVelocity;
this.y = borderTop;
}
};
// A function to set the position of the particle.
this.setPosition = function(x, y) {
this.x = x;
this.y = y;
};
// Function to set the velocity.
this.setVelocity = function(x, y) {
this.xVelocity = x;
this.yVelocity = y;
};
this.setImage = function(image) {
this.image = image;
};
}
// A function to generate a random number between 2 values
function generateRandom(min, max) {
return Math.random() * (max - min) + min;
}
// The canvas context if it is defined.
var context;
// Initialise the scene and set the context if possible
function init() {
var canvas = document.getElementById('myCanvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
if (canvas.getContext) {
// Set the context variable so it can be re-used
context = canvas.getContext('2d');
// Create the particles and set their initial positions and velocities
for (var i = 0; i < particleCount; ++i) {
var particle = new Particle(context);
// Set the position to be inside the canvas bounds
particle.setPosition(generateRandom(0, canvasWidth), generateRandom(borderTop, borderBottom));
// Set the initial velocity to be either random and either negative or positive
particle.setVelocity(generateRandom(-maxVelocity, maxVelocity), generateRandom(-maxVelocity, maxVelocity));
particles.push(particle);
context.clearRect(0, 0, canvas.width, canvas.height);
}
} else {
alert("Please use a modern browser");
}
}
// The function to draw the scene
function draw() {
// background image
context.globalAlpha = 1;
context.globalCompositeOperation = 'source-over';
context.drawImage(backImg, 0, 0, canvasWidth, canvasHeight);
context.fillStyle = "rgba(255,255,255, .5)";
context.fillRect(0, 0, canvasWidth, canvasHeight);
context.globalAlpha = 0.75;
context.globalCompositeOperation = 'soft-lights';
// Fog layer
// Go through all of the particles and draw them.
particles.forEach(function(particle) {
particle.draw();
});
}
// Update the scene
function update() {
particles.forEach(function(particle) {
particle.update();
});
}
// Initialize the scene
init();
backImg = new Image();
backImg.src = 'https://image.ibb.co/cTOOdF/e2VZQY.jpg';
// If the context is set then we can draw the scene (if not then the browser does not support canvas)
if (context) {
setInterval(function() {
// Update the scene befoe drawing
update();
// Draw the scene
draw();
}, 1000 / targetFPS);
}
<canvas id="myCanvas" ></canvas>
I just added a few lines. Hopefully you can spot them. I commented everything I added.
// Create an array to store our particles
var particles = [];
// The amount of particles to render
var particleCount = 60;
// The maximum velocity in each direction
var maxVelocity = 2;
// The target frames per second (how often do we want to update / redraw the scene)
var targetFPS = 33;
// Set the dimensions of the canvas as variables so they can be used.
var canvasWidth = window.innerWidth;
var canvasHeight = window.innerHeight;
// borders for particles on top and bottom
var borderTop = 0.01 * canvasHeight;
var borderBottom = 0.99 * canvasHeight;
// Create an image object (only need one instance)
var imageObj = new Image();
// x position of scrolling image
var imageX = 0;
var looping = false;
var totalSeconds = 0;
// Once the image has been downloaded then set the image on all of the particles
imageObj.onload = function() {
particles.forEach(function(particle) {
particle.setImage(imageObj);
});
};
// Once the callback is arranged then set the source of the image
imageObj.src = "https://image.ibb.co/fdpeJF/Smoke.png";
// A function to create a particle object.
function Particle(context) {
// Set the initial x and y positions
this.x = 0;
this.y = 0;
// Set the initial velocity
this.xVelocity = 0;
this.yVelocity = 0;
// Set the radius
this.radius = 5;
// Store the context which will be used to draw the particle
this.context = context;
// The function to draw the particle on the canvas.
this.draw = function() {
// If an image is set draw it
if (this.image) {
this.context.drawImage(this.image, this.x - 128, this.y - 128);
// If the image is being rendered do not draw the circle so break out of the draw function
return;
}
// Draw the circle as before, with the addition of using the position and the radius from this object.
this.context.beginPath();
this.context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
this.context.fillStyle = "rgba(0, 255, 255, 1)";
this.context.fill();
this.context.closePath();
};
// Update the particle.
this.update = function() {
// Update the position of the particle with the addition of the velocity.
this.x += this.xVelocity;
this.y += this.yVelocity;
// Check if has crossed the right edge
if (this.x >= canvasWidth) {
this.xVelocity = -this.xVelocity;
this.x = canvasWidth;
}
// Check if has crossed the left edge
else if (this.x <= 0) {
this.xVelocity = -this.xVelocity;
this.x = 0;
}
// Check if has crossed the bottom edge
if (this.y >= borderBottom) {
this.yVelocity = -this.yVelocity;
this.y = borderBottom;
}
// Check if has crossed the top edge
else if (this.y <= borderTop) {
this.yVelocity = -this.yVelocity;
this.y = borderTop;
}
};
// A function to set the position of the particle.
this.setPosition = function(x, y) {
this.x = x;
this.y = y;
};
// Function to set the velocity.
this.setVelocity = function(x, y) {
this.xVelocity = x;
this.yVelocity = y;
};
this.setImage = function(image) {
this.image = image;
};
}
// A function to generate a random number between 2 values
function generateRandom(min, max) {
return Math.random() * (max - min) + min;
}
// The canvas context if it is defined.
var context;
// Initialise the scene and set the context if possible
function init() {
var canvas = document.getElementById('myCanvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
if (canvas.getContext) {
// Set the context variable so it can be re-used
context = canvas.getContext('2d');
// Create the particles and set their initial positions and velocities
for (var i = 0; i < particleCount; ++i) {
var particle = new Particle(context);
// Set the position to be inside the canvas bounds
particle.setPosition(generateRandom(0, canvasWidth), generateRandom(borderTop, borderBottom));
// Set the initial velocity to be either random and either negative or positive
particle.setVelocity(generateRandom(-maxVelocity, maxVelocity), generateRandom(-maxVelocity, maxVelocity));
particles.push(particle);
context.clearRect(0, 0, canvas.width, canvas.height);
}
} else {
alert("Please use a modern browser");
}
}
// The function to draw the scene
function draw() {
// background image
context.globalAlpha = 1;
context.globalCompositeOperation = 'source-over';
// draw twice to cover wrap around
context.drawImage(backImg, imageX, 0, canvasWidth, canvasHeight);
context.drawImage(backImg, imageX + canvasWidth, 0, canvasWidth, canvasHeight);
context.fillStyle = "rgba(255,255,255, .5)";
context.fillRect(0, 0, canvasWidth, canvasHeight);
context.globalAlpha = 0.75;
context.globalCompositeOperation = 'soft-light';
// Fog layer
// Go through all of the particles and draw them.
particles.forEach(function(particle) {
particle.draw();
});
}
// Update the scene
function update() {
// incrementally change image position of background to scroll left
imageX -= maxVelocity;
if (imageX < -canvasWidth) {
imageX += canvasWidth;
}
particles.forEach(function(particle) {
particle.update();
});
}
// Initialize the scene
init();
backImg = new Image();
backImg.src = 'https://image.ibb.co/cTOOdF/e2VZQY.jpg';
// If the context is set then we can draw the scene (if not then the browser does not support canvas)
if (context) {
setInterval(function() {
// Update the scene befoe drawing
update();
// Draw the scene
draw();
}, 1000 / targetFPS);
}
<canvas id="myCanvas"></canvas>
Just to add to the answer given some additional improvements to the code structure.
Use requestAnimationFrame to call render calls.
Don't expose properties of objects if not needed.
Don't use forEach iteration in time critical code. Use for loops.
Use constants where ever possible.
Comments that state the obvious are just noise in the source code making it harder to read. Limit comment to abstracts that may not be obvious to another programmer reading the code.
eg
// If an image is set draw it
if (this.image) {
Really is that comment of any use to anyone. Comments should help not degrade the readability of code.
Also the original code tried to set the global composite operations to soft-lights this is not a know operation. I corrected it to soft-light which can on some machines, be a very slow render operation. It may pay to selected another operation for machines that are slow. This can be done by simply monitoring the render time of particles and switching operation type is too slow.
A quick rewrite of the OP's code.
const particles = [];
const particleCount = 60;
const maxVelocity = 2;
var canvasWidth = innerWidth;
var canvasHeight = innerHeight;
var borderTop = 0.01 * canvasHeight;
var borderBottom = 0.99 * canvasHeight;
var ctx;
const backgroundColor = "rgba(255,255,255, .5)";
const backgroundSpeed = -0.1;
var looping = false;
var totalSeconds = 0;
var lastTime = 0;
var frameTime = (1000 / 30) - (1000 / 120); // one quater frame short to
// allow for timing error
var imageCount = 0;
const backImg = new Image();
const imageObj = new Image();
backImg.src = 'https://image.ibb.co/cTOOdF/e2VZQY.jpg';
imageObj.src = "https://image.ibb.co/fdpeJF/Smoke.png";
backImg.onload = imageObj.onload = imageLoad;
function imageLoad(){
imageCount += 1;
if(imageCount === 2){
init();
}
}
function init() {
var canvas = myCanvas;
canvas.width = innerWidth;
canvas.height = innerHeight;
ctx = canvas.getContext('2d');
for (var i = 0; i < particleCount; i += 1) {
particles.push(new Particle(ctx));
}
lastTime = performance.now();
requestAnimationFrame(mainLoop);
}
function mainLoop(time){
if(time-lastTime > frameTime){
lastTime = time;
update();
draw(time);
}
requestAnimationFrame(mainLoop);
}
const rand = (min, max) => Math.random() * (max - min) + min; // names are best short (short only without ambiguity)
function Particle(ctx) {
var x, y, xVel, yVel, radius, image;
const color = "rgba(0, 255, 255, 1)";
x = rand(0, canvasWidth),
y = rand(borderTop, borderBottom);
xVel = rand(-maxVelocity, maxVelocity);
yVel = rand(-maxVelocity, maxVelocity);
radius = 5;
image = imageObj;
this.draw = function () { ctx.drawImage(image, x - 128, y - 128) }
this.update = function () {
x += xVel;
y += yVel;
if (x >= canvasWidth) {
xVel = -xVel;
x = canvasWidth;
}
else if (x <= 0) {
xVel = -xVel;
x = 0;
}
if (y >= borderBottom) {
yVel = -yVel;
y = borderBottom;
}
else if (y <= borderTop) {
yVel = -yVel;
y = borderTop;
}
}
}
function draw(time) {
var i,x;
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = 'source-over';
x = time * backgroundSpeed;
x = ((x % canvasWidth) + canvasWidth) % canvasWidth;
ctx.drawImage(backImg, x, 0, canvasWidth, canvasHeight);
ctx.drawImage(backImg, x - canvasWidth, 0, canvasWidth, canvasHeight);
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
ctx.globalAlpha = 0.75;
ctx.globalCompositeOperation = 'soft-light';
for(i = 0; i < particles.length; i += 1){
particles[i].draw();
}
}
function update() {
for(i = 0; i < particles.length; i += 1){
particles[i].update();
}
}
canvas {
position : absolute;
top : 0px;
left : 0px;
}
<canvas id=myCanvas></canvas>
I'm trying to refine my javascript skills by working on developing more object oriented javascript code. While I'm fluent in Java OOP syntax and concepts, I'm lost when it comes to javascript.
While the ball initializes fine, I can't figure out how to get the thing to move! If anyone could help, it would be greatly appreciated.
The ball is, at least currently, supposed to just bounce off the "walls" of the canvas.
Here's the code:
var canvas = document.getElementById("myCanvas");
var ctx=canvas.getContext("2d");
var x=canvas.width/2;//x ball position
var y = canvas.height-30;//y ball posi
var dx=2;
var dy=-2;
var ballRadius = 10;
function Ball(){
}
//draws gameball
Ball.prototype.drawBall=function(){
ctx.beginPath();
ctx.arc(x,y,ballRadius,0,Math.PI*2);
this.checkWalls();
fillStyle="#0095DD";
ctx.fill();
ctx.closePath();
}
//dynamically draw balls position
Ball.prototype.draw=function(){
ctx.clearRect(0, 0, canvas.width, canvas.height);
this.drawBall();
x+=dx;
y+=dy;
this.checkWalls(x,y,dx,dy);
}
Ball.prototype.refresh=function(){
setInterval(this.draw(),8);
}
Ball.prototype.checkWalls=function(x,y,dx,dy){
//reverse direction if ball hits top or bottom
if(this.y+this.dy> canvas.height-ballRadius || this.y + this.dy <ballRadius){
this.dy=-dy;
}
//reverse direction if ball hits left or right
if(this.x+this.dx>canvas.width-ballRadius || this.x+this.dx<ballRadius){
this.dx=-dx;
}
}
//instantiate objects
var ball = new Ball();
ball.refresh();
Here's the UPDATED: JSFIDDLE
When you do like that setInterval(this.draw(), 8) your this.draw() executed and return undefined but it has to return function which setInterval must be invoke.
Simple way:
Ball.prototype.refresh=function(){
var self = this;
setInterval(function () {
self.draw();
},8);
}
It is important that this is point to itself object so you have to use set local variable self
Fast way:
Ball.prototype.refresh=function(){
setInterval(this.draw.bind(this),8);
}
bind method link this.draw to this context and return function like in the simple example.
Updated fiddle : https://jsfiddle.net/reko91/d6mce9yq/2/
Been working on canvas recently and this is what I have used.
// game loop function
var loop = function () {
//update();
//draw();
ball.refresh();
window.requestAnimationFrame(loop, canvas);
};
window.requestAnimationFrame(loop, canvas);
Also add this at the top to make it run 60fps :
window.requestAnimFrame = function() {
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( /* function */ callback) {
window.setTimeout(callback, 1000 / 60);
}
);
}();
Here is your working walls fiddle : https://jsfiddle.net/reko91/d6mce9yq/7/
I have changed quite a few things.
Added x,y,dx,dy to the actual ball:
function Ball() {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
}
Then used these values later on (all in fiddle). Your 'checkwalls' function now looks like :
Ball.prototype.checkWalls = function(x, y, dx, dy, thisBall) {
//console.log(thisBall.dx)
//reverse direction if ball hits top or bottom
if (this.y > canvas.height - ballRadius || this.y < ballRadius) {
//this.y-=dy;
this.dy *= -1;
}
//reverse direction if ball hits left or right
if (this.x > canvas.width - ballRadius || this.x < ballRadius) {
//this.xdx;
this.dx *= -1;
}
}
Hope that helps.
I'm using this code: http://jsfiddle.net/jonnyc/Ujz4P/5/
Here is a customized fiddle http://jsfiddle.net/6fwFY/4/ where I'm using the canvas positioned absolutely on the text with only 3 particles, and it shows the problem: moving images leaving traces behind them.
Is there a way to fix this so moving images wouldn't let those traces so it would look better?
// Create an array to store our particles
var particles = [];
// The amount of particles to render
var particleCount = 30;
// The maximum velocity in each direction
var maxVelocity = 2;
// The target frames per second (how often do we want to update / redraw the scene)
var targetFPS = 33;
// Set the dimensions of the canvas as variables so they can be used.
var canvasWidth = 400;
var canvasHeight = 400;
// Create an image object (only need one instance)
var imageObj = new Image();
// Once the image has been downloaded then set the image on all of the particles
imageObj.onload = function() {
particles.forEach(function(particle) {
particle.setImage(imageObj);
});
};
// Once the callback is arranged then set the source of the image
imageObj.src = "http://www.blog.jonnycornwell.com/wp-content/uploads/2012/07/Smoke10.png";
// A function to create a particle object.
function Particle(context) {
// Set the initial x and y positions
this.x = 0;
this.y = 0;
// Set the initial velocity
this.xVelocity = 0;
this.yVelocity = 0;
// Set the radius
this.radius = 5;
// Store the context which will be used to draw the particle
this.context = context;
// The function to draw the particle on the canvas.
this.draw = function() {
// If an image is set draw it
if(this.image){
this.context.drawImage(this.image, this.x-128, this.y-128);
// If the image is being rendered do not draw the circle so break out of the draw function
return;
}
// Draw the circle as before, with the addition of using the position and the radius from this object.
this.context.beginPath();
this.context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
this.context.fillStyle = "rgba(0, 255, 255, 1)";
this.context.fill();
this.context.closePath();
};
// Update the particle.
this.update = function() {
// Update the position of the particle with the addition of the velocity.
this.x += this.xVelocity;
this.y += this.yVelocity;
// Check if has crossed the right edge
if (this.x >= canvasWidth) {
this.xVelocity = -this.xVelocity;
this.x = canvasWidth;
}
// Check if has crossed the left edge
else if (this.x <= 0) {
this.xVelocity = -this.xVelocity;
this.x = 0;
}
// Check if has crossed the bottom edge
if (this.y >= canvasHeight) {
this.yVelocity = -this.yVelocity;
this.y = canvasHeight;
}
// Check if has crossed the top edge
else if (this.y <= 0) {
this.yVelocity = -this.yVelocity;
this.y = 0;
}
};
// A function to set the position of the particle.
this.setPosition = function(x, y) {
this.x = x;
this.y = y;
};
// Function to set the velocity.
this.setVelocity = function(x, y) {
this.xVelocity = x;
this.yVelocity = y;
};
this.setImage = function(image){
this.image = image;
}
}
// A function to generate a random number between 2 values
function generateRandom(min, max){
return Math.random() * (max - min) + min;
}
// The canvas context if it is defined.
var context;
// Initialise the scene and set the context if possible
function init() {
var canvas = document.getElementById('myCanvas');
if (canvas.getContext) {
// Set the context variable so it can be re-used
context = canvas.getContext('2d');
// Create the particles and set their initial positions and velocities
for(var i=0; i < particleCount; ++i){
var particle = new Particle(context);
// Set the position to be inside the canvas bounds
particle.setPosition(generateRandom(0, canvasWidth), generateRandom(0, canvasHeight));
// Set the initial velocity to be either random and either negative or positive
particle.setVelocity(generateRandom(-maxVelocity, maxVelocity), generateRandom(-maxVelocity, maxVelocity));
particles.push(particle);
}
}
else {
alert("Please use a modern browser");
}
}
// The function to draw the scene
function draw() {
// Clear the drawing surface and fill it with a black background
context.fillStyle = "rgba(0, 0, 0, 0.5)";
context.fillRect(0, 0, 400, 400);
// Go through all of the particles and draw them.
particles.forEach(function(particle) {
particle.draw();
});
}
// Update the scene
function update() {
particles.forEach(function(particle) {
particle.update();
});
}
// Initialize the scene
init();
// If the context is set then we can draw the scene (if not then the browser does not support canvas)
if (context) {
setInterval(function() {
// Update the scene befoe drawing
update();
// Draw the scene
draw();
}, 1000 / targetFPS);
}
You should clear canvas between each new draw, see if that fits your needs:
DEMO jsFiddle
context.clearRect(0, 0, canvasWidth, canvasHeight);
I know that there are many similiar questions already asked on stackoverflow, but still, I can not figure out how to do it. I want to rotate only ball texture. draw is called with timer:
var canvas;
var ctx;
var width;
var height;
var ready;
var textures;
var loadIndex;
var loadCount;
var keyCodes;
var mouseLoc;
var playerX;
var playerY;
var playerVelocity;
function init() {
canvas = document.getElementById('game');
ctx = canvas.getContext("2d");
width = canvas.width;
height = canvas.height;
textures = [];
loadingCount = 0;
keyCodes = [];
mouseLoc = {};
playerX = 0;
playerY = 0;
playerVelocity = 6;
textures['Background'] = loadTexture('./textures/Background.png');
textures['Ball'] = loadTexture('./textures/Ball.png');
setInterval(function(){
if(loadingCount == 0) {
update();
draw();
}
}, 50);
}
function update(){
if(keyCodes[37])
playerX -= playerVelocity;
if(keyCodes[38])
playerY -= playerVelocity;
if(keyCodes[39])
playerX += playerVelocity;
if(keyCodes[40])
playerY += playerVelocity;
}
function draw() {
//ctx.clearRect(0, 0, width, height);
//ctx.beginPath();
drawBackground();
drawPlayer();
//ctx.closePath();
//ctx.fill();
}
function drawBackground(){
ctx.drawImage(textures['Background'], 0, 0, width, height);
}
function drawPlayer(){
ctx.save();
ctx.rotate(0.17);
ctx.drawImage(textures['Ball'], playerX, playerY, 100, 100);
ctx.restore();
}
function loadTexture(src){
var image = new Image();
image.src = src;
loadingCount++;
image.onload = function(){
loadingCount--;
};
return image;
}
document.onkeydown = function(evt){
keyCodes[evt.keyCode] = true;
evt.returnValue = false;
}
document.onkeyup = function(evt){
keyCodes[evt.keyCode] = false;
}
document.onmousemove = function(evt){
mouseLoc.x = evt.layerX;
mouseLoc.y = evt.layerY;
}
document.onmousedown = function(evt){
mouseLoc.down = true;
}
document.onmouseup = function(evt){
mouseLoc.down = false;
}
init();
Assuming that you want to give the illusion of the ball continuing to rotate, you should increase the rotation angle for each frame drawn.
As written, your code will give the ball a fixed rotation of 0.17 radians on each frame.
var frame = 0;
function drawPlayer() {
ctx.save();
ctx.rotate(0.17 * frame);
...
ctx.restore();
}
function draw() {
++frame;
drawPlayer();
...
}
You just need to save and restore the state of the canvas('s state-machine)
function drawPlayer(){
ctx.save();
ctx.rotate(0.17);
ctx.drawImage(textures['Ball'], playerX, playerY, 100, 100);
ctx.restore();
}
You want to use delta time for calculating the distance of your rotation. That is, the time that has passed since the last frame. That will make your rotation smoother should your browser hiccup and lose a frame here or there.
So store the time of each frame so you can make the comparison between frames and set your rotation speed as radians per second.