HTML5 Audio is not working with setTimeout method - javascript

I am fairly new to JavaScript and jQuery, what I'm doing is actually calling a function with an array of some colors, initially it is ['blue', 'green']
And I runs a for-loop on this array to play sound specific to each number.
But the problem is, it just plays sound with the last parameter. Why so?
I want to play both, (or how many I adjust) sounds to be played after a fixed interval (like here I've 1000ms). Thanks in advance.
Here's my code:
function singPattern(compPattern) {
var audio;
for(let i=0; i<compPattern.length; i++) {
audio = new Audio("../../music-files/" + compPattern[i] + ".mp3");
window.setTimeout(function () {
audio.play();
}, 2000);
}
}

function singPattern(compPattern) {
var audio; // There is only one audio
for(let i=0; i<compPattern.length; i++) {
//everytime in this loop audio is assigned to the "Audio"
// the old one is off to the pasture. (dead)(GC)
audio = new Audio("../../music-files/" + compPattern[i] + ".mp3");
window.setTimeout(function () {
audio.play();
}, 2000);
}
}
Like in all things javascript, There are several ways to handle this.
you could pass the object into the timeout like this. and the time out will have a closure of the object.
function singPattern(compPattern) {
for(let i=0; i<compPattern.length; i++) {
// I will just print the word but you can pass the object in.
var audio = compPattern[i] + ".mp3";
window.setTimeout(function (_audio) {
console.log(_audio)
}, 2000, audio);
}
}
If you need access to the Audio object outside of the setTimeout to cancel/replay it you could set up a dictionary like this :
function singPattern(compPattern) {
var audio ={};
for(let i=0; i<compPattern.length; i++) {
audio[compPattern[i]] = compPattern[i] + ".mp3";
}
setTimeout(function(){
for( var a in audio ){ console.log( "play ", a , audio[a] ); }
},2000);
// audio['green'].pause() ?
}
Edit
Heres a working sample with external mp3s
function singPattern(compPattern) {
for (let i = 0; i < compPattern.length; i++) {
var audio = compPattern[i];
setTimeout(function (_audio) {
var sound = new Audio(_audio);
console.log("Playing " + _audio);
sound.play();
}, 2000, audio);
}
}
var soundUrls = ["http://soundbible.com/mp3/Stream Noise-SoundBible.com-866411702.mp3", "http://soundbible.com/mp3/Strange_Days-Mike_Koenig-176042049.mp3"];
singPattern(soundUrls);

Related

How to prevent a video array from playing the same video twice?

I'm trying to play video1 to 3 without playing the video twice. I'm using videos.shift(); to remove the first video from the array, but I'm not sure how to remove the other 2 videos after they play once.
var videos = ["video1", "video2", "video3"];
videos.shift();
var player1 = document.getElementById("video");
function setMp4Source1() {
var currentVideoIndex = Math.floor(Math.random() * videos.length);
var currentVideo = "videos/" + videos[currentVideoIndex] + ".mp4";
player1.src = currentVideo;
player1.load();
player1.play();
}
player1.addEventListener('ended', nextVideo, false);
function nextVideo() {
setMp4Source1();
}
If I understand this correctly, videos is a queue of videos that need to be played in order, then nothing else.
I would normalize all the queue management into the nextVideo() function, so that there was nothing special about the first time you play. Thus:
var videos = ["video1", "video2", "video3"]
var player1 = document.getElementById("video")
function setMp4Source1(theVideo) {
var currentVideo = "videos/" + theVideo + ".mp4"
player1.src = currentVideo
player1.load()
player1.play()
}
player1.addEventListener('ended', nextVideo, false)
function nextVideo() {
let theVideo = videos.unshift()
setMp4Source1(theVideo)
}
nextVideo() // start the chain here!
Now, this doesn't, as in your initial example, play a random video - just the next in the queue. You can play a random video and remove it from the queue by using splice() in nextVideo() as such:
function nextVideo() {
let randomIndex = Math.floor(Math.random() * videos.length)
let theVideo = videos[randomIndex]
videos.splice(randomIndex,1) // remove 1 element starting at the index
setMp4Source1(theVideo)
}
Of course, next you'll want to add a check to make sure you're not accessing an empty array...
function nextVideo() {
if( videos.length < 1) {
// no moar videos! :(
// probably best to bail out here and avoid further chaining.
// if you're clever, you'll add a placeholder image to let
// the user know there's no more videos.
// maybe something from http://placekitten.com/
player1.removeEventListener('ended', nextVideo, false)
sizer_x = player1.clientWidth
sizer_y = player1.clientHeight
const kitty = '<img src="http://placekitten.com/' + sizer_x + '/' + sizer_y + '"/>'
const kittyImg = document.createElement(kitty)
player1.parentNode.replaceChild(kittyImg, player1)
return
}
let randomIndex = Math.floor(Math.random() * videos.length)
let theVideo = videos[randomIndex]
videos.splice(randomIndex,1) // remove 1 element starting at the index
setMp4Source1(theVideo)
}
Keep track of the index numbers played with a Set object. Set object is simular to an array but will only contain unique items. Details are commented in the following Snippet.
const videos = [
"vs8s3.mp4", "vs12s3.mp4", "vs21s3.mp4", "vs2s3.mp4"
];
const player = document.querySelector("video");
// Create a Set object
const played = new Set();
function playVideo() {
let current = Math.floor(Math.random() * videos.length);
const base = 'https://glpjt.s3.amazonaws.com/so/av/';
const source = base + videos[current];
/* Check if video has already been played */
// If Set {played} DOES NOT have the {current} index number...
if (!played.has(current)) {
// Add {current} index number to Set {played}
played.add(current);
player.src = source;
player.play();
}
/* or if Set {played} does have {current} index number AND Set {played}
DOES NOT have the same amount of numbers as Array {videos}... */
else if (played.size != videos.length) {
// Run the function again
playVideo();
}
// Otherwise end function
else {
return false;
}
/* OPTIONAL: Display the indexes of each video played in order */
console.clear();
console.log(JSON.stringify(Array.from(played)));
};
player.addEventListener('ended', playVideo, false);
player.addEventListener('click', playVideo, false);
video {
cursor: pointer;
}
<video poster='http://simgbb.com/background/W46xw0TswdDx.jpg' width='360'></video>

Clar Ajax data after each OnClick and then append new data

I'm doing a project for school. A memory game that uses the pokéAPI to get images.
I have three buttons depending on the game level (easy, medium, hard) and it generates the amount of cards.
When I click a button it generates the pictures but when I click it again it append the new data into the same selector.
I have tried with: $('#output').html(''), $('#output').append(''), $('#output').remove(), $('#output').empty()
// Settings for game level. Each integer represents number of cards * 2.
let easy = 4;
let medium = 6;
let hard = 8;
// Arrays for PokemonImgUrl.
let originalPokemonImgUrl = [];
let duplicateAllPokemonImgUrl = [];
let allPokemonImgUrl = [];
// PokéAPI URL.
const pokemonDataUrl = 'https://pokeapi.co/api/v2/pokemon/';
// This function creates a random number depending on the settings below.
function randomNumber() {
// Settings for max randomnumbers starting from index 1.
let randomNumberMax = 500;
let fromIndex = 1;
// Math random function with values from randomnumbers.
return Math.floor(Math.random() * randomNumberMax) + fromIndex;
}
// Function for getting data from PokéAPI.
function getData() {
$.ajax ({
type: 'GET',
url: pokemonDataUrl + randomNumber(), // Calling randomnnumber to get a random pokémon.
success: function(pokemonData) {
var pokemonImgUrl = pokemonData.sprites.front_default; // Store and extract pokemon images.
originalPokemonImgUrl.push(pokemonImgUrl); // store ImagesURL to a global array called allPokemonImgUrl.
}
})
}
// Shuffle code from css-tricks.com.
function Shuffle(cards) {
for(var j, x, i = cards.length; i; j = parseInt(Math.random() * i), x = cards[--i], cards[i] = cards[j], cards[j] = x);
return cards;
};
// function iterates through allPokemonImgUrl array and outputs it into the DOM.
function output() {
allPokemonImgUrl.forEach(function (i) {
$('#output').append('<img src="'+ [i] +'">');
}
)}
/* This function copies the array so that we always have two of the same cards.
Then concat into a new array and shuffles it. After that it outputs the result.*/
function startGame(){
setTimeout( function(){
duplicateAllPokemonImgUrl = originalPokemonImgUrl.slice();
}, 1000 );
setTimeout( function(){
allPokemonImgUrl = originalPokemonImgUrl.concat(duplicateAllPokemonImgUrl);
}, 1500 );
setTimeout( function(){
Shuffle(allPokemonImgUrl)
}, 2000 );
setTimeout( function(){
output();
}, 2500 );
}
/* Events for clicking on game levels. It iterates to check how many cards it needs
and calls the function getData accordingly. */
$(document).on('click', '#easy', function() {
for (var cards = 0; cards < easy; cards++) {
getData();
}
startGame();
})
$(document).on('click', '#medium', function() {
for (var cards = 0; cards < medium; cards++) {
getData();
}
startGame();
})
$(document).on('click', '#hard', function() {
for (var cards = 0; cards < hard; cards++) {
getData();
}
startGame();
})
When I click a button I get the images as expected.
But when I click it again it appends the new data after the old data. I want to remove the old ajax data first and then append the new data.
I found the solution:
Adding a clear function:
function clear() {
originalPokemonImgUrl = [];
duplicateAllPokemonImgUrl = [];
allPokemonImgUrl = [];
$('#output').empty();
}
Then just call it:
$(document).on('click', '#easy', function() {
for (var cards = 0; cards < easy; cards++) {
getData();
}
clear();
startGame();
})

For Loop refusing to Loop; Works Perfectly Fine if Run Manually

Alright, so, hopefully this is a simple enough question. I'm sure that it has to be something simple and stupid that I am not doing right. I'm building an app to program my exercise routine into so I can keep track of it every week (and to learn app development using Cordova). Within the code, each time I come back to the exersetup screen I want to read the saved exercise file, parse it back to an array of objects, convert those objects back into exercise objects, then display them on the screen. I have a loop that is supposed to check each element of the array to process the information, then display, then repeat until it has done this for each element in the array. It works perfectly fine for the first item, then stops. If I manually run the loop multiple times, it functions perfectly. Not listed is the code for the function addRow(), which simply adds a row of formatted dropdown selectors for input, then increments rowNum to keep track of which row it is operating on. Any help you guys can offer is appreciated. If I need to submit more information I would be happy to do so. Here's the loop in question:
EDIT: Fixed some things to the suggestions in the comments below. Code still functions the same. Works perfectly fine on first run through loop, then stops. If I continue to run the loop manually (declaring it over and over again in console) it works fine, but it will not, for some reason, run the "for" loop. I've also included the rest of the function that the for loop is nested in, in case that helps.
function readRout()
{
console.log("Reading routine...");
document.addEventListener('deviceready', getFile, false);
function getFile()
{
window.resolveLocalFileSystemURL(cordova.file.dataDirectory, function(dir)
{
console.log("got main dir",dir);
dir.getFile("exerciseroutine.txt", {create:true}, function(file)
{
console.log("got the file", file);
exerLog = file;
console.log(exerLog);
});
});
document.addEventListener('deviceready', logOut, false);
}
function logOut()
{
setTimeout(readLog, 50);
}
function readLog()
{
exerLog.file(function(file)
{
var reader = new FileReader();
reader.onloadend = function(e)
{
console.log(this.result);
exerRoutine = JSON.parse(this.result);
//console.log(exerRoutine);
//console.log(exerRoutine[0]);
var looper = exerRoutine.length + 1;
for (var i=0; i<looper; i++)
{
console.log("Loop count is: " + i);
console.log(looper);
exerRoutine[i] = new exercise(exerRoutine[i].type, exerRoutine[i].name, exerRoutine[i].sets, exerRoutine[i].reps, exerRoutine[i].pace);
console.log(exerRoutine[i]);
console.log(exerRoutine[i].description());
console.log(exerRoutine[i].type);
console.log(exerRoutine[i].name);
console.log(exerRoutine[i].sets);
console.log(exerRoutine[i].reps);
console.log(exerRoutine[i].pace);
addRow();
setTimeout(fillExer, 50);
console.log(exerRoutine);
}
console.log(looper);
};
reader.readAsText(file);
}, fail);
}
function fillExer()
{
var typeRow = "typeSel" + rowNum;
console.log(typeRow);
console.log(document.getElementById(typeRow));
var nameRow = "nameSel" + rowNum;
var setsRow = "setsSel" + rowNum;
var repsRow = "repsSel" + rowNum;
var paceRow = "paceSel" + rowNum;
fillType(typeRow);
setTimeout(fillName, 10, nameRow);
fillSets(setsRow);
fillReps(repsRow);
fillPace(paceRow);
}
function fillType(typek)
{
for (var k=0; k<document.getElementById(typek).options.length; k++)
{
//console.log(document.getElementById(typek).options.length);
console.log(exerRoutine[rowNum-1].type);
if(document.getElementById(typek).options[k].value == exerRoutine[rowNum-1].type)
{
document.getElementById(typek).selectedIndex = k;
//console.log(document.getElementById(typei).selectedIndex);
var quer = "#" + typek;
//console.log(query);
var typeInput = document.querySelector(quer);
//console.log(typeInput);
TypeSelect(typeInput);
return;
}
}
}
function fillName(namek)
{
for (var k=0; k<document.getElementById(namek).options.length; k++)
{
//console.log(document.getElementById(namek).options.length);
console.log(exerRoutine[rowNum-1].name);
if(document.getElementById(namek).options[k].value === exerRoutine[rowNum-1].name)
{
document.getElementById(namek).selectedIndex = k;
//console.log(document.getElementById(namei).selectedIndex);
return;
}
}
}
function fillSets(setsk)
{
for (var k=0; k<document.getElementById(setsk).options.length; k++)
{
console.log(exerRoutine[rowNum-1].sets);
if(document.getElementById(setsk).options[k].value === exerRoutine[rowNum-1].sets)
{
document.getElementById(setsk).selectedIndex = k;
//console.log(document.getElementById(setsk).selectedIndex);
return;
}
}
}
function fillReps(repsk)
{
for (var k=0; k<document.getElementById(repsk).options.length; k++)
{
console.log(exerRoutine[rowNum-1].reps);
if(document.getElementById(repsk).options[k].value === exerRoutine[rowNum-1].reps)
{
document.getElementById(repsk).selectedIndex = k;
//console.log(document.getElementById(repsi).selectedIndex);
return;
}
}
}
function fillPace(pacek)
{
for (var k=0; k<document.getElementById(pacek).options.length; k++)
{
console.log(exerRoutine[rowNum-1].pace);
if(document.getElementById(pacek).options[k].value === exerRoutine[rowNum-1].pace)
{
document.getElementById(pacek).selectedIndex = k;
//console.log(document.getElementById(pacek).selectedIndex);
return;
}
}
}
}

How to call JavaScript function repeatedly

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.

Animation Array in JavaScript

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).

Categories

Resources