fillRect not working when I have a lot of canvas elements - javascript

I have a Vue app where I want to perform fillRect on 10000 canvas elements.
The code does work but it breaks when it has a big number sometimes.
What can I do about this?
for (let key = 1; key <= 10000; key++) {
let card = document.getElementById(key);
if (this.scratched.scratchedValue.includes(key) == false) {
let cardCanvas = card.getContext('2d');
cardCanvas.beginPath();
cardCanvas.fillStyle = "#b6b6b4";
cardCanvas.fillRect(0, 0, card.width, card.height);
card.style.opacity = '1';
} else {
card.style.opacity = '1';
card.style.outline = '2px solid #ef7200';
}
}
How it looks when it works
How it looks when it isn't working

Related

Is there a way to select elements of an array every second?

What I'm trying to achieve is to loop and toggle a class on every individual div every second.
Below is the code to create 4 divs with colour from an array.
const container = document.querySelector(".container");
const colors = ["#FDB10B", "#FE8535", "#FD292F", "#B20000"];
const ball = container.querySelectorAll("div");
for (let i = 0; i < colors.length; i++) {
const balls = document.createElement("div");
balls.style.backgroundColor = colors[i];
container.appendChild(balls);
}
Now I want to loop through the containers element every second and Google a class on the elements.
I tried using the setInterval() method
I've added a class "ball" you could as well refer to element by container > div, but easier to understand this way.
const container = document.querySelector(".container");
const colors = ["#FDB10B", "#FE8535", "#FD292F", "#B20000"];
const ball = container.querySelectorAll("div");
for (let i = 0; i < colors.length; i++) {
const balls = document.createElement("div");
balls.style.backgroundColor = colors[i];
balls.classList.add('ball');
container.appendChild(balls);
}
setInterval(function() {
toggle();
}, 1000)
function toggle() {
const tog = document.querySelector('.class-to-toggle');
if (tog) {
tog.classList.remove('class-to-toggle');
const nextTog = tog.nextElementSibling;
if (nextTog) {
nextTog.classList.add('class-to-toggle');
} else {
document.querySelector('.ball').classList.add('class-to-toggle');
}
} else {
document.querySelector('.ball').classList.add('class-to-toggle');
}
}
<div class="container">
</div>
if you inspect in dev tools you'll see class changed
Here is another example of the way you could do this.
Your question lacks some details about what you need to do so the example is quite generic.
const container = document.querySelector(".container");
const colors = ["#FDB10B", "#FE8535", "#FD292F", "#B20000"];
colors.forEach(color => {
const ball = document.createElement("div");
ball.classList.add("ball");
// Added some stylnig value for the example
ball.style.width = "20px";
ball.style.height = "20px";
ball.style.backgroundColor = color;
container.appendChild(ball);
});
const balls = container.querySelectorAll(".ball");
let cursor = 0;
const int_id = setInterval(selectNextBall, 1000);
function selectNextBall() {
const selectedBall = balls[cursor];
// I do this just to illustrate the example,
// could be anything you need to do with your ball.
for (const ball of balls) {
ball.style.border = ball === selectedBall ? "1px solid black" : "none";
}
// for an endless loop
cursor = cursor === balls.length - 1 ? 0 : cursor + 1;
// Or if the loop need to iterate just once
/*
if (cursor === balls.length - 1) {
clearInterval(int_id);
}
cursor++
*/
}
<div class="container">
</div>

How do you blur a single object in Matter.js?

I'm trying to create a fire effect for a game in Matter.js, and I need to blur a circle to make it look more realistic. However, I need to make it so it only blurs the fire, not the whole canvas. How can I do this?
This is the code I have so far:
function setOnFire(object) {
var fireX = object.position.x;
var fireY = object.position.y;
var fire = Bodies.circle(fireX, fireY, vw*1, {
isStatic: true,
render: {
fillStyle: "rgba(255,130,0,1)"
}
});
World.add(world, fire);
}
This is not exactly what I had in mind, but it is as close as you can get.
Start by going to matter.js and go to this section:
for (k = body.parts.length > 1 ? 1 : 0; k < body.parts.length; k++) {
part = body.parts[k];
if (!part.render.visible)
continue;
Add this code after the continue;:
if (body.bloom) {
c.shadowColor = body.render.fillStyle;
c.shadowOffsetX = 0;
c.shadowOffsetY = 0;
c.shadowBlur = body.bloom;
}
Then, go to the very end of the loop and add this:
if (body.bloom) {
c.shadowColor = "transparent";
c.shadowOffsetX = 0;
c.shadowOffsetY = 0;
c.shadowBlur = 0;
}
Then, just add the bloom while making your body. For instance:
let fireParticle = Bodies.circle(0, 0, {
bloom: 25
});

how to prevent overlapping of 2 components

how to prevent overlapping of 2 components, please help me to make them follow Mouse but not overlap. i am not expert in coding, please explain in simple language.
function component(x,y,r) {
var randomcolor = ["violet","indigo","blue","green","yellow","orange","red"];
this.pos=createVector(x,y);
this.r=r;
this.color=randomcolor[Math.floor(Math.random()*randomcolor.length)];
this.show=function() {
fill(this.color);
stroke(241,241,241);
ellipse(this.pos.x,this.pos.y,this.r*2,this.r*2);
}
this.crash = function(other) {
var d = p5.Vector.dist(this.pos,other.pos);
if (d<this.r+other.r) {
this.r+=other.r/20;
return true;}
}
this.move=function(){
this.pos.x=lerp(this.pos.x,mouseX,0.1);
this.pos.y=lerp(this.pos.y,mouseY,0.1);
this.pos.x = constrain(this.pos.x,this.r,width-this.r)
this.pos.y = constrain(this.pos.y,this.r,height-this.r)
}
}
To make multiple objects move without running into each other you will need to
keep track of the current location of all objects
be able to identify each object so that the collision method does not detect a collision of an object with itself
check to make sure there will not be a collision before attempting to move an object
For your example code this is one possibility for making multiple components move towards the mouse without running into each other. I rewrote your crash function and added some global variables. This is not elegant but I think it answers your question in a way that you can understand how this kind of problem can be approached.
var ids = 0;
var allComponents = [];
function setup(){
createCanvas(600,600);
new component(10,10,10);
new component(590,10,10);
}
function draw(){
background(255);
for (let i = 0; i < allComponents.length; i++){
allComponents[i].show();
allComponents[i].move();
}
}
function component(x,y,r) {
var randomcolor = ["violet","indigo","blue","green","yellow","orange","red"];
this.pos=createVector(x,y);
this.r=r;
this.id = ids++;
allComponents[allComponents.length] = this;
this.color=randomcolor[Math.floor(Math.random()*randomcolor.length)];
this.show=function() {
fill(this.color);
stroke(241,241,241);
ellipse(this.pos.x,this.pos.y,this.r*2,this.r*2);
}
this.crash = function(other) {
var d = p5.Vector.dist(this.pos,other.pos);
if (d< this.r + other.r) {
return true;
}
return false;
}
this.move=function(){
let originalX = this.pos.x;
let originalY = this.pos.y;
this.pos.x=lerp(this.pos.x,mouseX,0.1);
this.pos.y=lerp(this.pos.y,mouseY,0.1);
this.pos.x = constrain(this.pos.x,this.r,width-this.r);
this.pos.y = constrain(this.pos.y,this.r,height-this.r);
for (let i = 0; i < allComponents.length; i++){
let other = allComponents[i];
if (this.id !== other.id && this.crash(other)){
this.pos.x = originalX;
this.pos.y = originalY;
break;
}
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>

Can I add class to dynamically created two.js element?

I am using the code showed below to create 46 small circles within a wrapper (div) draw-shapes;
let elem = document.getElementById('draw-shapes');
let params = { width: 1024, height: 768 };
let two = new Two(params).appendTo(elem);
for (let i = 1; i < 47; i++) {
circle = two.makeCircle(x, y, radius);
circle.fill = 'green';
circle.stroke = 'white';
circle.linewidth = 1;
circle.id = i;
}
All drawings are made with the Two.js library. I read in the documentation I can change the id of the created element, but I also need to assign a class to each element. I have tried everything from pure js setAttribute to jQuery .attr and .addClass methods, but none of them worked, so I started to wonder if this is even possible to do? If someone knows a way, please let me know how. Thank.
There is not internal utility or property to get to the DOM node of each Two element.
But the id you specify is indeed added as two-<specified-id> to the actual node.
So you can use the normal document.getElementById.
So in your case
let elem = document.getElementById('draw-shapes');
let params = {
width: 300,
height: 300
};
let two = new Two(params).appendTo(elem);
for (let i = 1; i < 20; i++) {
const circle = two.makeCircle(i * 10, i * 10, 40);
circle.fill = 'green';
circle.stroke = 'white';
circle.linewidth = 1;
circle.id = `two-circle-${i}`;
}
two.update();
// add classname to every fifth element;
for (let i = 1; i < 20; i += 5) {
const circleNode = document.getElementById(`two-circle-${i}`);
circleNode.classList.add('classname');
circleNode.addEventListener('mouseover', function() {
const path = two.scene.getById(this.id)
path.scale = 1.2;
two.update();
});
circleNode.addEventListener('mouseout', function() {
const path = two.scene.getById(this.id)
path.scale = 1;
two.update();
});
}
.classname {
stroke-width: 5;
stroke: red;
fill:yellow;
cursor:pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/two.js/0.6.0/two.js"></script>
<div id="draw-shapes"></div>

My javascript canvas map script and poor performance

Basically below is my script for a prototype which uses 128x128 tiles to draw a map on a canvas which user can drag to move around.
Script does work. However I have a few problems to be solved:
1. Poor performance and I can't figure out why.
2. I am missing a method to buffer the tiles before the actual drawing.
3. If you notice any other issues also that could help me to make things run more smoothly it would be fantastic.
Some explanations for the script:
variables
coordinates - Defines the actual images to be displayed. Image file names are type of '0_1.jpg', where 0 is Y and 1 is X.
mouse_position - As name says, is keeping record of mouse position.
position - This is a poorly named variable. It defines the position of the context drawn on canvas. This changes when user drags the view.
Any assistance would be appreciated greatly. Thank you.
var coordinates = [0, 0];
var mouse_position = [0, 0];
var position = [-128, -128];
var canvas = document.getElementById('map_canvas');
var context = canvas.getContext('2d');
var buffer = [];
var buffer_x = Math.floor(window.innerWidth/128)+4;
var buffer_y = Math.floor(window.innerHeight/128)+4;
var animation_frame_request = function() {
var a = window.requestAnimationFrame;
var b = window.webkitRequestAnimationFrame;
var c = window.mozRequestAnimationFrame;
var d = function(callback) {
window.setTimeout(callback, 1000/60);
}
return a || b || c || d;
}
var resizeCanvas = function() {
window.canvas.width = window.innerWidth;
window.canvas.height = window.innerHeight;
window.buffer_x = Math.floor(window.innerWidth/128)+4;
window.buffer_y = Math.floor(window.innerHeight/128)+4;
window.buffer = [];
for (row = 0; row < window.buffer_y; row++) {
x = [];
for (col = 0; col < window.buffer_x; col++) {
x.push(new Image());
}
window.buffer.push(x);
}
}
var render = function() {
animation_frame_request(render);
for (row = 0; row < window.buffer_y; row++) {
for (col = 0; col < window.buffer_x; col++) {
cy = window.coordinates[1]+row;
cx = window.coordinates[0]+col;
window.buffer[row][col].src = 'map/'+cy+'_'+cx+'.jpg';
}
}
for (row = 0; row < window.buffer_y; row++) {
for (col = 0; col < window.buffer_x; col++) {
window.context.drawImage(window.buffer[row][col],
window.position[0]+col*128,
window.position[1]+row*128, 128, 128);
}
}
}
var events = function() {
window.canvas.onmousemove = function(e) {
if (e['buttons'] == 1) {
window.position[0] += (e.clientX-window.mouse_position[0]);
window.position[1] += (e.clientY-window.mouse_position[1]);
if (window.position[0] >= 0) {
window.position[0] = -128;
window.coordinates[0] -= 1;
} else if (window.position[0] < -128) {
window.position[0] = 0;
window.coordinates[0] += 1;
}
if (window.position[1] >= 0) {
window.position[1] = -128;
window.coordinates[1] -= 1;
} else if (window.position[1] < -128) {
window.position[1] = 0;
window.coordinates[1] += 1;
}
render();
}
window.mouse_position[0] = e.clientX;
window.mouse_position[1] = e.clientY;
}
}
window.addEventListener('resize', resizeCanvas, false);
window.addEventListener('load', resizeCanvas, false);
window.addEventListener('mousemove', events, false);
resizeCanvas();
To get better performance you should avoid changing the src of img nodes and move them around instead.
A simple way to minimize the number of img nodes handled and modified (except for screen positioning) is to use an LRU (Least Recently Used) cache.
Basically you keep a cache of last say 100 image nodes (they must be enough to cover at least one screen) by using a dictionary mapping the src url to a node object and also keeping them all in a doubly-linked list.
When a tile is required you first check in the cache, and if it's already there just move it to the front of LRU list and move the img coordinates, otherwise create a new node and set the source or, if you already hit the cache limit, reuse the last node in the doubly-linked list instead. In code:
function setTile(x, y, src) {
var t = cache[src];
if (!t) {
if (cache_count == MAXCACHE) {
t = lru_last;
t.prev.next = null;
lru_last = t.prev;
t.prev = t.next = null;
delete cache[t.src]
t.src = src;
t.img.src = src;
cache[t.src] = t;
} else {
t = { prev: null,
next: null,
img: document.createElement("img") };
t.src = src;
t.img.src = src;
t.img.className = "tile";
scr.appendChild(t.img);
cache[t.src] = t;
cache_count += 1;
}
} else {
if (t.prev) t.prev.next = t.next; else lru_first = t.next;
if (t.next) t.next.prev = t.prev; else lru_last = t.prev;
}
t.prev = null; t.next = lru_first;
if (t.next) t.next.prev = t; else lru_last = t;
lru_first = t;
t.img.style.left = x + "px";
t.img.style.top = y + "px";
scr.appendChild(t.img);
}
I'm also always appending the requested tile to the container so that it goes in front of all other existing tiles; this way I don't need to remove old tiles and they're simply left behind.
To update the screen I just iterate over all the tiles I need and request them:
function setView(x0, y0) {
var w = scr.offsetWidth;
var h = scr.offsetHeight;
var iy0 = y0 >> 7;
var ix0 = x0 >> 7;
for (var y=iy0; y*128 < y0+h; y++) {
for (var x=ix0; x*128 < x0+w; x++) {
setTile(x*128-x0, y*128-y0, "tile_" + y + "_" + x + ".jpg");
}
}
}
most of the time the setTile request will just update the x and y coordinates of an existing img tag, without changing anything else. At the same time no more than MAXCACHE image nodes will be present on the screen.
You can see a full working example in
http://raksy.dyndns.org/tiles/tiles.html

Categories

Resources