I've run into an issue using jPlayer and I thought after searching a few places that it would be used commonly enough on here and someone is bound to have run into a similar issue.
I am using the jPlayer to play through audio files and my users would like the functionality to fast-forward and rewind via specific hotkeys. I noticed that jPlayer doesn't offer this functionality out of the box (it only handles fast-forward and rewind through clicking on the progress bar currently)
Ideally - all that would be necessary would be for a single keypress to toggle the fast-forwarding (or rewinding). When the same key is pressed again, the audio file would begin playing based on the current location.
I decided to implement my own solution to this, which currently seems to work just fine. I thought I would share it in case anyone else encounters such an issue.
Pardon the crude implementation. It was just a proof of concept:
Necessary Javascript:
//Handles the key down event (so the user can hold a key down to continue)
$(document).keydown(function (e) {
//Rewind
if (e.keyCode == 37 && (!rewinding)) {
rewinding = true;
//Pause the player
$("#player").jPlayer("pause");
RewindTrack();
rwaction = window.setInterval(function () { RewindTrack() }, 500);
}
else if (e.keyCode == 39 && (!fastforward)) {
fastforward = true;
//Pause the player
$("#player").jPlayer("pause");
FastforwardTrack();
ffaction = window.setInterval(function () { FastforwardTrack() }, 500);
}
});
//Ends the action
$(document).keyup(function (e) {
//Rewind
if (e.keyCode == 37) {
rewinding = false;
window.clearInterval(rwaction);
$("#player").jPlayer("pause");
}
else if (e.keyCode == 39) {
fastforward = false;
window.clearInterval(ffaction);
$("#player").jPlayer("pause");
}
});
//Related function
function GetPlayerProgress() {
return ($('.jp-play-bar').width() / $('.jp-seek-bar').width() * 100);
}
//Handles rewinding
function RewindTrack() {
//Get current progress and decrement
var currentProgress = GetPlayerProgress();
//Rewinds 10% of track length
var futureProgress = currentProgress - 10;
//If it goes past the starting point - stop rewinding and pause
if (futureProgress <= 0) {
rewinding = false;
window.clearInterval(rwaction);
$("#player").jPlayer("pause", 0);
}
//Continue rewinding
else {
$("#player").jPlayer("playHead", parseInt(futureProgress, 10));
}
}
//Fast forwards the track
function FastforwardTrack() {
//Get current progress and increment
var currentProgress = GetPlayerProgress();
//Fast forwards 10%
var futureProgress = currentProgress + 10;
//If the percentage exceeds the max - stop fast forwarding at the end.
if (futureProgress >= 100) {
fastforward = false;
window.clearInterval(ffaction);
$("#player").jPlayer("playHead", parseInt($('.jp-duration').text().replace(':', '')));
}
else {
$("#player").jPlayer("playHead", parseInt(futureProgress, 10));
}
}
Working Demo (Use left arrow for rewind and right for fast-forward)
Related
I'm making a simple JavaScript game (Space Invaders style) (Top-down space shooter) and I'm trying to make my character shoot a bullet per each 'space' key press. How can I do that?
I have tried multiple approaches, setting a flag, using onkeypress instead of keydown, Google searches (have also encountered this similar question yet it didn't help: Javascript onkeydown event fire only once?)
Below is an example of one solution I have tried.
document.onkeydown = function(e)
{
if(e.keyCode == 32 && space == false)
{
space = true;
}
}
document.onkeyup = function(e)
{
if(e.keyCode == 32) space = false;
}
window.requestAnimationFrame(gameLoop);
function gameLoop(timeStamp)
{ if(space === true)
{
p.shoot();
}
window.requestAnimationFrame(gameLoop);
}
Expected results: Key being fired only once.
Actual results: Key is being fired multiple times.
There's really 3 states to a bullet - and, taking into consideration that in classic Space Invaders the player can only have one bullet in flight at a time, this makes things relatively simple
The bullet can be
NONE - doesn't exist
FIRED - i.e. need to create one
EXISTS - it's in flight
The code to handle it is relatively simple too
var BulletState = {
NONE: 0,
FIRED: 1,
EXISTS: 2
};
var bullet = BulletState.NONE;
document.onkeydown = function(e) {
if (e.keyCode == 32 && bullet === BulletState.NONE) {
bullet = BulletState.FIRED;
}
}
window.requestAnimationFrame(gameLoop);
function gameLoop(timeStamp) {
if (bullet === BulletState.FIRED) {
bullet = BulletState.EXISTS;
p.shoot(); // p.shoot needs to set bullet = BulletState.NONE when the bullet expires
}
window.requestAnimationFrame(gameLoop);
}
document.onkeyup = function(e)
{
// not required at all
}
edit: To fire a bullet every press of spacebar (allowing multiple bullets)
var space = false;
var fireBullet = 0;
document.onkeydown = function(e) {
if (e.keyCode == 32 && !e.repeat && !space) {
fireBullet++;
space = true;
}
}
window.requestAnimationFrame(gameLoop);
function gameLoop(timeStamp) {
while (fireBullet) {
p.shoot();
fireBullet--;
}
window.requestAnimationFrame(gameLoop);
}
document.onkeyup = function(e)
{
space = false;
}
I use fireBullet++ and fireBullet-- because I guess it's plausible that someone could press and release and press the spacebar within 16ms (single frame) :p
You could also do
function gameLoop(timeStamp) {
if (fireBullet) {
p.shoot();
fireBullet--;
}
window.requestAnimationFrame(gameLoop);
}
that way you still handle multiple bullets OK, but only fire off one per frame - really depends on how you want to handle someone with a very fast trigger finger :p
As #JaromandaX suggested, space = false; after p.shoot(); will give you the desired result:
Reproducing the issue:
var space = false;
var element = document.querySelector('input');
element.onkeydown = function(e) {
if (!space && e.keyCode == 32) {
space = true;
console.log('event fired');
}
};
element.onkeyup = function(e) {
if (e.keyCode == 32)
space = false;
}
window.requestAnimationFrame(gameLoop);
function gameLoop(timeStamp) {
if (space == true) {
console.log('shoot');
}
window.requestAnimationFrame(gameLoop);
}
<input />
EDIT: Added another isShot variable to keep track of shot fired and space key is down in requestAnimationFrame event:
var space = false;
var element = document.querySelector('input');
var isShot = false;
element.onkeydown = function(e) {
if (!space && e.keyCode == 32) {
space = true;
console.log('event fired');
}
};
element.onkeyup = function(e) {
if (e.keyCode == 32) {
space = isShot = false;
}
}
window.requestAnimationFrame(gameLoop);
function gameLoop(timeStamp) {
if (space && !isShot) {
console.log('shoot');
isShot = true;
}
window.requestAnimationFrame(gameLoop);
}
<input />
Here you have two event, on each key press three are three even that get's fired
1. keydown (when user press the key but not yet release the key)
2. keypress (its fired after the keydown event)
3. keyup (When user release the key)
If you want to fire only once for each keypress
implement on of those if implement all of the three all three methods will fired.
Additionally: for safety you can write e.preventDefault().
im creating a browser game and i have added keybinds so the player can use keys to move around the world.
If people use the buttons to move, these buttons are getting hidden for 5 seconds to allow an animation to end. Now i want to do this for the keybinds too.
For example: Player presses spacebar and a interval will run that lasts 5 seconds. Pressing space again will glitch out really bad running the same interval twice. So how do i disable a function for 5 seconds after it has been called so that it won't glitch out?
This is the code i use for the keys:
<script>
document.body.onkeyup = function(e){
if(e.keyCode == 32){
var cmd = "<?php echo $row['HEADING']; ?>";
if(cmd == "N") myMoveUp();
if(cmd == "E") myMoveRight();
if(cmd == "S") myMoveDown();
if(cmd == "W") myMoveLeft();
}
if(e.keyCode == 37){
ChangeHeadingKP("W");
}
if(e.keyCode == 38){
ChangeHeadingKP("N");
}
if(e.keyCode == 39){
ChangeHeadingKP("E");
}
if(e.keyCode == 40){
ChangeHeadingKP("S");
}
}
</script>
Any help would be appreciated.
I wouldn't rely on timing (say, 5 seconds) because when you change them, you'll have to review your code.
Instead I'd rely on toggling a flag by calling specific methods, like this:
keymanager.suspend();
// play animation, cutscene or whatever
keymanager.resume();
Those two functions are easy to define, along with the keymanager, as follows:
var keymanager = {
"active": true,
"suspend": () => {
keymanager.active = false;
},
"resume": () => {
keymanager.active = true;
}
};
All that remains is the key event handler bailing out when keys are deactivated:
document.body.onkeyup = function (e) {
if (!keymanager.active)
{
return; // do not process any keys
}
// rest of your code...
}
I was working on Babylon.js when I wanted to make a jump. I found a website and copied it jump code and it works perfectly! But I want to make a real game and I don't want people spamming spacebar to go flying. How could I make it have a few second delay so no one can keep on spamming spacebar and go flying?
function jump(){
camera.cameraDirection.y = 2;
}
document.body.onkeyup = function(e){
if(e.keyCode == 32){
//your code
console.log("jump");
setTimeout(jump(), 1000);
}
}
Link to my game
https://playground.babylonjs.com/#JCE1G3
One option would be to create a persistent boolean variable such as justJumped, and only jump if justJumped is false. When jumping, set justJumped to true, and create a timeout that resets it back to false after the duration of the jump, which looks to be a bit less than half a second:
let justJumped = false;
document.body.onkeyup = function(e) {
if (e.keyCode == 32 && !justJumped) {
justJumped = true;
setTimeout(() => justJumped = false, 400)
console.log("jump");
jump();
}
}
Also note that setTimeout(jump(), 1000); probably isn't doing what you're thinking it does - it invokes jump immediately. If you wanted to call the jump function after 1000ms, just pass the function name itself:
let justJumped = false;
document.body.onkeyup = function(e) {
if (e.keyCode == 32 && !justJumped) {
justJumped = true;
setTimeout(() => justJumped = false, 1400)
console.log("jump");
setTimeout(jump, 1000);
}
}
You can also use other library to achieve it. My favorite one is lodash. You can use _.throttle
var throttled = _.throttle(jump, 1000);
document.body.onkeyup = function(e) {
if (e.keyCode == 32) {
throttled()
}
}
if you don't want to jump immediately after the first key up. you can add option trailing: false
var throttled = _.throttle(jump, 1000, { 'trailing': false });
I'm trying to insert delays in a sequence of stimuli that are presented on the keypress event.
The code below shows how I'd tried to insert a 1000ms delay between stimuli (i.e. after the key was pressed, it should take 1000ms before the next stimulus appears). Yet there seems to be no delay. Where am I wrong (is it better to do this with setTimeout)?
And: how could I set the maximum 'display-time' of each stimulus to, say, 2000ms, so that it automatically presents another stimulus even if no key is pressed?
var stim = [
{name:"WORD1", path:".../pic1.jpg"},
{name:"WORD2", path:".../pic2.jpg"},
{name:"WORD3", path:".../pic3.jpg"},
]
$(function(){
$(document).keypress(function(e){
if ($(e.target).is('input, textarea')) {
return;
};
if (e.which === 97 || e.which === 108) {
if(Math.random() < 0.5) {
var new_word = stim[Math.floor((Math.random()*stim.length)+1)].name;
$("#abc").delay(1000).text(new_word);
} else {
var new_img = stim[Math.floor((Math.random()*stim.length)+1)].path;
$("#abc").delay(1000).empty();
var prox_img = $('<img id="abcimg" height="300px" width="300px">');
prox_img.attr('src', new_img);
prox_img.appendTo('#abc');
}
};
});
});
According to the documentation for delay: https://api.jquery.com/delay/
The .delay() method is best for delaying between queued jQuery
effects.
You should be using setTimeout for this since it also allows you to cancel it. That is really important for your need to have a maximum timeout.
So, first I would break your function up. You could have a function that handles the displaying, which will get called when timeout occurs or when a key is pressed.
$(function(){
var to = 0; //this is used to clear your timeout when the user clicks a button
function showNext() {
if(Math.random() < 0.5) {
var new_word = stim[Math.floor((Math.random()*stim.length)+1)].name;
$("#abc").delay(1000).text(new_word);
} else {
var new_img = stim[Math.floor((Math.random()*stim.length)+1)].path;
$("#abc").delay(1000).empty();
var prox_img = $('<img id="abcimg" height="300px" width="300px">');
prox_img.attr('src', new_img);
prox_img.appendTo('#abc');
}
to = setTimeout(function(){showNext()}, 2000);
}
$(document).keypress(function(e){
if ($(e.target).is('input, textarea')) {
return;
};
clearTimeout(to);
if (e.which === 97 || e.which === 108) {
setTimeout(function(){showNext();}, 1000);
}
});
});
When i press spacebar, the function shoot executes.
window.onkeydown=function(e){
var which = e.keyCode;
if(which == 32){
shoot();
}
}
If you hold space down, shoot calls many times in a row. I only want the function to execute once every 500ms.
(function($){
var lazerCharging = false,
lazerChargeTime = 500; // Charge time in ms
function handleKeyPress(e){
if(e.keyCode == 32){
shoot(lazerChargeTime);
}
}
function shoot(chargeTime){
if(!lazerCharging){
lazerCharging = true;
$("body").append("pew<br/>");
setTimeout(function(){
lazerCharging = false;
}, chargeTime)
}
}
$(window).on("keydown", handleKeyPress);
})($);
Here's a jsfiddle
You'll want to "debounce"
Using jQuery throttle / debounce, you can pass a delay and function to
$.debounce to get a new function, that when called repetitively,
executes the original function just once per "bunch" of calls.
This can be especially useful for rate limiting execution of handlers
on events that will trigger AJAX requests. Just take a look at this
example to see for yourself!
Ben Alman did the hard work for you here: http://benalman.com/code/projects/jquery-throttle-debounce/examples/debounce/
Essentially a debounce as MattC suggested. Store the time the function was called last and make sure 500 ms has passed. Also you probably should be using .addEventListener instead of window.onkeydown
(function() {
var lastCallTime = 0;
window.onkeydown = function(e){
var now = Date.now();
if(e.keyCode == 32 && now - lastCallTime > 500) {
shoot();
lastCallTime = now;
}
}
});
I doubt it's guaranteed that keydown/keypress events are always fired continuously. It may depend on the browser, operating system settings, etc. Even if they are, "fire rate" may fluctate. You probably don't want this.
I think a better idea would be to create a timer that's started when the first keydown event is fired, and stopped on keyup event.
http://jsfiddle.net/kPbLH/
var fireTimer = null;
function fire() {
// do whatever you need
}
document.addEventListener("keydown", function(e) {
if (e.keyCode == 32 && fireTimer === null) {
fire(); // fire immediately...
fireTimer = setInterval(fire, 500); // ...and 500ms, 1000ms and so on after
}
});
document.addEventListener("keyup", function(e) {
if (e.keyCode == 32 && fireTimer !== null) {
clearInterval(fireTimer);
fireTimer = null;
}
});