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

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>

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>

JQuery - How can I check my cards in memory game?

I have a course where I learn JS+JQuery and I have to write a memory game. I want to check if the turned cards are in the same classes so I can delete them and count the points, but I just cannot do it. I tried a lot of ways but I cannot figure it out.
I tried with .is(), === but nothing...
Here's my code:
let gameArea;
let size = 6;
let card_size = 600 / size;
let images = ['arbalest', 'armored', 'arms', 'cannoner', 'cataphract', 'cavalier', 'centurion', 'champion', 'composite',
'conquistador', 'eagle', 'heavy', 'heavyhorse', 'hussar', 'knight', 'legion', 'paladin', 'skirmisher'
];
$(function() {
gameArea = $('<div></div>');
gameArea.appendTo('body');
gameArea.attr('id', 'gameArea');
drawMap();
gameArea.on("click", "div", function(e) {
if ($(e.target).is(".unturned")) {
$(e.target).removeClass("unturned");
}
let unturnedCardNum = $("#gameArea").find("div:not(.unturned)").length;
if (unturnedCardNum === 3) {
$("#gameArea").find("div:not(.unturned)").addClass("unturned");
}
//I'd like to check here
});
});
function generate() {
let generatedImage = images[Math.floor(Math.random() * images.length)];
if ($("#gameArea").find(`.${generatedImage}`).length === 2) {
images = images.filter(function(e) {
return e !== generatedImage
});
generatedImage = generate();
}
return generatedImage;
}
function drawMap() {
for (let i = 0; i < size; i++) {
for (let j = 0; j < size; j++) {
let pic = $('<div></div>');
let shuffledImages = generate();
pic.addClass(shuffledImages);
pic.addClass("unturned");
pic.css({
width: card_size,
height: card_size,
top: i * card_size,
left: j * card_size
});
pic.appendTo(gameArea);
}
}
}
Assuming that the string in the images array will have been assigned to the associated card as a class name and that, after removing the "unturned" class, this will be the only class name left then maybe the following might help you:
Inside the $("#gameArea").on("click"...) function replace the lines
let unturnedCardNum = $("#gameArea").find("div:not(.unturned)").length;
if (unturnedCardNum === 3) {
$("#gameArea").find("div:not(.unturned)").addClass("unturned");
}
with
let unturnedCards = $("#gameArea").find("div:not(.unturned)");
switch(unturnedCards.length){
case 3:
unturnedCards.addClass("unturned");
break;
case 2:
if (unturnedCards[0].className===
unturnedCards[1].className){
console.log("you found a pair!");
// score[currentPlayer]++;
}
}
Further hint:
Instead of calling the generate() function recursively, you could simply shuffle an array made up of two copies of the images array by using a Fisher-Yates algorithm:
function shuffle(a,n){ // shuffle array a in place (Fisher-Yates)
let m=a.length;
n=n||m-1;
for(let i=0,j;i<n;i++){
j=Math.floor(Math.random()*(m-i)+i);
if (j-i) [ a[i],a[j] ] = [ a[j],a[i] ]; // swap 2 array elements
}
}

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
});

Form element Iteration, in Javascript

I've been working on a code snippet for a template module that I have been creating and I've hit a wall so to speak. I'm trying to loop through all textareas on my page, and apply formatting with some very basic validation.
Javascript is not my strongest suit but I can understand it to a point, my question is how do I collect the ID's and then use them to apply the formatting.
For example,
for each (textarea)
{
collect character restriction from html
display character restriction in a formatted manner
}
I have included my JSFiddle which I have been using to build this snippet.
I would suggest creating a prototype class for this, that can be extended to do other things aswell:
var CharWatcher = function(input){
this.max = input.getAttribute('max-length');
this.input = input;
input.onKeyDown = this.update.bind(this);
this.wrapper = document.createElement('div');
this.wrapper.innerHTML = 'Chars left: '+ (max - input.value.length);
/* style wrapper element */
/* append around input */
};
CharWatcher.prototype = {
update: function(){
this.wrapper.innerHTML = 'Chars left: ' + (this.max - this.input.value.length);
}
};
/* Somewhere else */
var textareas = document.getElementsByTagName('textarea');
for(var i = 0, l = textareas.length; i < l; i++)
new CharWatcher(textareas[i]);
I've based on #FodorZoltán's class. My class does now:
append the counter below the textarea;
position the counter in the below part of the textarea;
Yeah, I'm lazy and the code has grown up. I added some events and renamed the class name to "TextAreaRanger". It's working here:
var TextAreaRanger = function(input) {
this.MAX = parseInt(input.getAttribute('maxlength'));
this.INPUT = input;
// add input events
input["oncut"] =
input["onpaste"] =
input["onkeydown"] =
input["onkeyup"] = this.update.bind(this);
// create wrapper element
this.wrapper = document.createElement('div');
this.wrapper.innerHTML = 'Chars left: '+ (this.MAX - input.value.length);
/* input parent element */
var ipar = input.parentNode;
// find input's i
for (var i = 0, el; el = ipar.children[i]; i ++) {
if(el === input) break;
}
// append wrapper below the input
if (ipar.children[++i]) {
ipar.insertBefore(this.wrapper, ipar.children[i]);
} else ipar.appendChild(this.wrapper);
/* stylize wrapper */
this.wrapper.style.position = "relative";
this.wrapper.style.color = '#f00';
this.wrapper.style.fontSize = '11px';
this.wrapper.style.left = (input.offsetLeft + (input.offsetWidth - 100)) + "px";
this.wrapper.style.top = (-parseInt(this.wrapper.style.fontSize) * 2) + "px";
};
// Update the counter
TextAreaRanger.prototype["update"] = function() {
this.wrapper.innerHTML = 'Chars left: ' + (this.MAX - this.INPUT.value.length);
};

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