How to limit the boundary of pan in fabric js? - javascript

How to limit the boundary of pan in fabric js?
I wrote the following code to limit the pan of my canvas but it is just not working. I want it to remember how much it has move outside of the canvas so that even after your pointer goes out of the canvas, you will have to move back the same amount to start dragging again. Currently, after the pointer move out of the canvas, the image will stick it. I cannot understand what I am doing wrong. Can someone help me?
Update
The following solution seems to work for zoom 1x. Still need to figure out how to solve it after zoom. But it is so verbose. Is there a better way to do this? https://jsfiddle.net/yxchng/0hL2khro/356/
canvas.on('mouse:move', function (opt) {
if (this.isDragging) {
var e = opt.e;
var zoom = canvas.getZoom();
var moveX = 0;
var moveY = 0;
if ((e.clientX - this.lastPosX) * this.offsetX > 0) {
this.offsetX += e.clientX - this.lastPosX;
} else if (e.clientX - this.lastPosX > 0 && this.offsetX < 0) {
moveX = e.clientX - this.lastPosX + this.offsetX;
if (moveX > 0) {
this.viewportTransform[4] += moveX;
this.offsetX = 0;
} else {
this.offsetX = moveX;
}
} else if (e.clientX - this.lastPosX < 0 && this.offsetX > 0) {
moveX = e.clientX - this.lastPosX + this.offsetX;
if (moveX < 0) {
this.viewportTransform[4] += moveX;
this.offsetX = 0;
} else {
this.offsetX = moveX;
}
} else {
this.viewportTransform[4] += e.clientX - this.lastPosX;
if (this.viewportTransform[4] < 0) {
this.offsetX = this.viewportTransform[4];
this.viewportTransform[4] = 0;
}
if (this.viewportTransform[4] > canvas.width) {
this.offsetX = this.viewportTransform[4] - canvas.width;
this.viewportTransform[4] = canvas.width;
}
}
if ((e.clientY - this.lastPosY) * this.offsetY > 0) {
this.offsetY += e.clientY - this.lastPosY;
} else if (e.clientY - this.lastPosY > 0 && this.offsetY < 0) {
moveY = e.clientY - this.lastPosY + this.offsetY;
if (moveY > 0) {
this.viewportTransform[5] += moveY;
this.offsetY = 0;
} else {
this.offsetY = moveY;
}
} else if (e.clientY - this.lastPosY < 0 && this.offsetY > 0) {
moveY = e.clientY - this.lastPosY + this.offsetY;
if (moveY < 0) {
this.viewportTransform[5] += moveY;
this.offsetY = 0;
} else {
this.offsetY = moveY;
}
} else {
this.viewportTransform[5] += e.clientY - this.lastPosY;
if (this.viewportTransform[5] < 0) {
this.offsetY = this.viewportTransform[5];
this.viewportTransform[5] = 0;
}
if (this.viewportTransform[5] > canvas.height) {
this.offsetY = this.viewportTransform[5] - canvas.height;
this.viewportTransform[5] = canvas.height;
}
}
this.requestRenderAll();
this.lastPosX = e.clientX;
this.lastPosY = e.clientY;
}
});

Related

Why can't I use a function to do collision check in javascript?

I have a simple collision detection working that lets you jump off of blocks, and yet if I call a function that has the same code block in it. It will break.
call without the function:
for(let i = 0; i < blocks.length; i++){
if(blockCollision(sprite, blocks[i])){
sprite.vy = 0;
// sprite.vy += gravity;
if(blockCollisionLeft(sprite, blocks[i])){
sprite.vx = 0;
sprite.x = blocks[i].x - sprite.w - offset;
}
if(blockCollisionRight(sprite, blocks[i])){
sprite.vx = 0;
sprite.x = blocks[i].x + blocks[i].w + offset;
}
if(blockCollisionTop(sprite, blocks[i])){
sprite.y = blocks[i].y - sprite.h - offset;
sprite.canJump = true;
}else{
// sprite.vy = 0;
sprite.y = blocks[i].y + blocks[i].h + offset;
sprite.vy += gravity;
}
}
}
and the call with the function:
for(let i = 0; i < blocks.length; i++){
if(blockCollision(sprite, blocks[i])){
checkCollisions(sprite, blocks[i]);
break;
}
}
I'm not sure what to do at this point, the sprite block keeps jumping out of control, can't quite understand why a function call would do this.
const PI = Math.PI;
const offset = 1;
const gravity = .33;
function inCanvas(obj){
if(obj.x - offset < 0 && obj.x + obj.w + offset > w
&& obj.y - offset < 0 && obj.y + obj.h + offset > h){
return true;
}
return false;
}
function groundCollision(obj){
if(obj.y + obj.h + offset == h){
return true;
}
return false;
}
function blockCollision(obj, block){
if(obj.x - offset < block.x + block.w && obj.x + obj.w + offset > block.x
&& obj.y - offset < block.y + block.h && obj.y + obj.h + offset > block.y){
return true;
}
return false;
}
function blockCollisionTop(obj, block){
if(obj.y + obj.h + offset > block.y && obj.y + obj.h + offset < block.y + block.h){
return true;
}
return false;
}
function blockCollisionLeft(obj, block){
if(obj.x + obj.w + offset > block.x && obj.x + obj.w < block.x){
return true;
}
return false;
}
function blockCollisionRight(obj, block){
if(obj.x - offset < block.x + block.w && obj.x > block.x + block.w){
return true;
}
return false;
}
function Sprite(_x, _y, _w, _h){
this.x = _x;
this.y = _y;
this.w = _w;
this.h = _h;
this.vx = 0;
this.vy = 0;
this.jv = -11;
this.canJump = false;
// this.maxv = 3;
};
Sprite.prototype.draw = function(){
ctx.fillRect(this.x, this.y, this.w, this.h);
};
Sprite.prototype.update = function(){
this.x += this.vx;
this.y += this.vy;
this.vy += gravity;
// this.yv -= gravity;
// this.xv -= gravity;
// this.xv += gravity;
if(this.y + this.h + offset > h){
this.y = h - this.h - offset;
}
if(this.y - offset < 0){
this.y = offset;
}
if(this.x - offset < 0){
this.x = offset;
}
if(this.x + this.w + offset > w){
this.x = w - this.w - offset;
}
};
Sprite.prototype.jump = function(){
this.vy = this.jv;
};
function Block(_x, _y, _w, _h){
this.x = _x;
this.y = _y;
this.w = _w;
this.h = _h;
this.steppedOn = false;
};
Block.prototype.draw = function(){
ctx.fillRect(this.x, this.y, this.w, this.h);
};
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
const w = canvas.width = 600;
const h = canvas.height = 600;
var right = false,
left = false,
up = false;
var sprite = new Sprite(100, 200, 20, 40);
var box1 = new Block(w - 300, h - 80, 300, 10);
var blocks = [];
for(let i = 1; i <= 10; i++){
blocks.push(new Block(Math.round(Math.random() * w), Math.round(Math.random() * h), w/4 + Math.round(Math.random() * (w)), 15));
}
//console.log(blocks);
(function update(){
ctx.clearRect(0, 0, w, h);
sprite.draw();
for(let i = 0; i < blocks.length; i++){
blocks[i].draw();
}
box1.draw();
sprite.update();
// pOne.xv = left ? -2 : right ? 2 : 0;
if(left && right){
sprite.vx = 0;
}
else if(left){
sprite.vx = -2;
}
else if(right){
sprite.vx = 2;
}
else{
sprite.vx = 0;
}
if(up && sprite.canJump){
sprite.jump();
sprite.canJump = false;
}
if(groundCollision(sprite)){
sprite.canJump = true;
}
checkCollisions(sprite, box1);
for(let i = 0; i < blocks.length; i++){
if(blockCollision(sprite, blocks[i])){
checkCollisions(sprite, blocks[i]);
break;
}
}
// if(blockCollision(sprite, box1)){
// // checkCollisions(sprite, box1);
// sprite.vy = 0;
// // sprite.vy += gravity;
// if(blockCollisionLeft(sprite, box1)){
// sprite.vx = 0;
// sprite.x = box1.x - sprite.w - offset;
// }
// if(blockCollisionRight(sprite, box1)){
// sprite.vx = 0;
// sprite.x = box1.x + box1.w + offset;
// }
// if(box1.steppedOn = blockCollisionTop(sprite, box1)){
// sprite.y = box1.y - sprite.h - offset;
// sprite.canJump = true;
// }else{
// // sprite.vy = 0;
// sprite.y = box1.y + box1.h + offset;
// sprite.vy += gravity;
// }
// }
window.addEventListener("keydown", (e)=>{
// console.log(e.key);
if(e.key == "a" || e.key == "ArrowLeft"){
left = true;
}
if(e.key == "d" || e.key == "ArrowRight"){
right = true;
}
if(e.key == " " || e.key == "ArrowUp"){
up = true;
}
});
window.addEventListener("keyup", (e)=>{
if(e.key == "a" || e.key == "ArrowLeft"){
left = false;
}
if(e.key == "d" || e.key == "ArrowRight"){
right = false;
}
if(e.key == " " || e.key == "ArrowUp"){
up = false;
}
});
window.requestAnimationFrame(update);
}());
function checkCollisions(obj, block){
if(blockCollision(obj, block)){
obj.vy = 0;
// obj.vy += gravity;
if(blockCollisionLeft(obj, block)){
obj.vx = 0;
obj.x = block.x - obj.w - offset;
}
if(blockCollisionRight(obj, block)){
obj.vx = 0;
obj.x = block.x + block.w + offset;
}
if(block.steppedOn = blockCollisionTop(obj, block)){
obj.y = obj.y - obj.h - offset;
obj.canJump = true;
}else{
// obj.vy = 0;
obj.y = block.y + block.h + offset;
obj.vy += gravity;
}
}
}
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Block Jump</title>
<style>
*{margin:0; padding: 0;}
#game{
display:inherit;
margin:30px auto;
border:thin solid black;
}
</style>
</head>
<body>
<canvas id="game"></canvas>
<script src="JS/main.js"></script>
</body>
Your first two code snippets are not equivalent, you seem to have added a break statement in your example with a function. This will have the effect of terminating the for loop prematurely when blockCollision(sprite, blocks[i]) is true, which might be why you are seeing a difference between the two.
edit:
You also seem to have changed the third if-block slightly in your function. In your first code snippet that if-block is defined as:
if(blockCollisionTop(sprite, blocks[i])){
sprite.y = blocks[i].y - sprite.h - offset;
sprite.canJump = true;
}
but in your function is defined as:
if(block.steppedOn = blockCollisionTop(obj, block)){
obj.y = obj.y - obj.h - offset;
obj.canJump = true;
}
which is not equivalent because you have replaced blocks[i] with obj, but it should be block.

Land a character on a block after he jumps on it in p5.js game

I'm just trying to create a small 2d game where we control a character that can place block on the "map" with the mouse and jump on it to climb etc. It's just a test to practice.
I've achieved to make the character stops when he walk into blocks.
But, i'm facing a problem when I want my character to jump on a block, i'm not able to make him land on it. I can't find the condition to.
Can someone helps me on it ? The code that handle the collision between the character and blocks is in the draw function and in the for.
Controls
Left: Q
Right: D
Jump: Space Bar
Place a block: Mouse Click
Remove a block: Mouse Click
Edit: You might have ask yourself why i'm using some variables that are not declared anywhere. It's because they are given by P5.js. (ex: width)
function Man() {
this.w = 20;
this.h = 40;
this.x = width/2;
this.y = height - this.h;
this.canJump = true;
this.velocity = 0;
this.show = function(){
fill(255);
rect(this.x, this.y, this.w, this.h);
}
this.update = function(){
this.velocity += gravity;
this.y += this.velocity;
if(this.y + this.h > height) {
this.y = height - this.h ;
this.velocity = 0;
this.canJump = true;
}
if(this.x <= 0) {
this.x = 0;
}
if(this.x + this.w >= width) {
this.x = width - this.w;
}
}
this.jump = function(){
if(this.canJump) {
man.velocity -= 6;
this.canJump = false;
}
}
}
var man;
var ground;
var gravity = 0.4;
function setup(){
frameRate(60);
createCanvas(640, 480);
man = new Man();
}
var tilesSize = 20;
var tiles = [];
for(var i = 0; i < 640; i+= tilesSize) {
for(var j = 0; j < 480; j+= tilesSize) {
tiles.push({x:i, y:j, empty: true});
}
}
function draw(){
background(0);
noStroke();
push();
for(var i = 0; i < tiles.length; i++) {
if(!tiles[i].empty) {
fill(150);
rect(tiles[i].x, tiles[i].y, tilesSize, tilesSize);
}
if(mouseX >= tiles[i].x && mouseX < tiles[i].x + tilesSize && mouseY >= tiles[i].y && mouseY < tiles[i].y + tilesSize) {
if(tiles[i].empty) {
fill(50);
rect(tiles[i].x, tiles[i].y, tilesSize, tilesSize);
}else {
fill(255,0,0);
rect(tiles[i].x, tiles[i].y, tilesSize, tilesSize);
}
}
// HERE IS CONDITIONS FOR COLLISION WITH BLOCK
if(!tiles[i].empty) {
if(tiles[i].x + tilesSize >= man.x && tiles[i].x < man.x && tiles[i].y < man.y + man.h ) {
man.x = tiles[i].x + tilesSize;
}
if(man.x + man.w >= tiles[i].x && man.x <= tiles[i].x && tiles[i].y < man.y + man.h) {
man.x = tiles[i].x - man.w;
}
}
}
pop();
man.show();
man.update();
moveMan();
}
function moveMan(){
if(keyIsDown(81)) {
man.x -= 2;
}
if(keyIsDown(68)) {
man.x += 2;
}
}
function keyPressed() {
if(keyIsDown(32)) {
man.jump();
}
}
function mouseClicked() {
for(var i = 0; i < tiles.length; i++) {
if(mouseX >= tiles[i].x && mouseX < tiles[i].x + tilesSize && mouseY >= tiles[i].y && mouseY < tiles[i].y + tilesSize) {
if(tiles[i].empty) {
tiles[i].empty = false;
}else {
tiles[i].empty = true;
}
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/p5.min.js"></script>

How to stop multiple running mouse event functions

I want to resize squares drawn on canvas (created by clicking) with mouse by moving from corners. Should only resize selected square. I have four different functions for each corner, and after resizing from more than one corner functions keep executing. How to prevent this?
var isMouseDown = false;
var canvas;
var ctx, size;
$(document).ready(function() {
canvas = $('#area')[0];
ctx = canvas.getContext('2d');
$('#area').on('mousedown', canvasClick);
$('#area').on('mouseup', up);
});
function up(event) {
isMouseDown = false;
resizing = false;
console.log("up");
return;
}
function Square(x, y, size) {
this.x = x;
this.y = y;
this.size = size;
this.isSelected = false;
}
var x, y;
function addSquare(event) {
x = event.pageX - canvas.offsetLeft;
y = event.pageY - canvas.offsetTop;
var size = parseInt(Math.random() * 81 + 49);
x -= size / 2;
y -= size / 2;
ctx.fillRect(x, y, size, size);
var square = new Square(x, y, size);
squares.push(square);
drawSquares();
}
var squares = new Array;
var previousSelectedSquare;
function drawSquares() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < squares.length; i++) {
var Square = squares[i];
ctx.beginPath();
ctx.fillStyle = "orange";
ctx.rect(Square.x, Square.y, Square.size, Square.size);
ctx.strokeStyle = "black";
if (Square.isSelected) {
ctx.lineWidth = 4;
} else {
ctx.lineWidth = 1;
}
ctx.fill();
ctx.stroke();
}
}
function canvasClick(event) {
isMouseDown = true;
x = event.pageX - canvas.offsetLeft;
y = event.pageY - canvas.offsetTop;
if (squares.length < 1) {
addSquare(event);
return;
}
var Square;
if (previousSelectedSquare) {
var prevX = previousSelectedSquare.x;
var prevY = previousSelectedSquare.y;
size = previousSelectedSquare.size;
}
var change = 7;
if (((x <= prevX + size + change) && (x >= prevX + size - change)) ||
((x >= prevX - change) && (x <= prevX + change)))
if (((y <= prevY + size + change) && (y >= prevY + size - change)) ||
((y <= prevY + change) && (y >= prevY - change))) {
console.log("borders");
$('#area').on('mousemove', resize(event));
return;
}
for (var i = squares.length - 1; i >= 0; i--) {
Square = squares[i];
if ((x >= Square.x && x <= Square.x + Square.size) && (y <= Square.y + Square.size && y >= Square.y)) {
Square.isSelected = true;
if (previousSelectedSquare != null)
previousSelectedSquare.isSelected = false;
if (previousSelectedSquare == Square)
previousSelectedSquare = null;
else
previousSelectedSquare = Square;
drawSquares();
return;
}
if (i === 0) {
addSquare(event);
return;
}
}
return;
}
// Part that resizes
var resizing = false;
function resize(event) {
if (!isMouseDown)
return;
resizing = true;
x = (event.pageX - canvas.offsetLeft);
y = (event.pageY - canvas.offsetTop);
size = previousSelectedSquare.size;
var centreX = previousSelectedSquare.x + size / 2;
var centreY = previousSelectedSquare.y + size / 2;
if (x > centreX && y < centreY) {
topr = true;
//top right
$('#area').on('mousemove', topR);
return;
}
if (x < centreX && y < centreY) {
//top left
$('#area').on('mousemove', topL);
return;
}
if (x > centreX && y > centreY) {
//bot right
$('#area').on('mousemove', botR);
return;
}
if (x < centreX && y > centreY) {
//bot left
$('#area').on('mousemove', botL);
return;
}
resizing = false;
return;
}
function topR(event) {
if (!resizing || !isMouseDown)
return;
$('#area').on('mouseup', up);
console.log("top right");
size = previousSelectedSquare.size;
var yb = previousSelectedSquare.y + size;
if (parseInt(yb - (event.pageY - canvas.offsetTop)) < 60)
return;
previousSelectedSquare.size = parseInt(yb - (event.pageY - canvas.offsetTop));
previousSelectedSquare.y = parseInt(event.pageY - canvas.offsetTop);
drawSquares();
return;
}
function topL(event) {
if (!resizing || !isMouseDown)
return;
$('#area').on('mouseup', up);
console.log("top left");
x = previousSelectedSquare.x + previousSelectedSquare.size;
y = previousSelectedSquare.y + previousSelectedSquare.size;
if (parseInt(y - (event.pageY - canvas.offsetTop)) < 60)
return;
previousSelectedSquare.size = parseInt(y - (event.pageY - canvas.offsetTop));
previousSelectedSquare.y = parseInt(event.pageY - canvas.offsetTop);
previousSelectedSquare.x = parseInt(event.pageX - canvas.offsetLeft);
drawSquares();
return;
}
function botR(event) {
if (!resizing || !isMouseDown)
return;
$('#area').on('mouseup', up);
console.log("bot right");
size = previousSelectedSquare.size;
if ((event.pageX - canvas.offsetLeft - previousSelectedSquare.x + size) / 2 < 60)
return;
previousSelectedSquare.size = parseInt((event.pageX - canvas.offsetLeft - previousSelectedSquare.x + size) / 2);
drawSquares();
return;
}
function botL(event) {
if (!resizing || !isMouseDown)
return;
$('#area').on('mouseup', up);
console.log("bot left");
var xr = previousSelectedSquare.x + previousSelectedSquare.size;
if (xr - event.pageX - canvas.offsetLeft < 60)
return;
previousSelectedSquare.size = parseInt(previousSelectedSquare.x + previousSelectedSquare.size - (event.pageX - canvas.offsetLeft));
previousSelectedSquare.x = parseInt(event.pageX - canvas.offsetLeft);
drawSquares();
return;
}
#area {
margin-top: 0;
border: 4px black solid;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="area" width="1250" height="550"></canvas>
Using namespace events, after you check the position, remove other handlers then add the one for the current corner.
e.g.: $('#area').off('mousemove.botR').off('mousemove.topL').off('mousemove.botL').on('mousemove.topR', topR);
var isMouseDown = false;
var canvas;
var ctx, size;
$(document).ready(function() {
canvas = $('#area')[0];
ctx = canvas.getContext('2d');
$('#area').on('mousedown', canvasClick);
$('#area').on('mouseup', up);
});
function up(event) {
isMouseDown = false;
resizing = false;
console.log("up");
return;
}
function Square(x, y, size) {
this.x = x;
this.y = y;
this.size = size;
this.isSelected = false;
}
var x, y;
function addSquare(event) {
x = event.pageX - canvas.offsetLeft;
y = event.pageY - canvas.offsetTop;
var size = parseInt(Math.random() * 81 + 49);
x -= size / 2;
y -= size / 2;
ctx.fillRect(x, y, size, size);
var square = new Square(x, y, size);
squares.push(square);
drawSquares();
}
var squares = new Array;
var previousSelectedSquare;
function drawSquares() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < squares.length; i++) {
var Square = squares[i];
ctx.beginPath();
ctx.fillStyle = "orange";
ctx.rect(Square.x, Square.y, Square.size, Square.size);
ctx.strokeStyle = "black";
if (Square.isSelected) {
ctx.lineWidth = 4;
} else {
ctx.lineWidth = 1;
}
ctx.fill();
ctx.stroke();
}
}
function canvasClick(event) {
isMouseDown = true;
x = event.pageX - canvas.offsetLeft;
y = event.pageY - canvas.offsetTop;
if (squares.length < 1) {
addSquare(event);
return;
}
var Square;
if (previousSelectedSquare) {
var prevX = previousSelectedSquare.x;
var prevY = previousSelectedSquare.y;
size = previousSelectedSquare.size;
}
var change = 7;
if (((x <= prevX + size + change) && (x >= prevX + size - change)) ||
((x >= prevX - change) && (x <= prevX + change)))
if (((y <= prevY + size + change) && (y >= prevY + size - change)) ||
((y <= prevY + change) && (y >= prevY - change))) {
console.log("borders");
$('#area').on('mousemove', resize(event));
return;
}
for (var i = squares.length - 1; i >= 0; i--) {
Square = squares[i];
if ((x >= Square.x && x <= Square.x + Square.size) && (y <= Square.y + Square.size && y >= Square.y)) {
Square.isSelected = true;
if (previousSelectedSquare != null)
previousSelectedSquare.isSelected = false;
if (previousSelectedSquare == Square)
previousSelectedSquare = null;
else
previousSelectedSquare = Square;
drawSquares();
return;
}
if (i === 0) {
addSquare(event);
return;
}
}
return;
}
// Part that resizes
var resizing = false;
function resize(event) {
if (!isMouseDown)
return;
resizing = true;
x = (event.pageX - canvas.offsetLeft);
y = (event.pageY - canvas.offsetTop);
size = previousSelectedSquare.size;
var centreX = previousSelectedSquare.x + size / 2;
var centreY = previousSelectedSquare.y + size / 2;
if (x > centreX && y < centreY) {
topr = true;
//top right
$('#area').off('mousemove.botR').off('mousemove.topL').off('mousemove.botL').on('mousemove.topR', topR);
return;
}
if (x < centreX && y < centreY) {
//top left
$('#area').off('mousemove.topR').off('mousemove.botL').off('mousemove.botR').on('mousemove.topL', topL);
return;
}
if (x > centreX && y > centreY) {
//bot right
$('#area').off('mousemove.topR').off('mousemove.topL').off('mousemove.botL').on('mousemove.botR', botR);
return;
}
if (x < centreX && y > centreY) {
//bot left
$('#area').off('mousemove.topR').off('mousemove.topL').off('mousemove.botR').on('mousemove.botL', botL);
return;
}
resizing = false;
return;
}
function topR(event) {
if (!resizing || !isMouseDown)
return;
$('#area').on('mouseup', up);
console.log("top right");
size = previousSelectedSquare.size;
var yb = previousSelectedSquare.y + size;
if (parseInt(yb - (event.pageY - canvas.offsetTop)) < 60)
return;
previousSelectedSquare.size = parseInt(yb - (event.pageY - canvas.offsetTop));
previousSelectedSquare.y = parseInt(event.pageY - canvas.offsetTop);
drawSquares();
return;
}
function topL(event) {
if (!resizing || !isMouseDown)
return;
$('#area').on('mouseup', up);
console.log("top left");
x = previousSelectedSquare.x + previousSelectedSquare.size;
y = previousSelectedSquare.y + previousSelectedSquare.size;
if (parseInt(y - (event.pageY - canvas.offsetTop)) < 60)
return;
previousSelectedSquare.size = parseInt(y - (event.pageY - canvas.offsetTop));
previousSelectedSquare.y = parseInt(event.pageY - canvas.offsetTop);
previousSelectedSquare.x = parseInt(event.pageX - canvas.offsetLeft);
drawSquares();
return;
}
function botR(event) {
if (!resizing || !isMouseDown)
return;
$('#area').on('mouseup', up);
console.log("bot right");
size = previousSelectedSquare.size;
if ((event.pageX - canvas.offsetLeft - previousSelectedSquare.x + size) / 2 < 60)
return;
previousSelectedSquare.size = parseInt((event.pageX - canvas.offsetLeft - previousSelectedSquare.x + size) / 2);
drawSquares();
return;
}
function botL(event) {
if (!resizing || !isMouseDown)
return;
$('#area').on('mouseup', up);
console.log("bot left");
var xr = previousSelectedSquare.x + previousSelectedSquare.size;
if (xr - event.pageX - canvas.offsetLeft < 60)
return;
previousSelectedSquare.size = parseInt(previousSelectedSquare.x + previousSelectedSquare.size - (event.pageX - canvas.offsetLeft));
previousSelectedSquare.x = parseInt(event.pageX - canvas.offsetLeft);
drawSquares();
return;
}
#area {
margin-top: 0;
border: 4px black solid;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="area" width="1250" height="550"></canvas>

How can I make the ball bounce off the red bricks without it (the red brick) breaking? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
So basically I want the red bricks to have an extra bounce (but not the blue ones). It means if I hit it once the red brick reflects the ball without it disappearing but when I hit it the second time it does. Thank you in advance.
<!DOCTYPE html>
<html>
<head>
<title>Breakout_Game</title>
</head>
<body>
<canvas id="myCanvas" width="800" height="600"></canvas>
<script type="text/javascript">
var canvas, canvasContext;
var ballX = 75, ballY = 75;
var ballSpeedX = 3, ballSpeedY = 3;
var ballR = 7;
const brickW = 80;
const brickH = 20;
const brickCols = Math.floor(800 / brickW);
const brickRows = Math.floor(600 / (brickH * 3));
const brickGap = 1;
var brickGrid = new Array(brickCols * brickRows);
var bricksLeft = 0;
const paddleW = 100, paddleH = 10;
var paddleX = 400, paddleY = 600;
const distanceBP = 60;
var mouseX, mouseY;
function updateMousePos (evt) {
var rect = canvas.getBoundingClientRect(); // this is for adjustments only (getting the mouse coordinates even if the page is scrollable)
var root = document.documentElement;
mouseX = evt.clientX - rect.left - root.scrollLeft; // clientX is the X of mouse
mouseY = evt.clientY - rect.top - root.scrollTop;
paddleX = mouseX - paddleW/2;
/* //Cheats Help debugging
ballX = mouseX;
ballY = mouseY;*/
}
function brickReset () {
bricksLeft = 0;
var i;
for(i = 0; i < 3 * brickCols; i++) {
brickGrid[i] = false;
}
for (; i < brickCols * brickRows; i++) {
brickGrid[i] = true;
bricksLeft++;
}
randBrickColor();
}
window.onload = function () {
canvas = document.getElementById("myCanvas");
canvasContext = canvas.getContext("2d");
var fps = 60;
setInterval(updateAll, 1000 / fps);
canvas.addEventListener("mousemove", updateMousePos); // everytime the mouse moves we call the function updateMousePos()
brickReset();
ballReset();
}
function updateAll () {
drawAll();
moveAll();
}
function ballReset () {
ballX = canvas.width / 2;
ballY = canvas.height / 2;
}
function ballMove () {
ballX += ballSpeedX;
ballY += ballSpeedY;
//COLLISION
//left
if (ballX - ballR < 0 && ballSpeedX < 0.0) {
ballSpeedX = -ballSpeedX;
}
//right
if (ballX + ballR > canvas.width && ballSpeedX > 0.0) {
ballSpeedX = -ballSpeedX;
}
//top
if (ballY - ballR < 0 && ballSpeedY < 0.0) {
ballSpeedY = -ballSpeedY;
}
//bottom
if (ballY > canvas.height) {
ballReset();
brickReset();
}
}
function isBrickAtColRow (col, row) {
if (col >= 0 && col < brickCols && row >= 0 && row < brickRows) {
var brickIndexUnderCoord = rowColToArrayIndex(col, row);
return brickGrid[brickIndexUnderCoord];
} else {
return false;
}
}
function ballBrickHandling () {
var ballBrickCol = Math.floor(ballX / brickW);
var ballBrickRow = Math.floor(ballY / brickH);
var brickIndexUnderBall = rowColToArrayIndex(ballBrickCol, ballBrickRow);
if (brickIndexUnderBall >= 0 && brickIndexUnderBall < brickCols * brickRows) {
if (isBrickAtColRow( ballBrickCol,ballBrickRow)) {
brickGrid[brickIndexUnderBall] = false;
bricksLeft--;
//console.log( bricksLeft)
var prevBallX = ballX - ballSpeedX;
var prevBallY = ballY - ballSpeedY;
var prevBrickCol = Math.floor(prevBallX / brickW);
var prevBrickRow = Math.floor(prevBallY / brickH);
var bothTestsFailed = true;
if (prevBrickCol != ballBrickCol) {
if (isBrickAtColRow(prevBrickCol, ballBrickRow) == false) {
ballSpeedX = -ballSpeedX;
bothTestsFailed = false;
}
}
if (prevBrickRow != ballBrickRow) {
if (isBrickAtColRow(ballBrickCol, prevBrickRow) == false) {
ballSpeedY = -ballSpeedY;
bothTestsFailed = false;
}
}
if (bothTestsFailed) {
ballSpeedX = -ballSpeedX;
ballSpeedY = -ballSpeedY;
}
}
}
}
function ballPaddleHandling () {
var paddleTopEdgeY = canvas.height - distanceBP;
var paddleBottomEdgeY = paddleTopEdgeY + paddleH;
var paddleLeftEdgeX = paddleX;
var paddleRightEdgeX = paddleLeftEdgeX + paddleW;
if (ballY + ballR > paddleTopEdgeY &&
ballY - ballR < paddleBottomEdgeY &&
ballX + ballR > paddleLeftEdgeX &&
ballX - ballR < paddleRightEdgeX
) {
ballSpeedY *= -1;
var centerOfPaddleX = paddleX + paddleW / 2;
var ballDistFromPadlleCenterX = ballX - centerOfPaddleX;
ballSpeedX = ballDistFromPadlleCenterX * 0.2;
if (bricksLeft == 0) {
brickReset();
}
}
}
function moveAll () {
//console.log("X: " + ballSpeedX,"Y: " + ballSpeedY);
ballMove();
ballBrickHandling();
ballPaddleHandling();
}
function rowColToArrayIndex (col, row) {
return col + brickCols * row;
}
// Random COLOR
var brickColors = [];
function randBrickColor () {
for (var eachRow = 0; eachRow < brickRows; eachRow++) {
brickColors[eachRow] = [];
for (var eachCol = 0; eachCol < brickCols; eachCol++) {
if (Math.random() > 0.7) {
brickColors[eachRow][eachCol] = "red";
} else {
brickColors[eachRow][eachCol] = "blue";
}
}
}
}
//end of Random COLOR
function drawBricks () {
for (var eachRow = 0; eachRow < brickRows; eachRow++) {
for (var eachCol = 0; eachCol < brickCols; eachCol++) {
var arrayIndex = brickCols * eachRow + eachCol;
if (brickGrid[arrayIndex]) {
colorRect(brickW * eachCol, brickH * eachRow, brickW-brickGap, brickH-brickGap, brickColors[eachRow][eachCol]);
}
}
}
}
function drawAll () {
// Black Screen
colorRect(0,0, canvas.width, canvas.height, "black");
// Ball
colorCircle(ballX,ballY, ballR, "white");
// Paddle
colorRect(paddleX, paddleY - distanceBP, paddleW, paddleH, "white");
// Bricks
drawBricks();
// Position of the mouse
colorText(mouseX+","+mouseY, mouseX,mouseY, "yellow");
}
function colorRect (topLeftX, topLeftY, boxWidth, boxHeight, fillColor) {
canvasContext.fillStyle = fillColor;
canvasContext.fillRect(topLeftX, topLeftY, boxWidth, boxHeight);
}
function colorCircle(centerX, centerY, radius, fillColor) {
canvasContext.fillStyle = fillColor;
canvasContext.beginPath();
canvasContext.arc(centerX, centerY, radius, 0, Math.PI * 2, true);
canvasContext.fill();
}
function colorText(showWords, textX, textY, fillColor) {
canvasContext.fillStyle = fillColor;
canvasContext.fillText(showWords, textX, textY);
}
</script>
</body>
</html>
in ballBrickHandling() replace
brickGrid[brickIndexUnderBall] = false;
bricksLeft--;
with
if(brickColors[ballBrickRow][ballBrickCol] != "red") {
brickGrid[brickIndexUnderBall] = false;
bricksLeft--;
} else brickColors[ballBrickRow][ballBrickCol] = "blue"

forEach inside Ticker makes Canvas slow

I'm creating a HTML5 minigame that uses collision-detection and I've recently discovered that it has a speed problem:
I think the reason of this problem is that...
Inside the 60fps ticker there are two forEach loops, one for the rects array and other for the lasers array. So, when there's 5 rects and 5 lasers in the canvas, it'll loop 5 times in the first forEach and five times in the second at each frame, and each forEach function has lots of ifs in it, making the game slow. How can I change that to something less CPU-intensive?
If you know a bigger speed problem in this minigame, feel free to help me to solve it too.
Here's my entire code:
I highly recommend you to see the JSFiddle instead of the code below, since there's more than 400 lines.
<!DOCTYPE html>
<html>
<head>
<title>VelJS α</title>
<!-- This app was coded by Tiago Marinho -->
<!-- Do not leech it! -->
<link rel="shortcut icon" href="http://i.imgur.com/Jja8mvg.png">
<!-- EaselJS: -->
<script src="http://static.tumblr.com/uzcr0ts/uzIn1l1v2/easeljs-0.7.1.min.js"></script>
<script src="http://pastebin.com/raw.php?i=W4S2mtCp"></script>
<!-- jQuery: -->
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script>
(function () {
// Primary vars (stage, circle, rects):
var stage,
circle, // Hero!
rects = [], // Platforms
lasers = [];
// Velocity vars:
var xvel = 0, // X Velocity
yvel = 0, // Y Velocity
xvelpast = 0,
yvelpast = 0;
// Keyvars (up, left, right, down):
var up = false, // W or arrow up
left = false, // A or arrow left
right = false, // D or arrow right
down = false; // S or arrow down
// Other vars (maxvel, col, pause):
var maxvel = 256, // Maximum velocity
col = false, // Collision detection helper (returns true if collided side-to-side)
pause = false;
// Volatility vars (rmdir, pastrmdir):
var rmdir = 0,
pastrmdir = 0;
// Main part (aka creating stage, creating circle, etc):
function init() {
stage = new createjs.Stage("canvas");
// Creating circle:
var circle = new createjs.Shape();
circle.radius = 11;
circle.graphics.beginFill("#fff").beginStroke("white").drawCircle(circle.radius - 0.5, circle.radius - 0.5, circle.radius);
circle.width = circle.radius * 2;
circle.height = circle.radius * 2;
stage.addChild(circle);
setTimeout(function () {
// newobj(W, H, X, Y)
newobj("laser", 3, 244, stage.canvas.width / 2 - 125, stage.canvas.height / 4 * 3 - 247);
newobj("rect", 125, 3, stage.canvas.width / 2 - 125, stage.canvas.height / 4 * 3 - 250);
}, 250); // Wait until first tick finishes and stage is resized to 100%, then calculate the middle of canvas.
// User Input (Redirect input to Input Handler):
// Keydown:
document.addEventListener("keydown", function (evt) {
if (evt.keyCode == 87 || evt.keyCode == 38) { // up
up = true;
}
if (evt.keyCode == 65 || evt.keyCode == 37) { // left
left = true;
}
if (evt.keyCode == 68 || evt.keyCode == 39) { // right
right = true;
}
if (evt.keyCode == 83 || evt.keyCode == 40) { // down
down = true;
}
if (evt.keyCode == 8 || evt.keyCode == 80) { // del/p
if (pause == false) {
xvelpast = xvel;
yvelpast = yvel;
pause = true;
var fadestep = 0;
for (var i = 1; i > 0; i -= 0.1) {
i = parseFloat(i.toFixed(1));
fadestep++;
fadeFill("circle", i, fadestep);
rects.forEach(function (rect) {
fadeFill("rect", i, fadestep);
});
}
} else {
pause = false;
xvel = xvelpast;
yvel = yvelpast;
var fadestep = 0;
for (var i = 0; i <= 1; i += 0.1) {
i = parseFloat(i.toFixed(1));
fadestep++;
fadeFill("circle", i, fadestep);
rects.forEach(function (rect) {
fadeFill("rect", i, fadestep);
});
}
}
}
});
// Keyup:
document.addEventListener("keyup", function (evt) {
if (evt.keyCode == 87 || evt.keyCode == 38) { // up
up = false;
}
if (evt.keyCode == 65 || evt.keyCode == 37) { // left
left = false;
}
if (evt.keyCode == 68 || evt.keyCode == 39) { // right
right = false;
}
if (evt.keyCode == 83 || evt.keyCode == 40) { // down
down = false;
}
});
// Functions:
// Fade beginFill to a lower alpha:
function fadeFill(obj, i, t) {
setTimeout(function () {
if (obj == "circle") {
circle.graphics.clear().beginFill("rgba(255,255,255," + i + ")").beginStroke("white").drawCircle(circle.radius, circle.radius, circle.radius).endFill();
}
if (obj == "rect") {
for (var r = 0; r < rects.length; r++) {
rects[r].graphics.clear().beginFill("rgba(255,255,255," + i + ")").beginStroke("white").drawRect(0, 0, rects[r].width, rects[r].height).endFill();
}
}
}, t * 20);
};
// To create new rects:
function newobj(type, w, h, x, y) {
if (type == "rect") {
var rect = new createjs.Shape();
rect.graphics.beginFill("#fff").beginStroke("white").drawRect(0, 0, w, h);
rect.width = w + 1;
rect.height = h + 1;
rect.y = Math.round(y) + 0.5;
rect.x = Math.round(x) + 0.5;
stage.addChild(rect);
rects.push(rect);
}
if (type == "laser") {
var laser = new createjs.Shape();
if (w >= h) {
laser.graphics.beginFill("#c22").drawRect(0, 0, w, 1);
laser.width = w;
laser.height = 1;
} else {
laser.graphics.beginFill("#c22").drawRect(0, 0, 1, h);
laser.width = 1;
laser.height = h;
}
laser.shadow = new createjs.Shadow("#ff0000", 0, 0, 5);
laser.y = Math.round(y);
laser.x = Math.round(x);
stage.addChild(laser);
lasers.push(laser);
}
}
// Collision recoil:
function cls(clsdir) {
if (clsdir == "top") {
if (yvel <= 4) {
yvel = 0;
} else {
yvel = Math.round(yvel * -0.5);
}
}
if (clsdir == "left") {
if (xvel <= 4) {
xvel = 0;
} else {
xvel = Math.round(xvel * -0.5);
}
}
if (clsdir == "right") {
if (xvel >= -4) {
xvel = 0;
} else {
xvel = Math.round(xvel * -0.5);
}
}
if (clsdir == "bottom") {
if (yvel >= -4) {
yvel = 0;
} else {
yvel = Math.round(yvel * -0.5);
}
}
col = true;
}
// Die:
function die() {
circle.alpha = 1;
createjs.Tween.get(circle).to({
alpha: 0
}, 250).call(handleComplete);
function handleComplete() {
circle.x = stage.canvas.width / 2 - circle.radius;
circle.y = stage.canvas.height / 2 - circle.radius;
createjs.Tween.get(circle).to({
alpha: 1
}, 250);
yvel = 0;
xvel = 0;
yvelpast = 0;
xvelpast = 0;
}
yvel = yvel/2;
xvel = xvel/2;
}
// Set Intervals:
// Speed/Score:
setInterval(function () {
if (pause == false) {
speed = Math.abs(xvel) + Math.abs(yvel);
$(".speed").html("Speed: " + speed);
} else {
speed = Math.abs(xvelpast) + Math.abs(yvelpast);
$(".speed").html("Speed: " + speed + " (Paused)");
}
}, 175);
// Tick:
createjs.Ticker.on("tick", tick);
createjs.Ticker.setFPS(60);
function tick(event) {
// Input Handler:
if (up == true) {
yvel -= 2;
} else {
if (yvel < 0) {
yvel++;
}
}
if (left == true) {
xvel -= 2;
} else {
if (xvel < 0) {
xvel++;
}
}
if (right == true) {
xvel += 2;
} else {
if (xvel > 0) {
xvel--;
}
}
if (down == true) {
yvel += 2;
} else {
if (yvel > 0) {
yvel--;
}
}
// Volatility:
pastrmdir = rmdir;
rmdir = Math.floor((Math.random() * 20) + 1);
if (rmdir == 1 && pastrmdir != 4) {
yvel--;
}
if (rmdir == 2 && pastrmdir != 3) {
xvel--;
}
if (rmdir == 3 && pastrmdir != 2) {
xvel++;
}
if (rmdir == 4 && pastrmdir != 1) {
yvel++;
}
// Velocity limiter:
if (xvel > maxvel || xvel < maxvel * -1) {
(xvel > 0) ? xvel = maxvel : xvel = maxvel * -1;
}
if (yvel > maxvel || yvel < maxvel * -1) {
(yvel > 0) ? yvel = maxvel : yvel = maxvel * -1;
}
// Collision handler:
// xvel and yvel modifications must be before this!
rects.forEach(function (rect) { // Affect all rects
// Collision detection:
// (This MUST BE after every change in xvel/yvel)
// Next circle position calculation:
nextposx = circle.x + event.delta / 1000 * xvel * 30,
nextposy = circle.y + event.delta / 1000 * yvel * 30;
// Collision between objects (Rect and Circle):
if (nextposy + circle.height > rect.y && circle.y + circle.height < rect.y && circle.x + circle.width > rect.x && circle.x < rect.x + rect.width) {
cls("top");
}
if (nextposx + circle.width > rect.x && circle.x + circle.width < rect.x && circle.y + circle.height > rect.y && circle.y < rect.y + rect.height) {
cls("left");
}
if (nextposx < rect.x + rect.width && circle.x > rect.x + rect.width && circle.y + circle.height > rect.y && circle.y < rect.y + rect.height) {
cls("right");
}
if (nextposy < rect.y + rect.height && circle.y > rect.y + rect.height && circle.x + circle.width > rect.x && circle.x < rect.x + rect.width) {
cls("bottom");
}
rects.forEach(function (rect) {
// Check side-to-side collisions with other rects:
if (nextposy + circle.height > rect.y && circle.y + circle.height < rect.y && circle.x + circle.width > rect.x && circle.x < rect.x + rect.width) {
col = true;
}
if (nextposx + circle.width > rect.x && circle.x + circle.width < rect.x && circle.y + circle.height > rect.y && circle.y < rect.y + rect.height) {
col = true;
}
if (nextposx < rect.x + rect.width && circle.x > rect.x + rect.width && circle.y + circle.height > rect.y && circle.y < rect.y + rect.height) {
col = true;
}
if (nextposy < rect.y + rect.height && circle.y > rect.y + rect.height && circle.x + circle.width > rect.x && circle.x < rect.x + rect.width) {
col = true;
}
});
// Edge-to-edge collision between objects (Rect and Circle) - Note that this will not occur if a side-to-side collision occurred in the current frame!:
if (nextposy + circle.height > rect.y &&
nextposx + circle.width > rect.x &&
nextposx < rect.x + rect.width &&
nextposy < rect.y + rect.height &&
col == false) {
if (circle.y + circle.height < rect.y &&
circle.x + circle.width < rect.x) {
cls("top");
cls("left");
}
if (circle.y > rect.y + rect.height &&
circle.x + circle.width < rect.x) {
cls("bottom");
cls("left");
}
if (circle.y + circle.height < rect.y &&
circle.x > rect.x + rect.width) {
cls("top");
cls("right");
}
if (circle.y > rect.y + rect.height &&
circle.x > rect.x + rect.width) {
cls("bottom");
cls("right");
}
}
col = false;
// Stage collision:
if (nextposy < 0) { // Collided with TOP of stage. Trust me.
cls("bottom"); // Inverted clsdir is proposital!
}
if (nextposx < 0) {
cls("right");
}
if (nextposx + circle.width > stage.canvas.width) {
cls("left");
}
if (nextposy + circle.height > stage.canvas.height) {
cls("top");
}
});
// Laser collision handler:
lasers.forEach(function (laser) {
laser.alpha = Math.random() + 0.5;
nextposx = circle.x + event.delta / 1000 * xvel * 30,
nextposy = circle.y + event.delta / 1000 * yvel * 30;
if (nextposy + circle.height > laser.y && circle.y + circle.height < laser.y && circle.x + circle.width > laser.x && circle.x < laser.x + laser.width) {
circle.y = laser.y-circle.height;
die();
}
if (nextposx + circle.width > laser.x && circle.x + circle.width < laser.x && circle.y + circle.height > laser.y && circle.y < laser.y + laser.height) {
circle.x = laser.x-circle.width;
die();
}
if (nextposx < laser.x + laser.width && circle.x > laser.x + laser.width && circle.y + circle.height > laser.y && circle.y < laser.y + laser.height) {
circle.x = laser.x+laser.width;
die();
}
if (nextposy < laser.y + laser.height && circle.y > laser.y + laser.height && circle.x + circle.width > laser.x && circle.x < laser.x + laser.width) {
circle.y = laser.y+laser.height;
die();
}
});
// Velocity:
if (pause == true) {
xvel = 0;
yvel = 0;
}
circle.x += event.delta / 1000 * xvel * 20;
circle.y += event.delta / 1000 * yvel * 20;
// Stage.canvas 100% width and height:
stage.canvas.width = window.innerWidth;
stage.canvas.height = window.innerHeight;
// Update stage:
stage.update(event);
}
setTimeout(function () {
// Centre circle:
circle.x = stage.canvas.width / 2 - circle.radius;
circle.y = stage.canvas.height / 2 - circle.radius;
// Fade-in after loading:
$(".speed").css({
opacity: 1
});
$("canvas").css({
opacity: 1
});
}, 500);
}
$(function () {
init();
});
})();
</script>
<style>
* {
margin: 0;
}
html,
body {
-webkit-font-smoothing: antialiased;
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
font-weight: 300;
color: #fff;
background-color: #181818
}
.build {
position: absolute;
bottom: 5px;
right: 5px;
color: rgba(255, 255, 255, 0.05)
}
canvas {
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
filter: alpha(opacity=0);
opacity: 0;
position: absolute;
top: 0;
left: 0;
-moz-transition: 5s ease;
-o-transition: 5s ease;
-webkit-transition: 5s ease;
transition: 5s ease
}
.speed {
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
filter: alpha(opacity=0);
opacity: 0;
position: absolute;
top: 5px;
left: 5px;
color: #fff;
font-size: 16px;
-moz-transition: 5s ease;
-o-transition: 5s ease;
-webkit-transition: 5s ease;
transition: 5s ease
}
h2 {
text-align: center;
font-size: 22px;
font-weight: 700
}
p {
font-size: 16px;
margin: 0
}
</style>
</head>
<body>
<p class="speed"></p>
<p class="build">α256</p>
<canvas id="canvas">
<h2>Your browser doesn't support Canvas.</h2>
<p>Switch to <b>Chrome 33</b>, <b>Firefox 27</b> or <b>Safari 7</b>.</p>
</canvas>
</body>
</html>
JSFiddle
The game logic isn't really a problem, the reason it's slow is because you "create" a new canvas every tick by setting the width and height:
stage.canvas.width = window.innerWidth;
stage.canvas.height = window.innerHeight;
So, even if you set the canvas width and height to the same values they had, under the hood pretty much a new canvas is constructed. If you remove the lines above from the game loop it should run smoothly.
Just set the canvas width and height once and then listen for window resize and set it when the browser window changes size.
Running logic in a tick can be expensive, as is updating the canvas each frame. If you can, a lower framerate could be advisable - since it often isn't necessary to run at 60fps. If you want to keep refreshing the canvas at that rate, and you have any particularly expensive functions, such as pathfinding, collision, etc - you could always decouple that from your update loop, so it isn't running quite as often.
Note that stage updating can be very expensive, especially with vectors. If you can, find a solution that lets you cache vector content as bitmaps, and update the vector as little as possible. In your case, since you are just fading the fill - you might separate the fill from the outline, cache them separately, and then use alpha on the fill object. If you have a lot of shapes, you can reuse the caches between them, and you will see huge performance gains.
Best of luck!

Categories

Resources