I was implementing some audio files today in my Javascript and it works fine. But
I noticed that with each subsequent click the sound gets louder. I alreadio used the "Audio.volume" method but no luck.
My Code:
var AppController = (function () {
var correctItem, secondItem, thirdItem, selectedItems, selectedItemsShuffled;
var rotateImage = function() {
var i = 0;
var randomNumbers = createThreeRandomNumbers();
var interval = window.setInterval(function () {
i++;
document.getElementById("imageToRotate").src= "img/" + items.images[i].imageLink;
if (i === items.images.length -1) {
i = -1;
}
}, 125);
var clearData = function () {
correctItem = "";
secondItem = "";
thirdItem = "";
selectedItems = "";
selectedItemsShuffled = "";
removeNodeChildren("button-1");
removeNodeChildren("button-2");
removeNodeChildren("button-3");
};
var removeNodeChildren = function (obj) {
var myNode = document.getElementById(obj);
while (myNode.firstChild) {
myNode.removeChild(myNode.firstChild);
}
};
var resetGame = function () {
clearData();
document.getElementById("buttonWrapper").classList.remove("show");
rotateImage();
}
document.getElementById("imageToRotate").addEventListener("click", function (e) {
// select 3 items
correctItem = getSelectedItemDetails(e);
secondItem = getRandomItem(correctItem);
thirdItem = getSecondRandomItem(correctItem, secondItem);
selectedItems = [correctItem, secondItem, thirdItem];
selectedItemsShuffled = shuffleArray(selectedItems);
// create the numbers 1 to 3 randomly
var order = createThreeRandomNumbers();
// remove the rotating effect
clearInterval(interval);
// set the images to the buttons in a random order.
setItemsToButtons(order, selectedItemsShuffled);
//show the buttons
showButtons();
// Create an event which triggers when a button is clicked.
document.getElementById("buttonWrapper").addEventListener("click", function(e) {
var valueOfButtonPressed = e.srcElement.innerText.toLowerCase();
var clickedButton = e.srcElement.id;
if (valueOfButtonPressed === correctItem) {
document.getElementById(clickedButton).innerHTML = e.srcElement.innerText.toLowerCase() + '<span class="icon"><i class="fa fa-check green" aria-hidden="true"></i></span>';
if (!audio) {
var audio = new Audio("audio/correct.mp3");
}
audio.play();
audio.volume = 0.5;
setTimeout(resetGame, 5000);
} else {
document.getElementById(clickedButton).innerHTML = e.srcElement.innerText.toLowerCase() + '<span class="icon"><i class="fa fa-times red" aria-hidden="true"></i></span>';
if (!audio) {
var audio = new Audio("audio/wrong.mp3");
}
audio.play();
audio.volume = 0.5;
setTimeout(resetGame, 5000);
}
});
});
};
// 1: hide the buttons
hidebuttons();
// 2: Replace the title
replaceTitle("Animals");
// 3: set up image rotation until it's clicked.
rotateImage();
})();
Any help will be much appreciated. Cheers!
I'm fairly certain the reason is that you're making a new Audio object with every single click.
Instead of doing this with every click:
var audio = new Audio("audio/correct.mp3");
do a check to see if audio already exists. if it does, then simply do audio.play(). If it does not, THEN you make a new audio object.
Related
I'm using the 'timeupdate' event listener to sync a subtitle file with audio.
It is working currently, but I'd like to adjust it to where it is just highlighting the corresponding sentence in a large paragraph instead of deleting the entire span and replacing it with just the current sentence. This is the sort of functionality I am trying to replicate: https://j.hn/lab/html5karaoke/dream.html (see how it only highlights the section that it is currently on).
This is made complicated due to timeupdate constantly checking multiple times a second.
Here is the code:
var audioSync = function (options) {
var audioPlayer = document.getElementById(options.audioPlayer);
var subtitles = document.getElementById(options.subtitlesContainer);
var syncData = [];
var init = (function () {
return fetch(new Request(options.subtitlesFile))
.then((response) => response.text())
.then(createSubtitle);
})();
function createSubtitle(text) {
var rawSubTitle = text;
convertVttToJson(text).then((result) => {
var x = 0;
for (var i = 0; i < result.length; i++) {
if (result[i].part && result[i].part.trim() != '') {
syncData[x] = result[i];
x++;
}
}
});
}
audioPlayer.addEventListener('timeupdate', function (e) {
syncData.forEach(function (element, index, array) {
if (
audioPlayer.currentTime * 1000 >= element.start &&
audioPlayer.currentTime * 1000 <= element.end
) {
while (subtitles.hasChildNodes()) {
subtitles.removeChild(subtitles.firstChild);
}
var el = document.createElement('span');
el.setAttribute('id', 'c_' + index);
el.innerText = syncData[index].part + '\n';
el.style.background = 'yellow';
subtitles.appendChild(el);
}
});
});
};
new audioSync({
audioPlayer: 'audiofile', // the id of the audio tag
subtitlesContainer: 'subtitles', // the id where subtitles should show
subtitlesFile: './sample.vtt', // the path to the vtt file
});
I am trying to call the JavaScript function repeatedly after the execution.
function startSlide(){
var products = [
['images/product_images/footwear/footwear_1.jpeg'],
['images/product_images/mobile/mobile_1.jpeg'],
['images/product_images/camera/camera_1.jpeg'],
['images/product_images/fashion/fashion_1.jpeg'],
['images/product_images/laptop/laptop_1.jpeg'],
['images/product_images/furniture/furniture_1.jpeg']
];
for (var i = 0; i < products.length; i++) {
var image = products[i][0];
imgslider(image, i * 800);
}
};
function imgslider(image, timeout)
{
window.setTimeout(function() {
document.getElementById('imgslider').innerHTML = "";
var product = document.getElementById('imgslider');
var elem = document.createElement("img");
product.appendChild(elem);
elem.src = image;
},timeout);
startSlide();
}
The startSlide() function iterates the array and fetch the value from array and call the function imgslider(). the imgslider() function appends the image to the div.
at the end of the imgslider() i am trying to call the startSlide() so that it could continue the execution.... but its not working. how can i do this?
The problem is your code is creating an infinite recursion...
Maintianing your code structure you can add a flag to fix the problem
function startSlide() {
var products = [
['//placehold.it/64X64&text=1'],
['//placehold.it/64X64&text=2'],
['//placehold.it/64X64&text=3'],
['//placehold.it/64X64&text=4'],
['//placehold.it/64X64&text=5']
];
for (var i = 0; i < products.length; i++) {
var image = products[i][0];
imgslider(image, i * 800, i == products.length - 1);
}
};
function imgslider(image, timeout, last) {
window.setTimeout(function() {
document.getElementById('imgslider').innerHTML = "";
var product = document.getElementById('imgslider');
var elem = document.createElement("img");
product.appendChild(elem);
elem.src = image;
if (last) {
setTimeout(startSlide, 800);
}
}, timeout);
}
startSlide()
<div id="imgslider"></div>
If you want to loop, then you can use setInterval() instead
function startSlide() {
var products = [
['//placehold.it/64X64&text=1'],
['//placehold.it/64X64&text=2'],
['//placehold.it/64X64&text=3'],
['//placehold.it/64X64&text=4'],
['//placehold.it/64X64&text=5']
],
i = 0;
setInterval(function() {
imgslider(products[i][0]);
if (++i >= products.length) {
i = 0
}
}, 800);
};
function imgslider(image) {
document.getElementById('imgslider').innerHTML = "";
var product = document.getElementById('imgslider');
var elem = document.createElement("img");
product.appendChild(elem);
elem.src = image;
}
startSlide()
<div id="imgslider"></div>
That's because window.setTimeout only calls the function once, so you should use window.setInterval instead. Something like this:
window.setInterval(function () { /* code to update picture... */ }, 3000);
If it's still not working you can check the console log for errors.
That's F12 in IE or Ctrl+Shift+J in Chrome.
I have a Javascript function to change colors of a svg ellipse object with id="eye"
document.getElementById("eye").onmouseover = function() {
changeColor()
};
function changeColor() {
document.getElementById("eye").style.fill = "red";
}
document.getElementById("eye").onmouseout = function() {
changeColor2()
};
function changeColor2() {
document.getElementById("eye").style.fill = "green";
}
Now there are 2 more svg objects with id="nose" and id="mouth", how can I apply the same change color founction on these 2 objects? Yes I can repeat the founction 2 more times but there must be a better way to do it. Note The 3 objects shouldn't change color at the same time, which means there no need for loop.
you mean like that? :
document.getElementById("eye").onmouseover = function() {
changeColor("eye")
};
document.getElementById("nose").onmouseover = function() {
changeColor("nose")
};
document.getElementById("mouth").onmouseover = function() {
changeColor("mouth")
};
document.getElementById("eye").onmouseout = function() {
changeColor2()
};
document.getElementById("nose").onmouseout = function() {
changeColor2("nose")
};
document.getElementById("mouth").onmouseout = function() {
changeColor2("mouth")
};
function changeColor(element) {
document.getElementById(element).style.fill = "red";
}
function changeColor2(element) {
document.getElementById(element).style.fill = "green";
}
You could add a parameter to your functions:
function changeColor(element, color) {
element.style.backgroundColor = color;
}
document.getElementById("eye").onmouseover = function(){changeColor(this, "red")};
document.getElementById("eye").onmouseout = function(){changeColor(this, "green")};
document.getElementById("nose").onmouseover = function(){changeColor(this, "blue")};
document.getElementById("nose").onmouseout = function(){changeColor(this, "orange")};
UPDATED
Syntax for background colour fixed(instead of .style.fill it is .style.backgroundColor)
See Demo Fiddle
Reducing element look-ups
Try to make a closure like this:
// Caching elements and closures
var eye = document.getElementById("eye"),
nose = document.getElementById("nose"),
mouth = document.getElementById("mouth"),
change_to_green = change_group_color("green"),
change_to_red = change_group_color("red");
eye.onmouseover = change_to_green;
eye.onmouseout = change_to_red;
nose.onmouseover = change_to_green;
eye.onmouseout = change_to_red;
mouth.onmouseover = change_to_green;
mouth.onmouseout = change_to_red;
function change_group_color(color){
var elem_group = [eye, nose, mouth];
return function(){
var i,l;
for(i=0, l=elem_group.length ; i<l ; i++){
elem_group[i].style.fill = color;
}
}
}
this inside the handler will point to the element that you registered the event with
function onMouseOver() {
this.style.fill = "red";
}
function onMouseOut {
this.style.fill = "green";
}
var eye = document.getElementById("eye");
var nose = document.getElementById("nose");
eye.onmouseover = nose.onmouseover = onMouseOver;
eye.onmouseout = nose.onmouseout = onMouseOout;
I have a class, that is supposed to display grey overlay over page and display text and loading gif. Code looks like this:
function LoadingIcon(imgSrc) {
var elem_loader = document.createElement('div'),
elem_messageSpan = document.createElement('span'),
loaderVisible = false;
elem_loader.style.position = 'absolute';
elem_loader.style.left = '0';
elem_loader.style.top = '0';
elem_loader.style.width = '100%';
elem_loader.style.height = '100%';
elem_loader.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
elem_loader.style.textAlign = 'center';
elem_loader.appendChild(elem_messageSpan);
elem_loader.innerHTML += '<br><img src="' + imgSrc + '">';
elem_messageSpan.style.backgroundColor = '#f00';
this.start = function () {
document.body.appendChild(elem_loader);
loaderVisible = true;
};
this.stop = function() {
if (loaderVisible) {
document.body.removeChild(elem_loader);
loaderVisible = false;
}
};
this.setText = function(text) {
elem_messageSpan.innerHTML = text;
};
this.getElems = function() {
return [elem_loader, elem_messageSpan];
};
}
Problem is, when I use setText method, it sets innerHTML of elem_messageSpan, but it doesn't reflect to the span, that was appended to elem_loader. You can use getElems method to find out what both elements contains.
Where is the problem? Because I don't see single reason why this shouldn't work.
EDIT:
I call it like this:
var xml = new CwgParser('cwg.geog.cz/cwg.xml'),
loader = new LoadingIcon('./ajax-loader.gif');
xml.ondataparse = function() {
loader.stop();
document.getElementById('cover').style.display = 'block';
};
loader.setText('Loading CWG list...');
loader.start();
xml.loadXML();
xml.loadXML() is function that usually takes 3 - 8 seconds to execute (based on download speed of client), thats why I'm displaying loading icon.
I've come up with this little animation that uses images in an array, but it's sloppy. What I'd really like is to modularize it so that I can use multiple instances of the same function to animate different arrays depending on which key is pressed. Also, I'd like to get it so that the animation stops when the key is released, if possible. I realize that calling all of those variables globally is a big no-no, but I have no idea how to make it work otherwise! Last but not least, I'd like to figure out how to get that last bit of inline script over to the external file and have it still work correctly. Any help would be much appreciated! But please note, I'm a novice with JavaScript, so please try to be as specific/detailed as possible so I can learn from your wisdom! Thank you!
I've created a jsfiddle.
HTML:
<div id="wrapper">
<span id="jajo"><img id="my_img" src="jajo.png" /></span>
<script>element = document.getElementById("jajo"); </script>
</div><!--wrapper-->
JavaScript:
var i = 0;
var element;
var waveArray = ["wave0.png","wave1.png","wave2.png","wave3.png","wave4.png","wave5.png"];
var jumpArray = ["jump0.png","jump1.png","jump2.png","jump3.png","jump4.png","jump5.png"];
var foodArray = ["food0.png","food1.png","food2.png","food3.png","food4.png","food5.png"];
document.onkeydown = function(event) {
var keyPress = String.fromCharCode(event.keyCode);
if(keyPress == "W") { // if "w" is pressed, display wave animation
increment ();
document.onkeyup = function(event) { // if "w" is released, display default image
document.getElementById("jajo").innerHTML= "<img alt='Jajo' src='jajo.png'>";
}
} else if(keyPress == "A") { // if "a" is pressed, display jump animation
} else if(keyPress == "S") { // if "s" is pressed, display food animation
}
}
function increment (){
i++;
if(i > (waveArray.length - 1)){
i = 0;
}
setTimeout(animation,1000/30);
}
function animation() {
var img = '<img src="' + waveArray[i] + '" alt="Jajo" />';
element.innerHTML = img;
setTimeout(increment, 2000/30);
}
Demo
http://jsfiddle.net/ghQwF/4/
Module
var animation = (function() {
var delay = 1000 / 30;
var map = {}, active = [], timer;
function animate() {
for(var i=0, l=active.length; i<l; ++i) {
var data = map[active[i]];
++data.index;
data.index %= data.array.length;
data.image.src = data.array[data.index];
}
timer = setTimeout(animate, delay);
}
function begin(e) {
var key = String.fromCharCode(e.keyCode),
data = map[key];
if(!data) return;
if(!active.length) timer = setTimeout(animate, delay);
if(!~active.indexOf(key)) active.push(key);
}
function end(e) {
var key = String.fromCharCode(e.keyCode),
data = map[key];
if(!data) return;
data.image.src = data.default;
var index = active.indexOf(key);
if(!~index) return;
active.splice(index, 1);
if(!active.length) clearTimeout(timer);
}
return {
add: function(data) {
data.index = data.index || 0;
data.image = data.image || data.target.getElementsByTagName('img')[0];
data.default = data.default || data.image.src;
map[data.key] = data;
},
remove: function(key) {
delete map[key];
},
enable: function() {
document.addEventListener('keydown', begin, false);
document.addEventListener('keyup', end, false);
},
disable: function() {
document.removeEventListener('keydown', begin, false);
document.removeEventListener('keyup', end, false);
clearTimeout(timer);
active = [];
}
};
})();
Example
animation.enable();
animation.add({
key: 'W',
target: document.getElementById('jajo'),
array: [
"http://static.spoonful.com/sites/default/files/styles/square_128x128/public/crafts/fingerprint-flowers-spring-craft-photo-420x420-aformaro-01.jpg",
"http://static.spoonful.com/sites/default/files/styles/square_128x128/public/crafts/paper-flowers-spring-craft-photo-420x420-aformaro-11.jpg",
"http://static.spoonful.com/sites/default/files/styles/square_128x128/public/crafts/tissue-paper-flowers-kaboose-craft-photo-350-fs-IMG_8971_rdax_65.jpg"
]
});
animation.add({ /* ... */ });
Methods
add
Adds a new animation, with parameters:
key: key to activate animation
target: wrapper of the image (optional if image is specified)
image: image to animate (optional, defaults to first image in target)
default: default url of the image (optional, defaults to original url)
index: initial position in array (optional, defaults to 0)
array: array of images
remove
Removes the animation related with key specified.
disable
Disables animations (typing keys would have no effect), and stops current ones.
enable
Enable animations (typing keys can start animations).