I am trying to move a image on <canvas> to jump.
index.html
<!DOCTYPE html>
<html>
<head>
<title>Ball Jump</title>
</head>
<body>
<img src="./assets/ball.png" height="0px" width="0px" id="ball">
<br>
<br>
<canvas height="210px" width="350px" id="paper" onclick="play()"></canvas>
<script type="text/javascript" src="./main.js"></script
</body>
</html>
main.js
'use strict'
var paper = document.getElementById('paper');
var ball = document.getElementById('ball');
var brush = paper.getContext('2d');
brush.drawImage(dino, 0, 150, 70, 50);
var y = 150;
function play() {
up();
function up() {
if (y > 50) {
brush.clearRect(0, y-1, 70, y+50);
brush.drawImage(ball, 0, y, 70, 50);
y--;
}
else { clearInterval(jump); }
}
var jump = setInterval(up, 10);
function down() {
if (y < 150) {
brush.clearRect(0, y-1, 70, y+50);
brush.drawImage(ball, 0, y, 70, 50);
y++;
}
else { clearInterval(fall); }
}
var fall = setInterval(down, 10);
}
But the problem is that the ball is lagging and taking more time to go up, however it is coming down fine even if both functions are set to 10ms.
Please help!!!
I think you should use requestAnimationFrame instead of setInterval
Example
const canvas = document.querySelector('#canvas');
const context = canvas.getContext('2d');
const image = new Image();
image.src = "https://picsum.photos/seed/picsum/50/50";
image.addEventListener("load", load);
function load(event) {
loop();
}
let x = 0;
let y = 0;
let t = 0;
let r = 50;
const inc = Math.PI/90;
function loop() {
const width = canvas.width;
const height = canvas.height;
context.clearRect(0, 0, width, height);
context.drawImage(image, x, y, image.width, image.height);
x = 100 + r * Math.cos(t);
y = 100 + r * Math.sin(t);
t = t + inc;
requestAnimationFrame(loop);
}
<canvas height="400px" width="400px" id="canvas"></canvas>
I would not use multiple setInterval just one should do...
Here is an example:
var paper = document.getElementById('paper');
var brush = paper.getContext('2d');
var y = 100;
var speed = 1;
function draw() {
if ((y < 10) || (y > 150)) {
speed *= -1
}
y += speed
brush.clearRect(0, 0, 160, 300);
brush.fillRect(10, y, 10, 10);
}
function play() {
setInterval(draw, 10);
}
draw()
<canvas height="160" width="300" id="paper" onclick="play()"></canvas>
In your code, both intervals are running simultaneously that could create the strange behavior you are seeing where the object is lagging and taking more time to go up but coming down is fine.
Instead of the two setInterval I introduced the variable speed that will determine the direction of the movement and we will "flip" it speed *= -1 when the position exceeds our boundaries
... with this pattern in your canvas animation it's easy to transition to requestAnimationFrame if performance becomes critical, but for simple practice code like this, setInterval works fine.
For animations running smoothly on the browser, you should really use a requestAnimationFrame loop instead of setInterval
checkout the example here:
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations
here is why:
Why is requestAnimationFrame better than setInterval or setTimeout.
Related
I want to create a break out game through javascript. I am wondering why the ctx.clearRect does not working. I want to put the rectangle in the y coordinate 430 to make it show at the bottom of the canvas. It moves when I have used the window.setInterval. But the rectangle move continuously.
Any help would be appreciated. Sorry for my poor English.
var canvas = document.getElementById("canvas");
const ctx = canvas.getContext('2d');
var position = 0;
var yposition = 430;
var length = 80;
var width = 20;
var xSpeed = length*1;
var ySpeed = 0;
function R(){
ctx.fillStyle = "green";
ctx.fillRect(position, yposition, length, width);
};
function C(){
position += xSpeed;
yposition += ySpeed;
};
window.setInterval(() => {
ctx.clearRect(0, 430, length, width);
R();
C();
},150);
ctx.beginPath();
ctx.arc(150, 50, 20, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fillStyle = "blue";
ctx.fill();
The culprit are the parameters you're feeding into the clearRect function:
(0, 430, length, width)
Since length and width are hardcoded values of 80 and 20 respectively, the above means every time the intervals callback function gets fired it clears a rectangular area of 80 x 20 pixels at x = 0 and y = 430.
As your green paddle is moving you're actually clearing an area your paddle isn't located at anymore.
So you basically have two options:
Clear the whole canvas every frame
Clear the screen area your paddle has been before changing it's position
The second would look a little something like this:
var canvas = document.getElementById("canvas");
const ctx = canvas.getContext('2d');
var position = 0;
var yposition = 150;
var length = 80;
var width = 20;
var xSpeed = length * 1;
var ySpeed = 0;
function R() {
ctx.fillStyle = "green";
ctx.fillRect(position, yposition, length, width);
}
function C() {
position += xSpeed;
yposition += ySpeed;
}
window.setInterval(() => {
ctx.clearRect(position, yposition, length, width);
C();
R();
}, 500);
<canvas id="canvas" width="600" height="400"></canvas>
I'd definitely recommend clearing the whole canvas though since there will be other on-screen objects beside the paddle.
I am trying to move an image from the right to the center and I am not sure if this is the best way.
var imgTag = null;
var x = 0;
var y = 0;
var id;
function doCanvas()
{
var canvas = document.getElementById('icanvas');
var ctx = canvas.getContext("2d");
var imgBkg = document.getElementById('imgBkg');
imgTag = document.getElementById('imgTag');
ctx.drawImage(imgBkg, 0, 0);
x = canvas.width;
y = 40;
id = setInterval(moveImg, 0.25);
}
function moveImg()
{
if(x <= 250)
clearInterval(id);
var canvas = document.getElementById('icanvas');
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
var imgBkg = document.getElementById('imgBkg');
ctx.drawImage(imgBkg, 0, 0);
ctx.drawImage(imgTag, x, y);
x = x - 1;
}
Any advice?
This question is 5 years old, but since we now have requestAnimationFrame() method, here's an approach for that using vanilla JavaScript:
var imgTag = new Image(),
canvas = document.getElementById('icanvas'),
ctx = canvas.getContext("2d"),
x = canvas.width,
y = 0;
imgTag.onload = animate;
imgTag.src = "http://i.stack.imgur.com/Rk0DW.png"; // load image
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // clear canvas
ctx.drawImage(imgTag, x, y); // draw image at current position
x -= 4;
if (x > 250) requestAnimationFrame(animate) // loop
}
<canvas id="icanvas" width=640 height=180></canvas>
drawImage() enables to define which part of the source image to draw on target canvas. I would suggest for each moveImg() calculate the previous image position, overwrite the previous image with that part of imgBkg, then draw the new image. Supposedly this will save some computing power.
Here's my answer.
var canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
var myImg = new Image();
var myImgPos = {
x: 250,
y: 125,
width: 50,
height: 25
}
function draw() {
myImg.onload = function() {
ctx.drawImage(myImg, myImgPos.x, myImgPos.y, myImgPos.width, myImgPos.height);
}
myImg.src = "https://mario.wiki.gallery/images/thumb/c/cc/NSMBUD_Mariojump.png/1200px-NSMBUD_Mariojump.png";
}
function moveMyImg() {
ctx.clearRect(myImgPos.x, myImgPos.y, myImgPos.x + myImgPos.width, myImgPos.y +
myImgPos.height);
myImgPos.x -= 5;
}
setInterval(draw, 50);
setInterval(moveMyImg, 50);
<canvas id="canvas" class="canvas" width="250" height="150"></canvas>
For lag free animations,i generally use kinetic.js.
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var hexagon = new Kinetic.RegularPolygon({
x: stage.width()/2,
y: stage.height()/2,
sides: 6,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4
});
layer.add(hexagon);
stage.add(layer);
var amplitude = 150;
var period = 2000;
// in ms
var centerX = stage.width()/2;
var anim = new Kinetic.Animation(function(frame) {
hexagon.setX(amplitude * Math.sin(frame.time * 2 * Math.PI / period) + centerX);
}, layer);
anim.start();
Here's the example,if you wanna take a look.
http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-animate-position-tutorial/
Why i suggest this is because,setInterval or setTimeout a particular function causes issues when large amount of simultaneous animations take place,but kinetic.Animation deals with framerates more intelligently.
Explaining window.requestAnimationFrame() with an example
In the following snippet I'm using an image for the piece that is going to be animated.
I'll be honest... window.requestAnimationFrame() wasn't easy for me to understand, that is why I coded it as clear and intuitive as possible. So that you may struggle less than I did to get my head around it.
const
canvas = document.getElementById('root'),
btn = document.getElementById('btn'),
ctx = canvas.getContext('2d'),
brickImage = new Image(),
piece = {image: brickImage, x:400, y:70, width:70};
brickImage.src = "https://i.stack.imgur.com/YreH6.png";
// When btn is clicked execute start()
btn.addEventListener('click', start)
function start(){
btn.value = 'animation started'
// Start gameLoop()
brickImage.onload = window.requestAnimationFrame(gameLoop)
}
function gameLoop(){
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height)
// Draw at coordinates x and y
ctx.drawImage(piece.image, piece.x, piece.y)
let pieceLeftSidePos = piece.x;
let middlePos = canvas.width/2 - piece.width/2;
// Brick stops when it gets to the middle of the canvas
if(pieceLeftSidePos > middlePos) piece.x -= 2;
window.requestAnimationFrame(gameLoop) // Needed to keep looping
}
<input id="btn" type="button" value="start" />
<p>
<canvas id="root" width="400" style="border:1px solid grey">
A key point
Inside the start() function we have:
brickImage.onload = window.requestAnimationFrame(gameLoop);
This could also be written like: window.requestAnimationFrame(gameLoop);
and it would probably work, but I'm adding the brickImage.onload to make sure that the image has loaded first. If not it could cause some issues.
Note: window.requestAnimationFrame() usually loops at 60 times per second.
So I have this rectangle that animates across to the right. How can I get the rectangle to reverse it when it hits the boundaries. I'm trying to make it go back and forth.
<!DOCTYPE html>
<html>
<head>
<script type='text/javascript'>
window.onload=function(){
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var x = 0;
var y = 50;
var width = 10;
var height = 10;
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillRect(x, y, width, height);
x++;
if(x <= 490) {
setTimeout(animate, 33);
}
}
animate();
}
</script>
</head>
<body>
<canvas id="canvas" width="500" height="400"
style="border: 1px solid #000000;"></canvas>
</body>
</html>
https://codepen.io/forTheLoveOfCode/pen/wqdpeg
Is that what you need? (link to codepen above).
var canvas = document.getElementById("canvas_id");
var context = canvas.getContext('2d');
var x=5;
var y=5;
var velocity = 10;
function move(){
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
x =x + velocity
if ((x+50)>canvas.width || x<0){
velocity *=-1;
}
draw()
}
function draw(){
context.fillStyle = "#E80C7A";
context.strokeStyle = "#000000";
context.lineWidth = '3';
context.fillRect(x, y, 50, 100);
context.strokeRect(x, y, 50, 100);
}
setInterval(move, 100);
<html>
<body>
<canvas id = "canvas_id">
</canvas>
</body>
</html>
here's a solution with boundaries detection
window.onload=function(){
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var x = 0;
var y = 50;
var width = 10;
var height = 10;
var speed = 10; // speed
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillRect(x, y, width, height);
if(
(x >= 500 - width && speed > 0) || // going to the right and bound reached
(x <= 0 && speed < 0) // going to the left and bound reached
) {
speed *= -1; // inverting the direction
}
x += speed;
setTimeout(animate, 33);
}
animate();
}
<canvas id="canvas" width="500" height="400"
style="border: 1px solid #000000;"></canvas>
consider using requestAnimationFrame instead of setTimeout to do this kind of work.
Right now my code works like I want it to but when I created my timer to redraw the triangle it overlayed a new triangle every time it was called, so to stop this I put clearRect in, but now it clears the entire canvas negating my fill of black that I have. How can I either add a new timer that still provides the same effect of moving triangles but that doesn't require clearRect or how can I fix what I have to have the background of the canvas stay black but not overlay new triangles every time? Any help is appreciated!
Edit: I also tried two different timers together instead of a timer for handleClick, but got weird results with the speed of the triangles, does anyone know why that is?:
timer = setInterval(init, 30);
timer = setInterval(Triangle, 30);
Code:
<html>
<head>
<script>
var canvas;
var context;
var triangles = [];
function init() {
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
resizeCanvas();
window.addEventListener('resize', resizeCanvas, false);
window.addEventListener('orientationchange', resizeCanvas, false);
canvas.onclick = function(event) {
handleClick(event.clientX, event.clientY);
};
timer = setInterval(handleClick, 30);
}
function Triangle(x,y,color) {
this.x = x;
this.y = y;
this.color = color;
this.vx = Math.random() * 10 - 5; //5-3
this.vy = Math.random() * 10 - 5;
for (var i=0; i < triangles.length; i++) {
var t = triangles[i];
t.x += t.vx;
t.y += t.vy;
if (t.x + t.vx > canvas.width || t.x + t.vx < 0)
t.vx = -t.vx;
if (t.y + t.vy > canvas.height || t.y + t.vy < 0)
t.vy = -t.vy;
}
}
function handleClick(x,y) {
context.clearRect(0,0, canvas.width,canvas.height);
var colors = ['red','green','purple','blue','yellow'];
var color = colors[Math.floor(Math.random()*colors.length)];
triangles.push(new Triangle(x, y, color));
for(i=0; i < triangles.length; i++) {
drawTriangle(triangles[i]);
}
}
function drawTriangle(triangle) {
context.beginPath();
context.moveTo(triangle.x,triangle.y);
context.lineTo(triangle.x + 50,triangle.y + 50);
context.lineTo(triangle.x + 50,triangle.y - 50);
context.fillStyle = triangle.color;
context.fill();
}
function resizeCanvas() {
canvas.width = window.innerWidth-10;
canvas.height = window.innerHeight-10;
fillBackgroundColor();
for(i=0; i < triangles.length; i++) {
drawTriangle(triangles[i]);
}
}
function fillBackgroundColor() {
context.fillStyle = 'black';
context.fillRect(0,0,canvas.width,canvas.height);
}
window.onload = init;
</script>
</head>
<body>
<canvas id="canvas" width="500" height="500">
</body>
</html>
I spent a few minutes reading threw your code, and found one thing that stood out to me. To begin, when you call the timer = setInterval(handleClick, 30); the commands are run from top to bottom in the handleClick function. So when we look at that function, the first thing you draw is the black background. Then, you push out a brand new triangle. Following this, you draw all the triangles in the array using a loop. This is where the issue is. What you are doing is drawing all the past triangles, ontop of the refreshed black rectangle.
Try setting your code to something like this.
function handleClick(x,y) {
context.clearRect(0,0, canvas.width,canvas.height);
var colors = ['red','green','purple','blue','yellow'];
var color = colors[Math.floor(Math.random()*colors.length)];
triangles.push(new Triangle(x, y, color));
triangles.shift(); //This removes the first point in the array, and then shifts all other data points down one index.
for(i=0; i < triangles.length; i++) {
drawTriangle(triangles[i]);
}
}
Pleaase let me know if I miss interpreted any of your code, or if you have a follow up question.
I want to move the rectangle horizontally,however,it enters the updateStageObjects() function several times but does not update the value of myRectangle.x.How do I fix this?
<script type="text/javascript">
var interval = 10;
var x=0;
var y=0;
var myRectangle;
var context ;
var canvas;
function Rectangle(x, y, width, height, borderWidth) {
this.x=x;
this.y=y;
this.width = width;
this.height = height;
this.borderWidth = borderWidth;
}
function DrawRects(){
myRectangle = new Rectangle (250,70,100,50, 5); context.rect(myRectangle.x,myRectangle.y,myRectangle.width,myRectangle.height);
context.fillStyle="#8ED6FF";
context.fill();
context.lineWidth=myRectangle.borderWidth;
context.strokeStyle="black";
context.stroke();
}
function updateStageObjects() {
var amplitude = 150;
var centerX = 240;
myRectangle.x += 100;
alert(myRectangle.x+" "+myRectangle.y);
}
function clearCanvas() {
context.clearRect(0,0,canvas.width, canvas.height);
}
function DrawRect(){
setTimeout(CheckCanvas,10);
clearCanvas();
updateStageObjects();
DrawRects();
}
function CheckCanvas(){
return !!(document.createElement('canvas').getContext);
}
function CheckSound(){
return !!(document.createElement('sound').canPlayType)
}
function CheckVideo(){
return !!(document.createElement('video').canPlayType)
}
function Checkstorage(){
return !!(window.localStorage)
}
function CheckVideo(){
return !!(document.createElement('video').canPlayType)
}
function DrawCanvas(){
if (CheckCanvas()){
canvas = document.getElementById('Canvas');
DrawRects();
setInterval(DrawRect, 10);
}
}
</script>
html
<canvas id="Canvas" width="800px" height="800px" onclick="DrawCanvas()"> Nor supported</canvas>
As Chris has pointed out your DrawRects() always draws the same co-ords, try adding a couple more variables like
var dx = 250;
var dy = 70;
Then change your new Rectangle to
myRectangle = new Rectangle (dx,dy,100,50, 5);
Next change your myRectangle.x += 100 line to
dx += 100;
Hope this helps.
Every time you call updateStageObjects(), it is followed immediately by a call to DrawRects(), which creates a new rectangle at exactly (250,70,100,50, 5). So you'll never notice it increment by 100.
You'll need to pass some numbers back and forth between these functions if you want one of them to remember the changes made in another.