Sound function-
function sound(src) {
this.sound = document.createElement("audio");
this.sound.src = src;
this.sound.setAttribute("preload", "auto");
this.sound.setAttribute("controls", "none");
this.sound.style.display = "none";
document.body.appendChild(this.sound);
this.play = function(){
this.sound.play();
}
this.stop = function(){
this.sound.pause();
}
}
putting sounds in-
flying = new sound("flying.wav");
rendering sounds-
flying.play()
what i'm wondering is, is there anyway to make the volume of the wav go up?
You just interact with the volume property of the audio element.
Here's an example function from a file that I use that gives the user an up and down button to adjust the volume by .1 in either direction and makes sure that they don't wind up setting it to an out of bounds value. It's pretty self-explanatory.
function adjustVolume(increment) {
// Get the proposed new volume level and check that it
// is between 0 and 1 (inclusive) or you will throw an error.
var proposedV = audioElement.volume + increment;
if (proposedV >= 0 && proposedV <= 1) {
audioElement.volume = proposedV;
}
}
Related
I want to animate stuff on a canvas using the window.requestanimationframe() method on top of two video (e.g. for highlighting important stuff in both videos). Currently, the animation runs and after that, the videos start playing together. So how can I execute everything (both videos and canvas animation) simultaneously?
class Animation {
constructor(canvas, data) {
this.canvas = document.getElementById(canvas);
this.ctx = this.canvas.getContext('2d');
this.data = data;
this.start = 0;
this.counter = 0;
this.running = false;
this.draw = function(){
console.log(this);
console.log("Draw");
if(!document.querySelector('video').playing){
// init
this.ctx.clearRect(0, 0, 300, 300); // clear canvas
// get data
var in_video_data = this.data['in_video'];
// draw CircleLineTimeSeries
for (var i=0; i<in_video_data.length; i++){
var item = in_video_data[i];
// if counter in phase intervall
if (this.counter >= item['start'] && this.counter <= item['end']){
console.log(item);
if (item['object'] == 'CircleLineTimeSeries'){
this.visualizeCircleLine(item['raw_kps'][0][this.counter],
item['raw_kps'][1][this.counter]);
}
}
}
// Increase time variable
this.counter += 1;
}
if (this.running){
window.requestAnimationFrame(this.draw.bind(this));
}
}
}
visualizeCircleLine(kps0, kps1){
this.ctx.beginPath();
this.ctx.moveTo(kps0[0], kps0[1]);
this.ctx.lineTo(kps1[0], kps1[1]);
this.ctx.stroke();
}
play(){
this.running = true;
window.requestAnimationFrame(this.draw.bind(this));
}
pause(){
this.running = false;
}
}
The code for running the video and the canvas animation:
/**
* Event handler for play button
*/
function playPause(){
if (running){
running = false;
animation.pause();
video0.pause();
video1.pause();
} else {
running = true;
animation.play();
video0.play();
video1.play();
}
}
Thank you in advance :)
As #Kaiido noted, you have to listen for updates in current time of the video. There is respective media event - timeupdate. Furthermore you should have a list of key frames of the video when to show your animation. Whenever timeupdate event fires, check the current time for reaching some key frame and, if so, notify your requestanimationframe callback to run animation via some shared variable.
The problem is that when i have 2 or more players on html page i can only play music in the first player and the rest wont work.
my customized HTML5 audio player:
<audio id="music" preload="true">
<source src="music/interlude.mp3">
</audio>
<div id="wrapper">
<div id="audioplayer">
<button id="pButton" class="play"></button>
<div id="timeline">
<div id="playhead"></div>
</div>
</div>
</div>
and js for this:
document.addEventListener("DOMContentLoaded", function(event) {
var music = document.getElementById('music'); // id for audio element
var duration; // Duration of audio clip
var pButton = document.getElementById('pButton'); // play button
var playhead = document.getElementById('playhead'); // playhead
var timeline = document.getElementById('timeline'); // timeline
// timeline width adjusted for playhead
var timelineWidth = timeline.offsetWidth - playhead.offsetWidth;
// play button event listenter
pButton.addEventListener("click", play);
// timeupdate event listener
music.addEventListener("timeupdate", timeUpdate, false);
// makes timeline clickable
timeline.addEventListener("click", function(event) {
moveplayhead(event);
music.currentTime = duration * clickPercent(event);
}, false);
// returns click as decimal (.77) of the total timelineWidth
function clickPercent(event) {
return (event.clientX - getPosition(timeline)) / timelineWidth;
}
// makes playhead draggable
playhead.addEventListener('mousedown', mouseDown, false);
window.addEventListener('mouseup', mouseUp, false);
// Boolean value so that audio position is updated only when the playhead is released
var onplayhead = false;
// mouseDown EventListener
function mouseDown() {
onplayhead = true;
window.addEventListener('mousemove', moveplayhead, true);
music.removeEventListener('timeupdate', timeUpdate, false);
}
// mouseUp EventListener
// getting input from all mouse clicks
function mouseUp(event) {
if (onplayhead == true) {
moveplayhead(event);
window.removeEventListener('mousemove', moveplayhead, true);
// change current time
music.currentTime = duration * clickPercent(event);
music.addEventListener('timeupdate', timeUpdate, false);
}
onplayhead = false;
}
// mousemove EventListener
// Moves playhead as user drags
function moveplayhead(event) {
var newMargLeft = event.clientX - getPosition(timeline);
if (newMargLeft >= 0 && newMargLeft <= timelineWidth) {
playhead.style.marginLeft = newMargLeft + "px";
}
if (newMargLeft < 0) {
playhead.style.marginLeft = "0px";
}
if (newMargLeft > timelineWidth) {
playhead.style.marginLeft = timelineWidth + "px";
}
}
// timeUpdate
// Synchronizes playhead position with current point in audio
function timeUpdate() {
var playPercent = timelineWidth * (music.currentTime / duration);
playhead.style.marginLeft = playPercent + "px";
if (music.currentTime == duration) {
pButton.className = "";
pButton.className = "play";
}
}
//Play and Pause
function play() {
// start music
if (music.paused) {
music.play();
// remove play, add pause
pButton.className = "";
pButton.className = "pause";
} else { // pause music
music.pause();
// remove pause, add play
pButton.className = "";
pButton.className = "play";
}
}
// Gets audio file duration
music.addEventListener("canplaythrough", function() {
duration = music.duration;
}, false);
// getPosition
// Returns elements left position relative to top-left of viewport
function getPosition(el) {
return el.getBoundingClientRect().left;
}
/* DOMContentLoaded*/
});
i belive the problem is somewhere in the js code but i cant figure out where.
i took the code form here
I notice that you're using a lot of IDs. When the "first of something" works on a page, usually it's IDs that are causing the issue.
Change all of your IDs for your <audio> players to class attributes. You will also need to update your JavaScript as well, from something like this:
var music = document.getElementById('music');
music.addEventListener(...
To something more like this:
var music = document.querySelectorAll(".music"); // class="music"
for (var i = 0; i < music.length; i++) {
music[i].addEventListener(...
You should find that this will work for you.
I am fairly new to JavaScript and have searched everywhere for an answer to my question and cant seem to find anything related at all. This tells me that I'm missing something with my understanding of how my program works.
I have written a small game where the player navigates through a randomly generated maze using a gameloop that checks keydown events every x milliseconds. The game has a difficulty dropdown menu and then the game is started my clicking a button that calls a function to create a canvas where the game is drawn.
My problem is that when the button is clicked again to create a new maze without reloading the page, the gameloop for the original maze is still running and so key events are registered twice. This is causing some unexpected behavior. It's as though every time the button is clicked, a new instance of the function is running. Is there some way that each time the button is clicked I can set it to stop the previous game function?
var canvas;
var div;
var mazeGenButton;
$(document).ready(function () {
canvas = null;
div = document.getElementById('canvascontainer');;
mazeGenButton = document.getElementById("mazeGenButton");
mazeGenButton.onclick = createInstance;
});
function createInstance() {
if (canvas != null) {
div.removeChild(document.getElementById("myCanvas"));
}
canvas = document.createElement('canvas');
canvas.id = "myCanvas";
canvas.width = 1000;
canvas.height = 1000;
div.appendChild(canvas);
drawMaze();
};
var drawMaze = function () {
//code here to create the game(not posted)
//here is the Key listener - not sure if it's related
var keyState = {};
window.addEventListener('keydown', function (e) {
keyState[e.keyCode || e.which] = true;
}, true);
window.addEventListener('keyup', function (e) {
keyState[e.keyCode || e.which] = false;
}, true);
function gameLoop() {
//left
if (keyState[37] || keyState[65]) {
if (isLegalMove(playerXPos - 1, playerYPos)) {
grid[playerXPos][playerYPos].removePlayerCell();
playerXPos -= 1;
grid[playerXPos][playerYPos].setPlayerCell();
}
}
//right
if (keyState[39] || keyState[68]) {
if (isLegalMove(playerXPos + 1, playerYPos)) {
grid[playerXPos][playerYPos].removePlayerCell();
playerXPos += 1;
grid[playerXPos][playerYPos].setPlayerCell();
}
}
//up
if (keyState[38] || keyState[87]) {
if (isLegalMove(playerXPos, playerYPos - 1)) {
grid[playerXPos][playerYPos].removePlayerCell();
playerYPos -= 1;
grid[playerXPos][playerYPos].setPlayerCell();
}
}
//down
if (keyState[40] || keyState[83]) {
if (isLegalMove(playerXPos, playerYPos + 1)) {
grid[playerXPos][playerYPos].removePlayerCell();
playerYPos += 1;
grid[playerXPos][playerYPos].setPlayerCell();
}
}
drawSurroundingCells();
setTimeout(gameLoop, 50);
}
}
I'm making a simple canvas- game and I'm using requestAnimationFrame (Paul Irish's version) for the gameloop. I'm using a javascript countdown in the game (showing seconds and 100th of seconds), but it doesn't count down in the right speed. I know this has to do with the refresh- rate of the game, so that the counter updates every frame instead of every 100th second. How do I fix this?
Here's the timer object:
/**
* The timer as an object
*/
function Timer(position, time) {
this.position = position || new Vector(150,210);
this.time = time || 6000;
}
Timer.prototype = {
start: function() {
console.log('start running');
this.initial = this.time; //time in 100'ths of seconds
this.count = this.initial;
this.counter; //10 will run it every 100th of a second
clearInterval(this.counter);
//this.counter = setInterval(this.timer, 10);
this.timer();
},
timer: function() {
//console.log(this.count);
this.count--;
var res = this.count / 100;
//Show counter in canvas
return 'Tid kvar: ' + res.toPrecision(this.count.toString().length) + ' sekunder';
},
draw: function(ct) {
if(this.initial === undefined){this.start();} //Start the timer
ct.save();
if(this.count <=0){ //Remove timer if timer has reached 0
ct.clearRect(this.position.x, this.position.y, Racetrack.innerTrackWidth, Racetrack.innerTrackHeight);
return false;
} else { //draw timer
ct.save();
ct.font = 'bold 3em arial';
ct.fillStyle = 'orange';
ct.fillText(this.timer(), this.position.x, this.position.y);
}
ct.restore();
},
}
And the gameloop and the part calling the timer:
var init = function(canvas) {
timer = new Timer(new Vector(160,210), 3000);
}
var render = function() {
ct.clearRect(0,0,width,height);
ship.draw(ct);
racetrack.draw(ct);
//draw timer or results if timer reached 0
timer.draw(ct) !=false ? timer.draw(ct) : endFrame.draw(ct);
};
var gameLoop = function() {
var now = Date.now();
td = (now - (lastGameTick || now)) / 1000; // Timediff since last frame / gametick
lastGameTick = now;
if(timer.draw(ct) == false) { //stop the game if timer has reached 0.
cancelRequestAnimFrame(gameLoop);
console.log('Time\'s up!');
} else { //Otherwise, keep the game going.
requestAnimFrame(gameLoop);
}
update(td);
render();
};
I've also tried this as timer- object, but debugging this.count shows number the first loop, undefined the second loop and NaN every loop after that (And I'm not sure this will fix the timing issue either way?):
function Timer(position, time) {
this.position = position || new Vector(150,210);
this.time = time || 6000;
}
Timer.prototype = {
start: function() {
console.log('start running');
this.initial = this.time; //time in 100'ths of seconds
this.count = this.initial;
this.counter; //10 will run it every 100th of a second
clearInterval(this.counter);
this.counter = setInterval(this.timer, 10);
this.timer();
},
timer: function() {
console.log(this.count);
this.count--;
},
getTime: function() {
var res = this.count / 100;
return 'Tid kvar: ' + res.toPrecision(this.count.toString().length) + ' sekunder';
},
draw: function(ct) {
if(this.initial === undefined){this.start();} //Start the timer
ct.save();
if(this.count <=0){ //Remove timer if timer has reached 0
ct.clearRect(this.position.x, this.position.y, Racetrack.innerTrackWidth, Racetrack.innerTrackHeight);
return false;
} else { //draw timer
ct.save();
ct.font = 'bold 3em arial';
ct.fillStyle = 'orange';
ct.fillText(this.getTime(), this.position.x, this.position.y);
}
ct.restore();
},
}
Not sure if you are asking to display the time in 1/100 intervals or if the time is inaccurate when using setInterval.
A: setInterval should not be used for timing, as it is far from accurate and gets worse the smaller the interval, and even worse if you have animations running.
B: Browser's refresh at 1/60th of a second. You can force the display to render and present at 1/100 of a second but then depending on the graphics hardware it may never be displayed because the display is scanning pixels elsewhere on the screen at the time. Or you will get shearing when you overwrite the display just as the graphics is being written to the display.
Best way to get a countdown using requestAnimationFrame
var start = true; // flags that you want the countdown to start
var stopIn = 3000; // how long the timer should run
var stopTime = 0; // used to hold the stop time
var stop = false; // flag to indicate that stop time has been reached
var timeTillStop = 0; // holds the display time
// main update function
function update(timer){
if(start){ // do we need to start the timer
stopTime = timer + stopIn; // yes the set the stoptime
start = false; // clear the start flag
}else{ // waiting for stop
if(timer >= stopTime){ // has stop time been reached?
stop = true; // yes the flag to stop
}
}
timeTillStop = stopTime - timer; // for display of time till stop
// log() should be whatever you use to display the time.
log(Math.floor(timeTillStop / 10) ); // to display in 1/100th seconds
if(!stop){
requestAnimationFrame(update); // continue animation until stop
}
}
requestAnimationFrame(update); // start the animation
Forgot to add.
You are never going to get 100th of a second accuracy via that method. You will be on average 7-8 ms out. But unless you make it far more complex that is the best you can do. That 7-8ms average is constant and the same for 2 hours and 1 second and is just determined by the animation refresh time of about 16 odd ms.
I am building a simple 2D game as an attempt to learn canvas. The character can run around a virtual environment, and a variable called yOffset controls his offset from the top of the screen. I also have a global variable called running which sets itself to true or false based on whether or not the character is running (not shown here). My goal is to make the character bob up and down whilst he is running, and all the below code does is spawn lots of setInterval()s. Is this the right way to make my character run, or should I do it another way? If so, how?
$(document).keydown(function(e) {
if(e.which == 97) {
running = true;
run();
} else if(e.which == 100) {
running = true;
run();
} else if(e.which == 119) {
running = true;
run();
} else if(e.which == 115) {
running = true;
run();
}
});
(yes, if the character stops running, the running variable does go to false [not shown here] - I've already made sure the running variable works well)
runTimer = 0;
function run() {
if(runTimer == 0 && running) {
runTimer = 1;
yOffset = 80;
setTimeout(function() {
yOffset = 120;
}, 150);
setTimeout(function() { if (running) { runTimer = 0;run(); } }, 300);
}
}
If you need more information, the version that I am currently working on is available here.
I think you can simplify your code, and in fact you must in the quite probable case where you'd like to add some other characters.
To allow re-use of the animation, it's better to separate what is an animation (== the different steps that your character will go through), and an animation state (== in which step your character is now).
I wrote here some elements of an animation system.
So i define what is an animation step, a whole Animation (which is so far only an array of animation step), and an Animator (which holds the state, one might see it as a 'reader' of an animation).
Once you defined the animation and animators, and started the animators, you just have to call tick(time) to have the animation move on, and offset() to read the offset, which is way simpler than fighting with a bunch of setIntervals.
http://jsfiddle.net/xWwFf/
// --------------------
function AnimationStep(duration, offset) {
this.duration = duration;
this.offset = offset;
// you might add : image index, rotation, ....
}
// --------------------
function Animation(animationSteps) {
this.steps = animationSteps; // Array of AnimationStep
}
// define a read-only length property
Object.defineProperty(Animation.prototype, 'length', {
get: function () {
return this.steps.length
}
});
// --------------------
function Animator() {
this.currentAnimation = null;
this.step = -1;
this.running = false;
this.remainingTime = 0; // remaining time in current step;
}
Animator.prototype.startAnim = function (newAnim, firstStep) {
this.currentAnimation = newAnim;
this.step = firstStep || 0;
this.remainingTime = newAnim.steps[this.step].duration;
this.running = true;
}
Animator.prototype.tick = function (dt) {
// do nothing if no animation ongoing.
if (!this.running) return;
this.remainingTime -= dt;
// 'eat' as many frames as required to have a >0 remaining time
while (this.remainingTime <= 0) {
this.step++;
if (this.step == this.currentAnimation.length) this.step = 0;
this.remainingTime += this.currentAnimation.steps[this.step].duration;
}
};
Animator.prototype.offset = function () {
return this.currentAnimation.steps[this.step].offset;
}
// ______________________________
// example
var bounceAnim = [];
bounceAnim.push(new AnimationStep(200, 10));
bounceAnim.push(new AnimationStep(180, 20));
bounceAnim.push(new AnimationStep(150, 30));
bounceAnim.push(new AnimationStep(300, 40));
bounceAnim.push(new AnimationStep(320, 45));
bounceAnim.push(new AnimationStep(200, 40));
bounceAnim.push(new AnimationStep(120, 30));
bounceAnim.push(new AnimationStep(100, 20));
var anim1 = new Animation(bounceAnim);
var animator1 = new Animator();
var animator2 = new Animator();
animator1.startAnim(anim1);
animator2.startAnim(anim1, 3);
// in action :
var ctx = document.getElementById('cv').getContext('2d');
function drawScene() {
ctx.fillStyle = 'hsl(200,60%, 65%)';
ctx.fillRect(0, 0, 600, 200);
ctx.fillStyle = 'hsl(90,60%,75%)';
ctx.fillRect(0, 200, 600, 200);
ctx.fillStyle = 'hsl(10,60%,75%)';
ctx.fillRect(200, 200 + animator1.offset(), 22, 22);
ctx.fillStyle = 'hsl(40,60%,75%)';
ctx.fillRect(400, 200 + animator2.offset(), 22, 22);
animator1.tick(20);
animator2.tick(20);
}
setInterval(drawScene, 20);