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);
Related
This animation (based on the answer of
Вася Воронцов) loads the computer very much. I do this animation in canvas. Animation loads proccesor very much. Here the light follows the cursor and leaves traces. Animation works correctly but proccesor loads very much.
Deleting and changing the radii of circles is done by saving their coordinates.
The effect is controlled by changing the variables radius (circle radius), period (time for which the circle disappears), color (circle color), blur (blur radius) and cursor radius (pointer circle radius).
How to optimize this animation so that it loads the computer less?
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var width = document.body.offsetWidth;
var height = document.body.offsetHeight;
var points = [],
cursor = [-10, -10];
var t = 0;
var radius = 100;
var period = 2100;
var color = "rgba(239, 91, 59, .5)";
var blur = 600;
canvas.style.width = canvas.width = width;
canvas.style.height = canvas.height = height;
context.fillStyle = color;
var filter = context.filter = "blur(" + 50 + "px)";
var dr = radius / period;
function draw() {
context.clearRect(0, 0, width, height);
let i = 0;
let deleted = 0;
let dt = -t + (t = window.performance.now());
context.beginPath();
while (i++ < points.length-1) {
let p = points[i];
p[2] += dt;
let r = radius - p[2] * dr;
context.moveTo(p[0], p[1]);
if (p[2] <= period) {
context.arc(p[0], p[1], r, 0, 2*Math.PI, true);
} else deleted = i;
}
context.fill();
points.splice(0, deleted);
context.beginPath();
context.arc(cursor[0], cursor[1], 20, 0, 2*Math.PI, true);
context.filter = "none";
context.fill();
context.filter = filter;
window.requestAnimationFrame(draw);
}
window.onmousemove = function(event) {
let x = event.pageX;
let y = event.pageY;
let backwardX = 0;
let backwardY = 0;
backwardX += (x-backwardX) / 5
backwardY += (y-backwardY) / 5
points.push([x, y, 0]);
cursor = [x, y];
}
t = window.performance.now();
window.requestAnimationFrame(draw);
body {
height: 100%;
width: 100%;
position: absolute;
cursor: none;
margin: 0;
}
<canvas id="canvas"></canvas>
PS: Question in Russian.
It's slow because you have a lot of overdraw. Each frame, a large number of points is being drawn, and each point touches a lot of pixels.
You can achieve something that looks very similar if you realize that the canvas retains its contents between frames. So every frame, you could do something like this:
Fade the canvas towards white by drawing a nearly transparent white rectangle over it.
Draw one new blurred point, at the current cursor location.
The circle that follows the mouse can easily be achieved by overlaying a separate element on top of the canvas, for example a <div>. Use transform: translate(x, y); to move it, which is more performant than using left/top because it's a compositor-only property. Add will-change: transform; for an extra potential performance boost.
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 would like to make the text in a canvas to appears and disappers from left to right. I don't know if it's possible. I tried to hide the text with the clip() function, but I can't transform the clip to reduce the width and make the text appears.
ctx.fillStyle = "#1f2f90";
ctx.fillText('This is a text!', 150, 100);
ctx.rect(50, 20, 200, 120);
ctx.stroke();
ctx.clip();
var i = 200;
setInterval(function(){
ctx.clearRect(0,0,300,150);
ctx.rect(50,20,i,120);
ctx.stroke();
ctx.clip();
i++;
},20);
Here is an example using fillRect to cover the text.
let ctx = document.getElementById('canvas').getContext('2d');
ctx.fillStyle = "#1f2f90";
ctx.fillText('This is a text!', 150, 100);
var i = 0;
setInterval(function(){
ctx.fillStyle = "white";
ctx.fillRect(50,20,i,120);
ctx.strokeRect(50, 20, 200, 120);
i++;
},20);
<canvas id="canvas"></canvas>
My solution is different. Every letter is an object with a position and a transparency alpha. During the animation the transparency of of the letters is decreasing, one letter at a time.
You may restart the animation on click.
Please read the comments in the code.
const canvas = document.getElementById("canvas");
const _canvas = document.getElementById("_canvas");
const ctx = canvas.getContext("2d");
const _ctx = _canvas.getContext("2d");
let cw = canvas.width = _canvas.width = 400,
cx = cw / 2;
let ch = canvas.height = _canvas.height = 150,
cy = ch / 2;
let rid = null;// request animation id
let theText = 'This is a text!';
let letters = [];// the letters array
let k = 20;// controls the speed of the animation
ctx.font = _ctx.font = "2em Verdana";
// every letter is an object
class Letter{
constructor(char,x){
this.char = char;// the letter
// measure the letter
_ctx.fillText(this.char, 0, cy);
this.w = _ctx.measureText(this.char).width;
//the position of the text
this.pos = {}
this.pos.y = cy;
this.pos.x = x;
// the transparency of the letter
this.alpha = 1;
}
show(){// draw the letter
ctx.fillStyle = `rgba(0,0,0,${this.alpha})`;
ctx.fillText(this.char, this.pos.x, this.pos.y);
}
update(){
//change the transparency of the text
if(this.alpha > 0){this.alpha -= 1/k;}
if(this.alpha < 0){this.alpha = 0; index++}
}
}
let x = 0;
for(l=0; l<theText.length; l++){
// a new letter object
letters.push(new Letter(theText[l],x))
//calculate the x position of the next letter
x = letters.reduce( function(a, b){return a + b.w}, 0);
}
// draw all the letters
for(l=0; l<letters.length; l++){letters[l].show()}
// the actual letter.
let index = 0;
function Draw() {
rid = window.requestAnimationFrame(Draw);
ctx.clearRect(0,0,cw,ch);
letters[index].update();//change the transparency of the actual letter
// draw all the letters
for(l=0; l<letters.length; l++){letters[l].show()}
// if the last letter is fully transparent stop the animation
if(letters[letters.length - 1].alpha <= 0){
window.cancelAnimationFrame(rid);rid = null;
}
}
Draw();
//resume animation on click
canvas.addEventListener("click",()=>{
if(rid){window.cancelAnimationFrame(rid);rid = null;}
index = 0;
for(l=0; l<letters.length; l++){letters[l].alpha = 1;letters[l].show()}
Draw();
})
#_canvas{display:none;}
canvas {
border:1px solid;
}
<canvas id="canvas"></canvas>
<canvas id="_canvas"></canvas>
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.
Here I'm trying to draw squares with the same opacity, side by side.
Even though each squares x position is exactly the width of a square apart, they look as though there are more semi transparent squares being drawn on top of them. The first square having the higher opacity level, and less so on the last one, which creates this gradient of squares effect.
I have console logged out the number of squares being rendered and it is as expected. So I dont believe there are extra squares being rendered on top of them.
I'v also checked the x positions to see if there is any overlap, which there isn't. So what could be causing the squares to have different transparencies?
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
const canvas = document.getElementById("app-canvas");
canvas.width = windowWidth;
canvas.height = windowHeight;
canvas.style.position = "absolute";
canvas.style.backgroundColor = "grey";
canvas.style.zIndex = 1;
document.body.appendChild(canvas);
class Scene {
constructor() {
this.orbs = [];
this.squares = [];
this.context = canvas.getContext("2d");
}
addOrb(orb) {
this.orbs.push(orb);
}
addSquare(square) {
this.squares.push(square);
}
render() {
this.context.clearRect(0, 0, windowWidth, windowHeight);
this.squares.forEach(square => {
square.draw(this.context);
});
}
}
class Square {
constructor(options) {
this.options = options;
}
draw(ctx) {
let x = this.options.x;
let y = this.options.y;
let width = this.options.width;
let height = this.options.height;
ctx.moveTo(x,y);
ctx.rect(x, y, width, height);
ctx.fillStyle = "rgba(255,255,255,.1)";
ctx.fill();
}
};
let squares = [];
let squareWidth = 200;
let squareHeight = 200;
let x = 0;
let y = 0;
for (let i = 0; i < windowWidth; i+=squareWidth) {
x += squareWidth;
let square = new Square({
x, y: 0, width: squareWidth, height: squareHeight
});
console.log(square)
squares.push(square);
}
console.log(squares.length);
let scene = new Scene();
squares.forEach(square => scene.addSquare(square));
scene.render();
You need a call to ctx.beginPath() before every rectangle. Otherwise, the calls to .rect() are just adding to the path, and fill() fills the whole thing.
draw(ctx) {
let x = this.options.x;
let y = this.options.y;
let width = this.options.width;
let height = this.options.height;
ctx.beginPath(); // <========= this
ctx.moveTo(x,y);
ctx.rect(x, y, width, height);
ctx.fillStyle = "rgba(255,255,255,.1)";
ctx.fill();
}
You might also consider just using .fillRect().