Progressively add image (arrays) - javascript

So I am creating a game where a spaceship moves and it must avoid a fireball image in order to win. My issue is that I have only one fireball looping over and over. Instead, I would like to have many fireballs, which are multiplied as time passes. I think I should need to incorporate an array and use push() method but I tried and it didn't worked. If anyone could help me, it would be very appreciated. Thanks
//Fireball script
function fireballScript(offset) {
return Math.floor(Math.random() * (window.innerWidth - offset))
}
let fireballMovement = {
x: fireballScript(fireball.offsetWidth),
y: 0
}
const fireLoop = function () {
fireballMovement.y += 1
fireball.style.top = fireballMovement.y + 'px'
if (fireballMovement.y > window.innerHeight) {
fireballMovement.x = fireballScript(fireball.offsetWidth)
fireball.style.left = fireballMovement.x + 'px'
fireballMovement.y = 0
fireball.setAttribute('hit', false)
}
}
fireball.style.left = fireballMovement.x + 'px'
let fireballSpeed = setInterval(fireLoop, 1000 / 250)
let fireball = document.querySelector("#fireball")
<img src = "Photo/fireball.png" id = "fireball" >
//Stop game on collision
function checkCollision() {
if (detectOverlap(spaceship, fireball) && fireball.getAttribute('hit') == 'false') {
hits++
fireball.setAttribute('hit', true)
alert("lost")
}
setTimeout(checkCollision, 1)
}
var detectOverlap = (function () {
function getPositions(spaceship) {
var pos = spaceship.getBoundingClientRect()
return [[pos.left, pos.right], [pos.top, pos.bottom]]
}
function comparePositions(p1, p2) {
let r1 = p1[0] < p2[0] ? p1 : p2
let r2 = p1[0] < p2[0] ? p2 : p1
return r1[1] > r2[0] || r1[0] === r2[0]
}
return function (a, b) {
var pos1 = getPositions(a),
pos2 = getPositions(b)
return comparePositions(pos1[0], pos2[0]) && comparePositions(pos1[1], pos2[1])
}
})()
let spaceship = document.querySelector("#icon")
<img src = "Photo/Spaceship1.png" id="icon">
Ps: I asked this question a dozen of times but I could never get an answer which is incorporate into my code. It would be very helpful if someone could fix this... thanks again

You need to have an array of fireballs
var fireballs = [];
Make a constructor for fireballs instead of putting them directly in the html
function fireball(x, y) {
movementX = x;
movementY = y;
}
Then push new ones into the array with dynamic position values. To add them to the document, you need to append them to a parent element. If the <body> is that parent, you'd do this:
let f = new fireball(10, 20)
fireballs.push(f)
document.body.appendChild(f)
In your update, iterate through the fireballs and update their movement.
fireballs.forEach((fireball) => {
// update position for each fireball
});

Related

Play a random song on button press using Javascript

I made some HTML code a function so that when a button is clicked it runs randomSong() which is supposed to run a random song right now it runs this:
function randomSong() {
var n = (1 + Math.random() * 2);
if (n == 0){
var song = document.getElementById('1')
console.log("0")
}else if(n == 1){
var song = document.getElementById('2');
console.log('1')}
let y = song
}
var x = y
function playAudio() {
x.play();
}
function pauseAudio() {
x.pause();
}
but it doesn't work it says x is not defined anyone now why?
Link to the website I am using this for
I tried your demo it says y is not defined because when you try to access to the outer scope of y.
you can try like so:
var x;
function randomSong() {
var n = (1 + Math.random() * 2);
var song;
if (n === 0){
song = document.getElementById('1')
console.log("0")
}
else if(n === 1){
song = document.getElementById('2');
console.log('1')
}
x = song;
}
function playAudio() {
if(x){
x.play();
}
}
function pauseAudio() {
if(x){
x.pause();
}
}
Yuu need to provide more code if this didn't work. All I did was fix some obviose mistakes. Specifically, n ==enter code here 0 changed to n==0 and var x = y; where y was not a global variable y is now global. If this didn't help add more code and I will see if I can solve it.
var y;
function randomSong() {
var n = (1 + Math.random() * 2);
if (n == 0) {
var song = document.getElementById('1')
console.log("0")
} else if (n == 1) {
var song = document.getElementById('2');
console.log('1')
}
y = song;
}
var x = y;
function playAudio() {
x.play();
}
function pauseAudio() {
x.pause();
}
let, const and class introduced identifiers are block scoped. Hence in
else if(n == 1){
var song = document.getElementById('2');
console.log('1')}
let y = song
}
var x = y
the y variable created by let y = song is not in scope when assigned to x by
var x = y
If the assignment to x proceeds without generating an error it probably means a previous definition of y, possibly with undefined value is in scope. This is a trap that automatically typing can produce:
Don't automatically precede assignments to outer scoped variables with let or const or the assignment won't take place.
let x;
function randomSong() {
const n = (1 + Math.random() * 2);
let song;
if (n === 0) {
song = '0'
console.log("0");
}else if (n === 1) {
song = '1';
console.log('1');
} else {
song = '999';
console.log('999');
}
x = song;
}
function playAudio() {
console.log(`${x} play`);
}
function pauseAudio() {
console.log(`${x} pause`);
}
randomSong();
playAudio();
pauseAudio();
Don't recommend to use var if using of var is unnecessary
i take a look at your site and i noticed when you click on Click Here For Suggesed Song it call function num(), so this show an alert with track suggested. So to turn this to what you want. when we click on button we have to call function randomSong and code with commented explain:
var song = null;
function randomSong() {
//to get audio's number dynamically.
let n = document.querySelectorAll('p audio').length;
//grab all audios from HTML Node List to an array.
let audios = [...document.querySelectorAll('p audio')];
//if there is another song singing ! we should stop it
if(song!==null) song.pause();
//we pick randomly a song inside the array of audios.
song = audios[ Math.floor ( Math.random() * n) ];
//and then we play the song, Enjoy listening!
song.play();
}
Thank you all for you answers but I found a better/eaiser way to do it the javascript is
var songList = [
'mp3_url',
'mp3_url',
'mp3_url',
'mp3_url',
'mp3_url',
];
//this gets a random url from the list
function randomChoice(choices) {
var index = Math.floor(Math.random() * choices.length);
return choices[index];
}
//this is just a place holder you can change this function to anything
function age(){
let current = new Date().getTime();
var myAge = current - 1151560800000;
var myAge = myAge/1000
return myAge;
}
/*this trys to pause x and then play a new song but if x is not defined then you
get an error so you have to do try catch finally*/
function playAudio() {
try{x.pause();}catch(err){
var old = age();
var years = old / 31536000;
console.log('Hello I was ' + old + ' seconds (' + years + ' years) old when you clicked that button!');}finally{
x = randomChoice(songList);
x = new Audio(x);
x.play();
}};
function pauseAudio() {
x.pause();
}
and the HTML is some thing like this
<button onclick="playAudio()">play a song</button>
<button onclick="pauseAudio()">pause the current song</button>

How to edit a textbox within a group in fabricjs

My fabric application requires a feature that allows the user edit textboxes within groups. I almost have it working. As it stands, this is how my application works:
Click on a group
Checks to see if you are clicking on a textbox. If yes...
Ungroups the group
Sets the focus on the textbox so that the user can make edits.
On "editing:exited," re-groups the objects
Here is a fiddle with my code:
https://jsfiddle.net/1dr8jn26/4/
Do the following to replicate my issue:
Add 2 input boxes using the button at the top
Group the input boxes using the button at the top
Click once on one of the input boxes in the group you created
Click on the canvas outside of the group
Click back on the same input box
Click on the canvas outside of the group again
Repeat 5 & 6
You will notice that the group creates duplicates of itself off to the side of the page. What I have discovered on my own:
"object.on('editing:exited... is what keeps getting run over and over.
The "duplication counter" resets if you click on one input box vs another.
When the duplicates appear, if you click on some of them, they disappear.
I am still relatively new to programming, so i'm sure this is something obvious to the experienced coders here. Any help would be appreciated!
canvas.on('mouse:down', function(options) {
if (options.target) {
var thisTarget = options.target;
var mousePos = canvas.getPointer(options.e);
if (thisTarget.isType('group')) {
var groupPos = {
x: thisTarget.left,
y: thisTarget.top
}
var currentGroup = [];
var groupItems = []
groupItems = thisTarget._objects;
thisTarget.forEachObject(function(object,i) {
currentGroup[i] = object;
currentGroup.push(object);
})
thisTarget.forEachObject(function(object,i) {
if(object.type == "textbox"){
console.log("Start for statement that finds the x and y for each
object")
var matrix = thisTarget.calcTransformMatrix()
var newPoint = fabric.util.transformPoint({y: object.top, x:
object.left}, matrix)
var objectPos = {
xStart: newPoint.x,
xEnd: newPoint.x + object.width,
yStart: newPoint.y,
yEnd: newPoint.y + object.height
}
if (mousePos.x >= objectPos.xStart && mousePos.x <=
(objectPos.xEnd)) {
if (mousePos.y >= objectPos.yStart && mousePos.y <=
objectPos.yEnd) {
function ungroup (group) {
groupItems = group._objects;
group._restoreObjectsState();
canvas.remove(group);
for (var i = 0; i < groupItems.length; i++) {
canvas.add(groupItems[i]);
}
canvas.renderAll();
};
ungroup(thisTarget)
canvas.setActiveObject(object);
object.enterEditing();
object.selectAll();
object.on('editing:exited', function (options) {
var items = [];
groupItems.forEach(function (obj) {
items.push(obj);
canvas.remove(obj);
});
console.log(JSON.stringify(groupItems))
var grp = new fabric.Group(items, {});
canvas.add(grp);
});
}
}
}
});
}
}
});
-Chris
Have been looking at the code and I think I found a solution. The group is added two times in the group groupItems. Once at the start and a second time when ungrouping the items.
Used the code below in your fiddle and that works...
canvas.on('mouse:down', function(options) {
var groupItems;
if (options.target) {
var thisTarget = options.target;
var mousePos = canvas.getPointer(options.e);
var editTextbox = false;
var editObject;
if (thisTarget.isType('group')) {
var groupPos = {
x: thisTarget.left,
y: thisTarget.top
}
thisTarget.forEachObject(function(object,i) {
if(object.type == "textbox"){
var matrix = thisTarget.calcTransformMatrix();
var newPoint = fabric.util.transformPoint({y: object.top, x: object.left}, matrix);
var objectPos = {
xStart: newPoint.x - (object.width * object.scaleX) / 2,//When OriginX and OriginY are centered, otherwise xStart: newpoint.x - object.width * object.scaleX etc...
xEnd: newPoint.x + (object.width * object.scaleX) / 2,
yStart: newPoint.y - (object.height * object.scaleY) / 2,
yEnd: newPoint.y + (object.height * object.scaleY) / 2
}
if ((mousePos.x >= objectPos.xStart && mousePos.x <= objectPos.xEnd) && (mousePos.y >= objectPos.yStart && mousePos.y <= objectPos.yEnd)) {
function ungroup (group) {
groupItems = group._objects;
group._restoreObjectsState();
canvas.remove(group);
for (var i = 0; i < groupItems.length; i++) {
if(groupItems[i] != "textbox"){
groupItems[i].selectable = false;
}
canvas.add(groupItems[i]);
}
canvas.renderAll();
};
ungroup(thisTarget);
canvas.setActiveObject(object);
object.enterEditing();
object.selectAll();
editObject = object;
var exitEditing = true;
editObject.on('editing:exited', function (options) {
if(exitEditing){
var items = [];
groupItems.forEach(function (obj) {
items.push(obj);
canvas.remove(obj);
});
var grp
grp = new fabric.Group(items, {});
canvas.add(grp);
exitEditing = false;
}
});
}
}
});
}
}
});
Here is the fiddle: https://jsfiddle.net/u3Lfnja2/1/

How to check with d3.js if element is in viewpoint (in visible area)

I'm drawing large number of elements and in many situations majority of elements are outside of view point.
I'd like to avoid processing expensive rotation transformations on hidden elements.
Here's an example:
https://blockchaingraph.org/#ipfs-QmfXtMeUdjWBPQHUNKvF3nkYR57aZz7qarW5qikEUYWJvw
Many elements in this graph are hidden (try to zoom out to see). But currently I have to render each element on every tick and it's getting painfully slow.
Here's my code:
function transformLinks(svgLinks, nodeRadius, arrowSize) {
if (svgLinks) {
var link = svgLinks.selectAll('.link').filter(needRedraw);
//console.log("total:", svgLinks.selectAll('.link').size(), ',needRedraw:', link.size());
transformLinksLines(link);
transformLinksTexts(link.selectAll('.text'));
transformLinksOutlines(link, nodeRadius, arrowSize);
transformLinksOverlays(link.selectAll('.overlay'));
link.each(function (n) {
n.source.lx = n.source.x;
n.source.ly = n.source.y;
n.target.lx = n.target.x;
n.target.ly = n.target.y;
});
}
}
function needRedraw(link) {
if (!link.source) {
link = link.parentNode;
}
return nodeMoved(link.source) || nodeMoved(link.target);
}
var minDistToRedraw = 0.8;
function nodeMoved(n) {
return utils.isNumber(n.x) && utils.isNumber(n.y)
&& !(utils.isNumber(n.lx) && Math.abs(n.x - n.lx) <= minDistToRedraw && Math.abs(n.y - n.ly) <= minDistToRedraw);
}
I'd like to extend needRedraw() function to check for visibility. For now the function just checks if either linked node moved significantly enough.
Since I didn't find any out of box solution I had to get into those coordinate conversion stuff.
First, I created function that translates external container coordinates into SVG internal clients coordianate system - containerToSVG()
Then applied it on .getBoundingClientRect(); to get visible area in SVG coordinate space.
Then in the filter checking if both nodes outsize of visible area - do not redraw link.
There are possible situations when both nodes are outsize the area, but link can still cross the area. But it's not a big concern as long as user don't see link detachments.
Here's the code:
function transformLinks(svgLinks, nodeRadius, arrowSize) {
if (svgLinks) {
var containerRect = container.node().getBoundingClientRect();
var p = containerToSVG(-nodeRadius, -nodeRadius);
var r = containerToSVG(containerRect.width + nodeRadius, containerRect.height + nodeRadius);
svgVisibleRect = {left: p.x, top: p.y, right: r.x, bottom: r.y};
minDistToRedraw = (svgVisibleRect.right - svgVisibleRect.left) / (containerRect.width + nodeRadius * 2);
var link = svgLinks.selectAll('.link').filter(needRedraw);
transformLinksLines(link);
transformLinksTexts(link.selectAll('.text'));
transformLinksOutlines(link, nodeRadius, arrowSize);
transformLinksOverlays(link.selectAll('.overlay'));
link.each(function (n) {
updateLastCoord(n.source);
updateLastCoord(n.target);
});
}
}
function needRedraw(link) {
if (!nodeMoved(link.source) && !nodeMoved(link.target)) {
return false;
}
return isVisible(link.source) || isVisible(link.target);
}
function nodeMoved(n) {
return utils.isNumber(n.x) && utils.isNumber(n.y) &&
!(utils.isNumber(n.lx) && Math.abs(n.x - n.lx) <= minDistToRedraw && Math.abs(n.y - n.ly) <= minDistToRedraw);
}
function isVisible(n) {
var result = n.x > svgVisibleRect.left && n.x < svgVisibleRect.right &&
n.y > svgVisibleRect.top && n.y < svgVisibleRect.bottom;
return result;
}
function updateLastCoord(n) {
n.lx = n.x;
n.ly = n.y;
}
function containerToSVG(containerX, containerY) {
var svgPount = svgNode.createSVGPoint();
svgPount.x = containerX;
svgPount.y = containerY;
return svgPount.matrixTransform(document.getElementById("links-svg").getScreenCTM().inverse());
}
function transformLinksLines(link) {
link.attr('transform', function (d) {
var angle = rotation(d.source, d.target);
return 'translate(' + d.source.x + ', ' + d.source.y + ') rotate(' + angle + ')';
});
}

Javascript decrement -- not working in my script

var backgroundImages = new Array(); // create an array holding the links of each image
backgroundImages[0] = "style/images/bg0.png";
backgroundImages[1] = "style/images/bg1.png";
backgroundImages[2] = "style/images/bg2.png";
backgroundImages[3] = "style/images/bg3.png";
backgroundImages[4] = "style/images/bg4.png";
backgroundImages[5] = "style/images/bg5.png";
backgroundImages[6] = "style/images/bg6.png";
var ImageCnt = 0;
function nextImage(direction) // this should take into account the current value (starts at 3) and determines whether a higher or lower value should be returned based on the direction
{
if(direction == "left")
{
ImageCnt-- ;
}
if(direction == "right")
{
ImageCnt++ ;
}
document.getElementById("body-1").style.background = 'url(' + backgroundImages[ImageCnt] + ')'; //put's the new background together for rendering by using the returned value from nextImage()
if(ImageCnt == 6)
{
ImageCnt = -1;
}
}
In this script, ImageCnt ++ is working fine, on the function "nextImage('right')" but on nextImage('left') which triggers ImageCnt-- , the function breaks. What am I doing wrong? (Newbie with js)
Well, your "wrap" code says to make it -1, but what if I then go left? It will try to access -2, which doesn't exist.
Try this instead:
ImageCnt = (ImageCnt + (direction == "left" ? backgroundImages.length-1 : 1)) % backgroundImages.length;
document.getElementById("body-1").style.background = "url('"+backgroundImages[ImageCnt]+"')";
Note also that it is very inefficient to build an array piece by piece like that. Try this:
var backgroundImages = [
"style/images/bg0.png",
"style/images/bg1.png",
"style/images/bg2.png",
"style/images/bg3.png",
"style/images/bg4.png",
"style/images/bg5.png",
"style/images/bg6.png",
];

Trouble with onclick attribute for img when used in combination with setInterval

I am trying to make images that move around the screen that do something when they are clicked. I am using setInterval to call a function to move the images. Each image has the onclick attribute set. The problem is that the clicks are not registering.
If I take out the setInterval and just keep the images still, then the clicks do register.
My code is here (html, css, JavaScript): https://jsfiddle.net/contini/nLc404x7/4/
The JavaScript is copied here:
var smiley_screen_params = {
smiley_size : 100, // needs to agree with width/height from css file
num_smilies: 20
}
var smiley = {
top_position : 0,
left_position : 0,
jump_speed : 2,
h_direction : 1,
v_direction : 1,
intvl_speed : 10, // advance smiley every x milliseconds
id : "smiley"
}
function randomise_direction(s) {
var hd = parseInt(Math.random()*2);
var vd = parseInt(Math.random()*2);
if (hd === 0)
s.h_direction = -1;
if (vd === 0)
s.v_direction = -1;
}
function plotSmiley(sp /* sp = smiley params */) {
var existing_smiley = document.getElementById(sp.id);
if (existing_smiley !== null)
// delete existing smiley so we can move it
document.getElementById("smileybox").removeChild(existing_smiley);
var smiley_to_plot = document.createElement('img');
smiley_to_plot.setAttribute('src', "http://i.imgur.com/C0BiXJx.png");
smiley_to_plot.setAttribute('id', sp.id);
smiley_to_plot.setAttribute('onclick', "my_click_count()");
smiley_to_plot.style.position = 'absolute';
smiley_to_plot.style.top = sp.top_position + "px";
smiley_to_plot.style.left = sp.left_position + "px";
document.getElementById("smileybox").appendChild(smiley_to_plot);
}
function random_direction_change() {
var r = parseInt(Math.random()*200);
if (r===0)
return true;
else
return false;
}
function moveFace(sp_array /* sp_array = smiley params array */) {
var i;
var sp;
for (i=0; i < sp_array.length; ++i) {
// move ith element
sp = sp_array[i];
if (
(sp.h_direction > 0 && sp.left_position >= smiley_screen_params.width - smiley_screen_params.smiley_size) ||
(sp.h_direction < 0 && sp.left_position <= 0) ||
(random_direction_change())
) {
sp.h_direction = -sp.h_direction; // hit left/right, bounce off (or random direction change)
}
if (
(sp.v_direction > 0 && sp.top_position >= smiley_screen_params.height - smiley_screen_params.smiley_size) ||
(sp.v_direction < 0 && sp.top_position <= 0) ||
(random_direction_change())
) {
sp.v_direction = -sp.v_direction; // hit top/bottom, bounce off (or random direction change)
}
sp.top_position += sp.v_direction * sp.jump_speed;
sp.left_position += sp.h_direction * sp.jump_speed;
plotSmiley(sp);
}
}
if (typeof Object.create !== 'function') {
Object.create = function(o) {
var F = function () {};
F.prototype = o;
return new F();
};
}
function generateFaces() {
var smilies = new Array();
var s;
var i;
var css_smileybox=document.getElementById("smileybox");
var sb_style = getComputedStyle(css_smileybox, null);
// add info to the screen params
smiley_screen_params.width = parseInt(sb_style.width);
smiley_screen_params.height = parseInt(sb_style.height);
// create the smileys
for (i=0; i < smiley_screen_params.num_smilies; ++i) {
s = Object.create(smiley);
s.id = "smiley" + i;
s.top_position = parseInt(Math.random() * (smiley_screen_params.height - smiley_screen_params.smiley_size)),
s.left_position = parseInt(Math.random() * (smiley_screen_params.width - smiley_screen_params.smiley_size)),
randomise_direction(s);
smilies.push(s);
}
setInterval( function(){ moveFace(smilies) }, smiley.intvl_speed );
}
var click_count=0;
function my_click_count() {
++click_count;
document.getElementById("mg").innerHTML = "Number of clicks: " + click_count;
}
generateFaces();
The generateFaces() will generate parameters (for example, coordinates of where they are placed) for a bunch of smiley face images. The setInterval is within this function, and calls the moveFace function to make the smiley faces move at a fixed interval of time. moveFace computes the new coordinates of each smiley face image and then calls plotSmiley to plot each one on the screen in its new location (removing it from the old location). The plotSmiley sets the onclick attribute of each image to call a dummy function just to see if the clicks are registering.
Thanks in advance.
This is not a complete answer but it could give you some perspective to improve your code.
First of all, your idea of deleting the existing img so wrong. If it does exist, all you need is to just change its position so instead of this
if (existing_smiley !== null)
// delete existing smiley so we can move it
document.getElementById("smileybox").removeChild(existing_smiley);
you should do something like this:
if (existing_smiley !== null)
var smiley_to_plot = existing_smiley;
else {
var smiley_to_plot = document.createElement('img');
smiley_to_plot.setAttribute('src', "http://i.imgur.com/C0BiXJx.png");
smiley_to_plot.setAttribute('id', sp.id);
smiley_to_plot.style.position = 'absolute';
document.getElementById("smileybox").appendChild(smiley_to_plot);
smiley_to_plot.addEventListener('click', my_click_count);
}
smiley_to_plot.style.top = sp.top_position + "px";
smiley_to_plot.style.left = sp.left_position + "px";
As you can see new image is only being added if it's not already there. Also notice that adding events by using .setAttribute('onclick', "my_click_count()"); is not a good way to do. You should use .addEventListener('click', my_click_count); like I did.
Like I said this is not a complete answer but at least this way they response to the click events now.
FIDDLE UPDATE
Good luck!

Categories