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'm doing a little game on JavaScript and have a problem, Example: I have my square move randomly and one red square always standing (not move), when my square touch the red square, it will automacally find the way avoid the red square, but i don't know how to make my square find its way through the red square. I'm using canvas withmethod canvas.drawRect.
imgur.com/a/7e93Xn6, Here is my square, i want it move up or down automacaly to avoid the red square, but don't know how to make it, Thanks
This is actually not too complicated you just have to continually check if those two squares will touch if one of them is moving. This can be calculated using it's screen positions and size. If those are about to collide move sideways as long as moving forward would still lead into the other square.
Here's a quick example:
var canvas = document.createElement("canvas");
canvas.width = 400;
canvas.height = 300;
document.body.appendChild(canvas);
var context = canvas.getContext("2d");
var Squares = function(xPos, yPos, wid, hei, col) {
this.x = xPos;
this.y = yPos;
this.width = wid;
this.height = hei;
this.color = col;
this.speed = 0;
}
var redSquare = new Squares(200, 100, 40, 40, "#ff0000");
var blueSquare = new Squares(0, 100, 40, 40, "#0000ff");
blueSquare.speed = 3;
var squares = [redSquare, blueSquare];
function loop() {
if (blueSquare.x + blueSquare.width + blueSquare.speed > redSquare.x && blueSquare.y + blueSquare.height > redSquare.y) {
blueSquare.x = redSquare.x - blueSquare.width;
blueSquare.y -= blueSquare.speed;
} else {
blueSquare.x += blueSquare.speed;
}
if (blueSquare.x > canvas.width) {
blueSquare.x = 0;
blueSquare.y = 100;
}
context.clearRect(0, 0, canvas.width, canvas.height);
for (var a = 0; a < squares.length; a++) {
context.fillStyle = squares[a].color;
context.fillRect(squares[a].x, squares[a].y, squares[a].width, squares[a].height);
}
}
var interval = setInterval(loop, 20);
the rotate() function seems to rotate the whole drawing area. Is there a way to rotate paths individually? I want the center for the rotation to be the object, not the drawing area.
Using save() and restore() still makes rotate take into account the whole drawing area.
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
context.save();
context.fillStyle = 'red';
context.rotate(0.35);
context.fillRect(40,40, 100, 100);
context.restore();
context.save();
context.fillStyle = 'blue';
context.rotate(0.35);
context.fillRect(200, 40, 100, 100);
context.restore();
<canvas id="canvas" width="500" height="500"></canvas>
Use local space
Instead of drawing object at the position you want them draw everything around its own origin in its local space. The origin is at (0,0) and is the location that the object rotates around.
So if you have a rectangle that you draw with
function drawRect(){
context.fillRect(200, 40, 100, 100);
}
change it so that it is drawn at its origin
function drawRect(){
context.fillRect(-50,-50 , 100, 100);
}
Now you can easily draw it wherevery you want
Start with the setTransform function as that clears any existing tranforms and is a convenient way to set the location of the center of the object will be
ctx.setTransform(1,0,0,1,posX,posY); // clear transform and set center location
if you want to rotate it then add the rotation
ctx.rotate(ang);
and scale with
ctx.scale(scale,scale);
if you have two different scales you should scale before the rotate.
Now just call the draw function
drawRect();
and it is drawn with its center at posX,posY rotated and scaled.
You can combine it all into a function that has the x,y position, the width and the height, scale and rotation. You can include the scale in the setTransform
function drawRect(x,y,w,h,scale,rotation){
ctx.setTransform(scale,0,0,scale,x,y);
ctx.rotate(rotation);
ctx.strokeRect(-w/2,-h/2,w,h);
}
It also applies to an image as a sprite, and I will include a alpha
function drawImage(img,x,y,w,h,scale,rotation,alpha){
ctx.globalAlpha = alpha;
ctx.setTransform(scale,0,0,scale,x,y);
ctx.rotate(rotation);
ctx.drawImage(img,-img.width/2,-img.height/2,img.width,img.height);
}
On a 6 year old laptop that can draw 2000 sprites on firefox every 1/60th of a second, each rotated, scaled, positioned, and with a alpha fade.
No need to mess about with translating back and forward. Just keep all the objects you draw around there own origins and move that origin via the transform.
Update
Lost the demo so here it is to show how to do it in practice.
Just draws a lot of rotated, scaled translated, alphaed rectangles.
By using setTransform you save a lot of time by avoiding save and restore
// create canvas and add resize
var canvas,ctx;
function createCanvas(){
canvas = document.createElement("canvas");
canvas.style.position = "absolute";
canvas.style.left = "0px";
canvas.style.top = "0px";
canvas.style.zIndex = 1000;
document.body.appendChild(canvas);
}
function resizeCanvas(){
if(canvas === undefined){
createCanvas();
}
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx = canvas.getContext("2d");
}
resizeCanvas();
window.addEventListener("resize",resizeCanvas);
// simple function to draw a rectangle
var drawRect = function(x,y,w,h,scale,rot,alpha,col){
ctx.setTransform(scale,0,0,scale,x,y);
ctx.rotate(rot);
ctx.globalAlpha = alpha;
ctx.strokeStyle = col;
ctx.strokeRect(-w/2,-h/2, w, h);
}
// create some rectangles in unit scale so that they can be scaled to fit
// what ever screen size this is in
var rects = [];
for(var i = 0; i < 200; i ++){
rects[i] = {
x : Math.random(),
y : Math.random(),
w : Math.random() * 0.1,
h : Math.random() * 0.1,
scale : 1,
rotate : 0,
dr : (Math.random() - 0.5)*0.1, // rotation rate
ds : Math.random()*0.01, // scale vary rate
da : Math.random()*0.01, // alpha vary rate
col : "hsl("+Math.floor(Math.random()*360)+",100%,50%)",
};
}
// draw everything once a frame
function update(time){
var w,h;
w = canvas.width; // get canvas size incase there has been a resize
h = canvas.height;
ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.clearRect(0,0,w,h); // clear the canvas
// update and draw each rect
for(var i = 0; i < rects.length; i ++){
var rec = rects[i];
rec.rotate += rec.dr;
drawRect(rec.x * w, rec.y * h, rec.w * w,rec.h * h,rec.scale + Math.sin(time * rec.ds) * 0.4,rec.rotate,Math.sin(time * rec.da) *0.5 + 0.5,rec.col);
}
requestAnimationFrame(update); // do it all again
}
requestAnimationFrame(update);
All transformations in canvas are for the whole drawing area. If you want to rotate around a point you're going to have to translate that point to the origin, do your rotation and translate it back. Something like this is what you want.
Use a rotate function to rotate all of the shape's points around its center.
<!DOCTYPE html>
<html>
<head>
<style>
body
{
margin: 0px;
padding: 0px;
overflow: hidden;
}
canvas
{
position: absolute;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
var canvas;
var context;
canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var degreesToRadians = function(degrees)
{
return degrees*Math.PI/180;
}
var rotate = function(x, y, cx, cy, degrees)
{
var radians = degreesToRadians(degrees);
var cos = Math.cos(radians);
var sin = Math.sin(radians);
var nx = (cos * (x - cx)) + (sin * (y - cy)) + cx;
var ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;
return new Vector2(nx, ny);
}
var Vector2 = function(x, y)
{
return {x:x,y:y};
}
var Shape = function(points, color)
{
this.color = color;
this.points = points;
};
Shape.prototype.rotate = function(degrees)
{
var center = this.getCenter();
for (var i = 0; i < this.points.length; i++)
{
this.points[i] = rotate(this.points[i].x,this.points[i].y,center.x,center.y,degrees);
}
context.beginPath();
context.arc(center.x,center.y,35,0,Math.PI*2);
context.closePath();
context.stroke();
}
Shape.prototype.draw = function()
{
context.fillStyle = this.color;
context.strokeStyle = "#000000";
context.beginPath();
context.moveTo(this.points[0].x, this.points[0].y);
for (var i = 0; i < this.points.length; i++)
{
context.lineTo(this.points[i].x, this.points[i].y);
//context.fillText(i+1, this.points[i].x, this.points[i].y);
}
context.closePath();
context.fill();
context.stroke();
}
Shape.prototype.getCenter = function()
{
var center = {x:0,y:0};
for (var i = 0; i < this.points.length; i++)
{
center.x += this.points[i].x;
center.y += this.points[i].y;
}
center.x /= this.points.length;
center.y /= this.points.length;
return center;
}
Shape.prototype.translate = function(x, y)
{
for (var i = 0; i < this.points.length; i++)
{
this.points[i].x += x;
this.points[i].y += y;
}
}
var Rect = function(x,y,w,h,c)
{
this.color = c;
this.points = [Vector2(x,y),Vector2(x+w,y),Vector2(x+w,y+h),Vector2(x,y+h)];
}
Rect.prototype = Shape.prototype;
var r = new Rect(50, 50, 200, 100, "#ff0000");
r.draw();
r.translate(300,0);
r.rotate(30);
r.draw();
</script>
</body>
</html>
I have the following code, which is drawing out a repeated pattern of small squares to fill the canvas size(like a background). What I want to do next is slowly rotate the squares(in a continuous loop) but I can't seem to get it working. It just rotates the entire canvas. I've read that I need to do something with context saving and restoring but I don't fully understand how this works. I've commented out the bits I'm not sure about, just so it's working. Edit: Here's a codepen of the below
var mainCanvas = document.getElementById("myCanvas");
var mainContext = mainCanvas.getContext('2d');
var canvasWidth = mainCanvas.width;
var canvasHeight = mainCanvas.height;
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
var rotate = 1;
var elementWidth = 40;
var elementHeight = 40;
var canvasWidthGrid = canvasWidth / elementWidth;
var canvasHeightGrid = canvasHeight / elementHeight;
function drawShape() {
mainContext.clearRect(0, 0, canvasWidth, canvasHeight);
mainContext.fillStyle = "#EEEEEE";
mainContext.fillRect(0, 0, canvasWidth, canvasHeight);
mainContext.fillStyle = "#66d";
mainContext.beginPath();
//mainContext.save();
for (var x = 0, i = 0; i < canvasWidthGrid; x+=90, i++) {
for (var y = 0, j=0; j < canvasHeightGrid; y+=90, j++) {
//mainContext.rotate( rotate );
mainContext.fillRect (x, y, 45, 40);
//mainContext.restore();
}
}
requestAnimationFrame(drawShape);
rotate++;
}
drawShape();
You have a couple of issues with your rotation code:
The rotation angle in context.rotate is expressed in radians, not degrees.
Rotation always takes place around the origin point. The default origin point is [0,0]. That's why your rects are spinning as they are -- with all the rects spinning around the top-left of the canvas.
So, if you want your rects to rotate around their center points you can do this:
Set the rotation point to the rects centerpoint with context.translate( rect.centerX, rect.centerY )
Rotate by an angle expressed in radians. This code converts 30 degrees into radians: context.rotate(30*Math.PI/180)
Drawing the rect is a bit tricky. context.translate causes new drawings to originate at [centerX,centerY] so you must offset the drawing command to pull the drawing from center rect back to the top-left rect: context.fillRect(-45/2,-40/2,45,50)
When your done with transformations (translate+rotate) you can reset to the default orientation with context.setTransform(1,0,0,1,0,0)
Here's your code refactored to use these new tips:
var mainCanvas = document.getElementById("myCanvas");
var mainContext = mainCanvas.getContext('2d');
var canvasWidth = mainCanvas.width;
var canvasHeight = mainCanvas.height;
var rotate = 0;
var elementWidth = 40;
var elementHeight = 40;
var canvasWidthGrid = canvasWidth / elementWidth;
var canvasHeightGrid = canvasHeight / elementHeight;
drawShape();
function drawShape() {
mainContext.clearRect(0, 0, canvasWidth, canvasHeight);
mainContext.fillStyle = "#EEEEEE";
mainContext.fillRect(0, 0, canvasWidth, canvasHeight);
mainContext.fillStyle = "#66d";
mainContext.beginPath();
for (var x = 0; x<canvasWidth; x+=90) {
for (var y = 0; y<canvasHeight; y+=90) {
// set origin to center rect
mainContext.translate(x+45/2,y+40/2);
// rotate
mainContext.rotate( rotate );
mainContext.fillRect (-45/2, -40/2, 45, 40);
// reset
mainContext.setTransform(1,0,0,1,0,0);
}
}
requestAnimationFrame(drawShape);
rotate+=Math.PI/90;
}
body{ background-color: ivory; }
canvas{border:1px solid red; margin:0 auto; }
<canvas id="myCanvas" width=360 height=360></canvas>
I am making a guage in javascript for a project at work and based on the value of the guage, the whole thing should change color and it does, however the colors go nuts on some values and I can't figure out why this is going on.
Normaly the behaviour should be the following:
Between 0% and 25% - Red
Between 26% and 75% - Yellow
Between 76% and 100% - Green
When the code runs it apparently behaves as it should except...
If it's between 3% and 9% it yellow (should be red)
If it's 100% it's red (should be green)
If it's 0% it's red, but the bar does a 360 degree spin... wut?
I have looked at the code for over 2 hours and I cannot find the logic in why the bugs, I was wondering is someone here might see something I missed.
HTML:
<canvas id="canvas" width="300" height="300">
CSS:
body {
background: #333;
}
/*Centering the gauge*/
#canvas {
display: block;
width: 300px;
margin: 100px auto;
}
/*Custom font for numbers*/
#font-face {
font-family: "bebas";
src: url("http://thecodeplayer.com/uploads/fonts/BebasNeue.otf");
}
JS:
window.onload = function(){
//canvas initialization
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
//dimensions
var W = canvas.width;
var H = canvas.height;
//Variables
var red = "25%";
var yellow = "75%";
var green = "100%";
var degrees = 0;
var new_degrees = 724;
var difference = 0;
var color = "lightgreen";
var bgcolor = "#222";
var redcolor = "orangered";
var yellowcolor = "goldenrod";
var greencolor = "lightgreen";
var text;
var animation_loop, redraw_loop;
var startAngle = 1 * Math.PI;
var endAngle = 1.7 * Math.PI;
function init()
{
//Clear the canvas everytime a chart is drawn
ctx.clearRect(0, 0, W, H)
//Background 360 degree arc
ctx.beginPath();
ctx.strokeStyle = bgcolor;
ctx.lineWidth = 30;
ctx.arc(W/2, H/2, 100, startAngle, endAngle, false); //you can see the arc now
ctx.stroke();
//gauge will be a simple arc
//Angle in radians = angle in degrees * PI / 180
var radians = degrees * Math.PI / 1030;
ctx.beginPath();
ctx.strokeStyle = color;
ctx.lineWidth = 30;
//The arc starts from the rightmost end. If we deduct 90 degrees from the angles
// CHANGE THIS LINE HERE
//the arc will start from the left
ctx.arc(W/2, H/2, 100, startAngle, radians - 180*Math.PI/180, false);
//you can see the arc now
ctx.stroke();
//Lets add the text
ctx.fillStyle = color;
ctx.font = "50px bebas";
text = Math.floor(degrees/720*100) + "%";
if (text < "25%") {
color = redcolor;
} else if (text > "25%") {
color = yellowcolor;
} else if (text > "75%") {
color = greencolor;
}
//Lets center the text
//deducting half of text width from position x
var text_width = ctx.measureText(text).width;
//adding manual value to position y since the height of the text cannot
//be measured easily. There are hacks but we will keep it manual for now.
ctx.fillText(text, W/2 - text_width/2, H/2 + 15);
}
function draw()
{
//Cancel any movement animation if a new chart is requested
if(typeof animation_loop != undefined) clearInterval(animation_loop);
//random degree from 0 to 360
new_degrees = Math.round(Math.random()*360);
//new_degrees = 721;
difference = new_degrees - degrees;
//This will animate the gauge to new positions
//The animation will take 1 second
//time for each frame is 1sec / difference in degrees
animation_loop = setInterval(animate_to, 1000/difference);
}
//function to make the chart move to new degrees
function animate_to()
{
//clear animation loop if degrees reaches to new_degrees
if(degrees == new_degrees)
clearInterval(animation_loop);
if(degrees < new_degrees)
degrees++;
else
degrees--;
init();
}
//Lets add some animation for fun
draw();
redraw_loop = setInterval(draw, 2000); //Draw a new chart every 2 seconds
}
You can see the code at http://codepen.io/rgaspary/pen/Glfdn
You are using strings instead of numbers. As a result, the comparison is alphanumeric instead of a number comparison.
For example, the string "9%" comes after the string "25%".
I have no idea if synchronosity is the culprit, but...
using setInterval() is generally a bad idea.
Use setTimeout(), and when your draw() function finishes executing, call it recursively with another setTimeout().