Slow Render/Delayed Render in Vanilla Javascript App - javascript

I have a couple issues - the app is a vanilla javascript app for school that maps audio to each alphabetical key. The issues are as follows:
It takes quite a while to load the audio - the app proceeds like normal but with no audio - it usually can run as intended after a few minutes or a few refreshes, but I do want to get rid of that period. I tried to fix that using (document.readyState === "interactive") and if (allAudio.entries(audio => (audio.readyState === 4)))
It doesn't take the first key down event to change the intro slides, it takes the second - but I'm also not sure how to fix this. The slides are in an array that goes through each item and then makes them display as none. I also tried to add the event listener for the keyboard earlier, but to no avail.
To look at the bugs yourself, live link is here: https://haeuncreative.github.io/mosatic/
relevant code:
if (document.readyState === "interactive") {
const allAudio = document.querySelectorAll("audio")
console.log(allAudio)
if (allAudio.entries(audio => (audio.readyState === 4)))
window.addEventListener('load', function() {
const keysDown = new KeyDownHandler()
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
context.fillStyle = '#967bb6'
context.fillRect(0, 0, 1000, 562.5)
const clickDown = new ClickHandler(keysDown)
}
);
}
export default class KeyDownHandler {
constructor() {
this.addPressListener()
// audio
this.soundBank = new AudioBank
this.soundBank.createBank(CONSTANTS.KEY_ALPHABET)
// visual // intro
this.intro1 = document.querySelector('#intro1')
this.intro2a = document.querySelector('#intro2a')
this.intro2b = document.querySelector('#intro2b')
this.intro2c = document.querySelector('#intro2c')
this.intro3 = document.querySelector('#intro3')
this.intro4 = document.querySelector('#intro4')
this.introBank = [
this.intro1,
this.intro2a,
this.intro2b,
this.intro2c,
this.intro3,
this.intro4
]
// visual // main
this.body = document.querySelector('body')
this.canvas = document.querySelector('canvas');
this.context = this.canvas.getContext('2d');
this.background_colors = ["#95c88c", "#967bb6", "#A7C7E7", "#FF6961"]
this.aniBank = new AniBank;
// recording/user interaction
this.keys = [];
this.durations = [];
this.recording = false;
}
introSwitch() {
this.soundBank.playSpace()
if (this.currentSlide) {
this.currentSlide.style.animation = "fadeOut 1s"
this.currentSlide.style.display = "none"
this.currentSlide = ""
}
if (!this.introBank.length) {
slide.style.display = "none"
this.introFinish = true
}
if (this.introBank.length) {
let slide = (this.introBank.shift())
slide.style.filter = "brightness(60%)"
console.log(slide)
if (slide === this.intro4 || !slide) {
this.canvas.style.display = "flex";
this.addKeyListeners()
}
if (slide.style.display = "none") {
slide.style.display = "flex"
slide.style.filter = "brightness(60%)"
}
this.currentSlide = slide;
slide.style.filter = "brightness(100%)"
}
}
addPressListener() {
window.addEventListener('keypress', e => {
e.preventDefault()
e.stopImmediatePropagation()
this.introSwitch()
if (this.currentSlide === this.intro4) {
this.currentSlide.style.display = "none"
window.removeEventListener("keypress", this.introSwitch)
this.addKeyListeners()
}
})
}`
It takes quite a while to load the audio - the app proceeds like normal but with no audio - it usually can run as intended after a few minutes or a few refreshes, but I do want to get rid of that period. I tried to fix that using (document.readyState === "interactive") and if (allAudio.entries(audio => (audio.readyState === 4)))
It doesn't take the first key down event to change the intro slides, it takes the second - but I'm also not sure how to fix this. The slides are in an array that goes through each item and then makes them display as none. I also tried to add the event listener for the keyboard earlier, but to no avail.

Related

How to make audio element more responsive?

Introduction
Greetings, I have looked at some similar questions that are on this platform and none of them seem to match my problem or maybe I missed something but I will try my best to give it a try because I am new in this platform.
OverView
This audio app maps the keyboard keys to audio samples.
Problem
There is a audio delay when pressing the keys rapidly and after each press the sample sound keeps playing for 4 to 6 second before it calls another sound and doesn't kill the sound immediately after the keyboard button is released.
Code
This is what I tried:
const dump = console.log.bind(console);
const sample = new Object
({
drum: "/samples/drums/trance01/BD_Trance.wav",
clap: "/samples/drums/trance01/Clap_trance.wav",
});
const keymap = new Object
({
"KeyD": "drum",
"KeyC": "clap",
});
Object.keys(sample).forEach((smpl)=>
{
let node = document.createElement("audio");
node.id = (smpl+"Sample");
node.className = "instrument";
node.src = sample[smpl];
document.body.appendChild(node);
});
document.body.addEventListener("keydown", function keyListener(event)
{
event.preventDefault(); // kill it
event.stopPropagation(); // seal it's ashes in a capsule
event.stopImmediatePropagation(); // and hurl it into the sun!
let key = event.code;
// console.log("pressed: "+key);
let tgt = keymap[key];
if (!tgt){ dump(key+" - is unused"); return };
var intervalID = setInterval(myCallback, 500, 'Parameter 1', 'Parameter 2');
function myCallback(a, b)
{
let nde = document.getElementById(tgt+"Sample");
nde.play();
dump("play: "+tgt);
}
});
Following what happens in the logic and how elements respond in their own way (and time) is important for analysing what the issue may be.
In this case I believe this can be solved by cloning the source-node, not expecting it to play multiple instances of itself by itself (or that is what I believe should happen) - but we can force it to:
const dump = console.log.bind(console);
const sample = new Object
({
drum: "/samples/drums/trance01/BD_Trance.wav",
clap: "/samples/drums/trance01/Clap_trance.wav",
});
const keymap = new Object
({
"KeyD": "drum",
});
(Object.keys(sample)).forEach((item,indx)=>
{
let node = document.createElement("audio");
node.id = (item+"Sample");
node.className = "instrument";
node.src = sample[item];
document.body.appendChild(node);
});
function keyHandler(event)
{
if (event.ctrlKey){ return }; // whew .. that was annoying as f*ck
event.preventDefault(); // kill it
event.stopPropagation(); // seal it's ashes in a capsule
event.stopImmediatePropagation(); // and hurl it into the sun!
let key = event.code;
let tag = keymap[key];
let nde,tgt;
tgt = document.getElementById(tag+"Sample");
if (!tgt){ dump(key+" - is unused"); return };
nde = tgt.cloneNode(true);
nde.id = (tgt.id+"Clone");
nde.addEventListener("ended",function()
{
this.parentNode.removeChild(this);
});
document.body.appendChild(nde);
nde.play();
dump("playing: "+tgt);
}
document.body.addEventListener("keydown", keyHandler);

matter.js: collisionStart triggered many times for one collision

I am working on a game app using React native and Matter.js.
I am trying to implement a system that adds points every time a bullet hits a target.
In order to do this, I am trying to use collisionStart to detect the collision.
However, even though the bullet and target collide only once, the event seems to be triggered 41 times.
This is the code:
Matter.Events.on(engine, 'collisionStart', (event) => {
let pairs = event.pairs
for (const pair of pairs) {
if (pair.bodyA.label === 'bullet' && pair.bodyB.label === 'Worm') {
console.log("target hit");
}
}
})
In the end, I'm planning to replace console.log with something that adds points. At the current moment, one collision seems like it would trigger the add points 41 times, which is obviously not ideal.
Any ideas what is happening here and how I can get this to trigger only once for one collision?
Try next example. I take it from my own project [you need little adaptd from ts to js]:
Matter.Events.on(this.starter.getEngine(), "collisionStart", function (event) {
root.collisionCheck(event, true);
});
public collisionCheck(event, ground: boolean) {
const myInstance = this;
const pairs = event.pairs;
for (let i = 0, j = pairs.length; i !== j; ++i) {
const pair = pairs[i];
if (pair.activeContacts) {
if (pair.bodyA.label === "bullet" && pair.bodyB.label === "Worm") {
const collectitem = pair.bodyA;
this.playerDie(collectitem);
} else if (pair.bodyB.label === "bullet" && pair.bodyA.label === "Worm") {
const collectitem = pair.bodyB;
this.playerDie(collectitem);
}
// ....
}
}
}
public destroyBody = (destroyBody) => {
try {
Matter.Composite.remove(this.getWorld(), destroyBody);
} catch(err) {
console.log(err)
}
}
If you still have same problem , we can adapt also with flag PREVENT_DOUBLE_BY_1_SECOUND for example.

Web audio API, problem using the panNode, the sounds only play once

I am trying to integrate some panning effects to some sounds in a small testing app. It works fine except for one important issue: each sound only plays once!
I have tried several ways to attempt to bypass that issue without any success. The thing is, I can't pinpoint where the problem comes from. Here is my code, and a little explanation bellow.
const audio = new Audio('audio/background.mp3');
const footstep = new Audio('audio/footstep1.mp3');
const bumpWall1 = new Audio(`audio/bump-wall1.mp3`);
const bumpWall2 = new Audio(`audio/bump-wall2.mp3`);
const bumpWall3 = new Audio(`audio/bump-wall3.mp3`);
const bumpWall4 = new Audio(`audio/bump-wall4.mp3`);
const bumpWallArray = [bumpWall1, bumpWall2, bumpWall3, bumpWall4];
audio.volume = 0.5;
function play(sound, dir) {
let audioContext = new AudioContext();
let pre = document.querySelector('pre');
let myScript = document.querySelector('script');
let source = audioContext.createMediaElementSource(sound);
let panNode = audioContext.createStereoPanner();
source.connect(panNode);
panNode.connect(audioContext.destination);
if (dir === "left") {
panNode.pan.value = -0.8
} else if (dir === "right") {
panNode.pan.value = 0.8;
} else {
panNode.pan.value = 0;
}
sound.play();
}
So basically, when you call the play() function it plays the sound either on the left, the right, or the middle. But each sound is only played once. For example, if the footstep was played one time, it is never played again if I call the play() function on it.
Can anyone help me with that?
In your developer console, you should have a message stating something along the lines of
Uncaught InvalidStateError: Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode.
(At least in Chrome,) you can't connect a MediaElement several times to a MediaElementSourceNode.
To avoid this, you would have to disconnect this MediaElement from the MediaElementSourceNode, but this isn't possible...
The best in your situation is probably to use directly AudioBuffers rather than HTMLAudioElements, moreover if you don't append them in the doc.
let audio;
const sel = document.getElementById( 'sel' );
// create a single AudioContext, these are not small objects
const audioContext = new AudioContext();
fetch( 'https://dl.dropboxusercontent.com/s/agepbh2agnduknz/camera.mp3' ).then( resp => resp.arrayBuffer() )
.then( buf => audioContext.decodeAudioData( buf ) )
.then( audioBuffer => audio = audioBuffer )
.then( () => sel.disabled = false )
.catch( console.error );
function play(sound, dir) {
let source = audioContext.createBufferSource();
source.buffer = sound;
let panNode = audioContext.createStereoPanner();
source.connect( panNode );
panNode.connect( audioContext.destination );
if (dir === "left") {
panNode.pan.value = -0.8
} else if (dir === "right") {
panNode.pan.value = 0.8;
} else {
panNode.pan.value = 0;
}
source.start( 0 );
}
sel.onchange = evt => play( audio, sel.value );
<select id="sel" disabled>
<option>left</option>
<option>center</option>
<option>right</option>
</select>

Creating multiples of the same DOM Element Image

I am a new Developer and am working on a Tic Tac Toe SPA. I have a working game, but I want to customize the game tokens. I have tried creating a DOM element a few different ways, which were all successful. Here is the problem:
Every time I go to make a second move for a player, the DOM image disappears and reappears in the new square selected. Obviously this is not the desired action. Is there something I don't know about creating a DOM element. I have googled this and read countless articles and watched countless videos.
const stark = document.createElement('img')
stark.src = 'https://i.imgur.com/d70XlET.png'
stark.height = 80
stark.width = 80
const lanister = document.createElement('img')
lanister.src = 'https://i.imgur.com/d70XlET.png'
lanister.height = 80
lanister.width = 80
const play = (event) => {
if (gameOver === false) {
if (event.target.innerHTML === '') {
$('#' + event.target.id).append(turn)
}
}
}
'turn' is a variable that works with a toggle function to switch between players and stores whichever players turn it is(i.e. 'stark')
I would be really grateful if someone could point me in the direction of a resource where I could learn more about this.
const player1 = stark
const player2 = lanister
let turn = player1
let prevTurn = player2
const togglePrevTurn = () => {
if (!gameOver) {
if (prevTurn === player1) {
prevTurn = player2
} else {
prevTurn = player1
}
}
}
const toggleTurn = () => {
if (!gameOver) {
if (turn === player1) {
turn = player2
} else {
turn = player1
}
$('#message').text(turn + " 's turn")
}
}
Whenever you use Javascript's appendChild or jQuery's append, when you pass it an element, that element gets removed from its previous location in the DOM (if it's in the DOM), and then gets inserted at the new position. It sounds like what you need to do is explicitly create a new element each time, which you might do with cloneNode().
Also, probably best to name your variables precisely - if turn is an image, name it to make it clear that it's an image, perhaps currentPlayerImage.
In addition, because you already have a reference to the event.target, there's no need to reselect it with $('#' + event.target.id) - just select event.target:
const play = (event) => {
if (gameOver === false) {
if (event.target.innerHTML === '') {
$(event.target).append(currentPlayerImage.cloneNode());
}
}
}

JavaScript iOS9 Bug - ClassList

I have a block of JavaScript written with some ES6 that transpiles down with Babel. What the code is looking to do is check to see whenever a section scrolls into view, at which point a class it added to toggle in its content. I have been told this isn't working on an iOS9 device and I don't have one here to test myself to replicate the error. Can anybody see what part of this isn't supported? I suspect it would be classList.contains but I'm not positive. Any suggestions would be great!
function toggleLume() {
const lumeImages = document.querySelectorAll('.model-image.lume');
lumeImages.forEach(image => image.classList.toggle('is-active'));
lumeIcons.forEach(icon => icon.classList.toggle('is-active'));
}
function checkIfInView() {
const windowHeight = window.innerHeight;
const windowTopPosition = window.scrollY;
const windowBottomPosition = (windowHeight + windowTopPosition);
const buffer = 120;
const panels = document.querySelectorAll('.panel');
panels.forEach(panel => {
const panelHeight = panel.clientHeight;
const panelTop = (panel.offsetTop - panel.scrollTop) + buffer;
const panelBottom = (panelTop + panelHeight);
if ((panelBottom >= windowTopPosition) && (panelTop <= windowBottomPosition) && !panel.classList.contains('in-view')) {
panel.classList.add('in-view');
}
});
}
Side note, I am interested in JavaScript solutions first and jQuery if I have no other plain JS option.

Categories

Resources