I've made a jQuery player for images Demo Link.
It changes the screens with provided intervals and draws touches on it. Now, I want to implement pouse, play functionality.
When I click on play button to stop screen playing, I call FlowPlaye.stop() method:
FlowPlayer.prototype.stop = function() {
$(".fp-pause").removeClass("fp-pause").addClass("fp-play");
clearInterval(this.screenIntervalId);
clearInterval(this.timeIntervalId);
clearInterval(this.touchIntervalId);
$('.fp-progress').stop();
this.isAnimated = false;
return false;
}
And at the second time FlowPlayer.play():
FlowPlayer.prototype.play = function() {
var fp = this; // Obj refers to the FlowPlayer itself such as "this"
fp.isAnimated = true;
console.log(typeof this.screenIndex)
console.log(this.screenIndex)
fp.screenIndex = typeof this.screenIndex == 'number' ? this.screenIndex : 0;
fp.render(fp.screens[fp.screenIndex]);
fp.initTimeline(fp.duration);
fp.screenIntervalId = setInterval(function() {
if (fp.screenIndex == fp.screens.length - 1) {
console.log("the end of screens");
clearInterval(fp.screenIntervalId)
return;
}
++fp.screenIndex;
fp.render(fp.screens[fp.screenIndex]);
}, fp.screens[fp.screenIndex].delay)
}
The problem is that when I do this, the screen playing intervals are messing (try to stop video at 20th second and restore). I need to save state of player, but I don't know how.
I think using 3 different timers is making this unnecessary difficult. If you refactor it into 1 unified timer, pausing (and other playback controls) would be quite easy.
Separate your keyframe events into separate functions:
function setImage(img) {...}
function showTouch(x, y) {...}
function hideTouch() {...}
On startup, convert your screens array to something like this:
var keyframes = [
{ time:0, func:setImage, args:['http://...']},
{ time:1000, func:showTouch, args:[10, 30]},
{ time:3000, func:hideTouch, args:[]},
...
];
Set up a single timer for playback:
var time = 0,
next = 0,
isPaused = false,
interval;
function timer() {
if (isPaused) {
return;
}
var nextKeyframe = keyframes[next];
time += 100;
if (time >= nextKeyframe.time) {
nextKeyframe.func.apply(this, nextKeyframe.args);
next += 1;
if (next === keyframes.length) {
clearInterval(interval);
}
}
}
Now, you have an easily controllable playback:
// play / replay - reset time and next, then start the timer
time = 0;
next = 0;
interval = setInterval(timer, 100);
// seek - just set a new time, and find the next keyframe
time = 1500;
for (next = 0; keyframes[next].time < time && next < keyframes.length; next++) {}
// pause - the timer stays on, but won't do anything
isPaused = true;
// stop
clearInterval(interval);
Note: The snippets are untested, may have some typos in them. I just wanted to demonstrate the process of making it cleaner / more controllable.
Related
Cannot terminate the setInterval I created in launch. It works until the time is up. I want to use clearInterval (interval) operation in next() function and prev() function. How should I do this? When I click forward, I want clearInterval(interval) to run this, but I couldn't.
function launch() {
thisTimeline = document.getElementsByClassName('story-active-' + start)[0];
var maxtime = 5000;
var incremental = 100;
var actualtime = 0;
var interval = setInterval(function() {
actualtime += incremental;
var percentage = Math.ceil((100 / maxtime) * actualtime);
thisTimeline.style.width = percentage + '%';
if (percentage == 100) {
clearInterval(interval);
thisTimeline.style.width = "0%";
}
}, incremental);
}
function next() {
// Set previous video timeline to 100% complete
thisTimeline.style.width = '100%';
// Advance play count to next video
start++;
// If next video doesn't exist (i.e. the previous video was the last) then close the Social Story popup
if (start >= defaults.playlist.length) {
setTimeout(function() {
close();
return false;
}, 400);
} else {
// Otherwise run the next video
launch(start);
}
}
function prev() {
if (start != 0) {
thisTimeline.style.width = '0%';
}
// Subtract play count to previous video
start--;
// If next video doesn't exist (i.e. the previous video was the last) then close the Social Story popup
if (start < 0) {
start = 0;
return false;
} else {
// Otherwise run the previous video
launch(start);
}
}
This is an extension of #lagoCalazans comment.
What he is saying is that in your variable "interval" is created in your launch function. You need to make "interval" global in order to clear your setInterval.
Ex:
let interval = null; //global
function launch() {
let tempInterval = setInterval(function() {
//whatever code
},100);
interval = setInterval(function(){
console.log("Hello");
}, 100);
}
function clear() {
//Since interval is global I can clear it when I call clear();
clearInterval(interval);
}
As you can see in the launch function "tempInterval" is limited to the scope of launch, therefore cannot be accessed anywhere else, but now since "interval" is global it can be accessed in any function.
Your code seems a bit incomplete, so for illustrative purposes only I will assume you encapsulate those functions in a higher order function (like an IIFE) and will avoid writing that (also, some kind of global state or variable would do for an example).
First of all, setInterval will return an id which you would use later, so if you want to use it within next and prev, you need that value to be available to them.
So, in your example, you should declare interval outside launch, and assign a value to it inside:
let interval
function launch() {
// ...
interval = setInterval(function() { ... })
}
and then use interval wherever you want.
launch, next and prev are three separate functions. They do not reference the same interval because they don't share scope. Raise the scope of the interval variable.
let interval = ''; // declared here, interval can be accessed by all functions
function launch() {
// ...
// remove the var before interval
interval = setInterval( ... )
}
function next() {
// ...
// remove the var before interval
interval = setInterval( ... )
}
function prev() {
// ...
// remove the var before interval
interval = setInterval( ... )
}
I developed a button with two states (Start and Stop).
When I start on one of the lines, a time count is started. When executing the button on a different line (start to start counting on another line), I want to pause the line that was previously active and start the timer on that new line.
My problem is that I am not able to implement this.
When I try to stop the previous line and start only on the new line, the timers simply stop :(
Can someone help me?
Stackblitz
ts
startTimer(data) {
let self = this;
self.taskService.startTimer(data);
self.currentState = self.taskService.getCurrentState();
const pauseIds = [];
this.data.forEach(d => {
if (d.id !== data.key.id && d.idUser !== data.key.idUser
&& this.taskService.fetchDisplay() !== undefined
&& this.taskService.currentState === 'start')
pauseIds.push(d.id);
});
pauseIds.forEach(id => {
console.log (id)
this.taskService.pauseTimer(id);
});
}
pauseTimer(data) {
let self = this;
self.taskService.pauseTimer(data);
self.currentState = self.taskService.getCurrentState();
}
service
startTimer(data) {
this.time = 0;
this.currentRowIndex = data.rowIndex;
this.currentState = 'start';
this.interval = setInterval(() => {
if (this.time === 0) {
this.time++;
} else {
this.time++;
}
this.display = this.time;
return this.display;
}, 1000);
}
pauseTimer(data) {
this.currentRowIndex = data.rowIndex;
this.currentState = 'pause';
this.time = 0;
clearInterval(this.interval);
}
fetchDisplay() {
return this.display;
}
getCurrentState() {
return this.currentState;
}
As Ivan Mihaylov mentioned in the comments the logic really seems too convoluted.
Nevertheless the solution is rather simple.
You should start you timer after you pause all other possible started timers. Otherwise you timer state is pause after startTimer finishes so conditions in the template prevent active state from displaying.
You need to reset display as well as time in taskService.startTimer method. Otherwise displayed time at the start contains the last value of previous counter.
Here is a fixed StackBlitz example.
I'm a bit of a beginner with Javascript and am struggling to figure out how to use a function of one instance to trigger a function in another instance, and vice versa, both of the same class. Let me explain what I mean.
My project is to build a Pomodoro Clock in Javascript. Pomodoro is a process where you work for a specified time (25 minutes, for example), then take a short break (5 mins), and then repeat. The clocks should run back to back, indefinitely or until the user stops it.
I need the completion of one clock to trigger the beginning of the other one, and vice versa.
I built a working program using completely separate, slightly varying functions for each timer (with much redundancy). I tried to simplify my code by creating a class of Timers and building each one from that. That's where I've got stuck.
I have a function in my Timer class which, when the timer reaches zero, needs to call the other timer's countdown to begin (line 126 on Codepen). How can I do that?
Thank you for any help you can offer.
Here's my project on Codepen: https://codepen.io/lieberscott/pen/baRpgx?editors=1010
And my Javascript code below:
let session; // session Timer object instance
let btimer; // break Timer object instance
let s_off; // boolean for whether session timer is off or on
let s_timer; // reference to session timer HTML element
let s_stop; // reference to session stop HTML button
let s_increase; // reference to session increase HTML button
let s_decrease; // reference to session decrease HTML button
// same variables as above for break timer
let b_off;
let b_timer;
let b_stop;
let b_increase;
let b_decrease;
$(document).ready(function() {
s_off = true;
s_timer = $("#timer");
s_stop = $("#stop");
s_increase = $("#increase");
s_decrease = $("#decrease");
b_off = true;
b_timer = $("#breaktimer");
b_stop = $("#breakstop");
b_increase = $("#breakincrease");
b_decrease = $("#breakdecrease");
session = new Timer(1, 60, s_off, s_timer, s_stop, s_increase, s_decrease);
btimer = new Timer(5, 60, b_off, b_timer, b_stop, b_increase, b_decrease);
// increase session minutes
$(s_increase).on("click", function() {
if (session.off) {
session.min++;
session.sec = 00;
s_timer.html(session.min + ":" + session.sec);
}
});
// decrease session minutes
$(s_decrease).on("click", function() {
if (session.off) {
if (session.min > 1) {
session.min--;
}
session.sec = 00;
s_timer.html(session.min + ":" + session.sec);
}
});
// increase break minutes
$(b_increase).on("click", function() {
if (btimer.off) {
btimer.min++;
btimer.sec = 00;
b_timer.html(btimer.min + ":" + btimer.sec);
}
});
// decrease break minutes
$(b_decrease).on("click", function() {
if (btimer.off) {
if (btimer.min > 1) {
btimer.min--;
}
btimer.sec = 00;
b_timer.html(btimer.min + ":" + btimer.sec);
}
});
// begin session timer by clicking on the timer itself
$(s_timer).on("click", function() {
session.time();
});
// stop session timer
$(s_stop).on("click", function() {
session.off = true;
session.stopClock(session.intervalFunction);
});
// stop break timer
$(b_stop).on("click", function() {
btimer.off = true;
btimer.stopClock(btimer.intervalFunction);
});
});
class Timer {
constructor(min, sec, off, disp, stopButton, increaseButton, decreaseButton) {
this.min = min; // minutes
this.minsSet = min; // minutes again, this will be used to reset the timer
this.sec = sec;
this.off = off; // boolean saying whether timer is off or not
this.disp = disp; // HTML display
this.stopButton = stopButton;
this.increaseButton = increaseButton;
this.decreaseButton = decreaseButton;
this.func;
}
time() { // function fired when the timer is clicked
if (this.off) {
this.off = false;
this.func = this.intervalFunc();
}
}
intervalFunc() { // set the interval of the timer
setInterval(function() {this.countdown();}, 1000); // ERROR HERE
}
countdown() { // interval to complete for duration of timer
// check if clock reaches zero
if (this.sec == 0) {
this.min--;
this.sec = 60;
if (this.min < 0) {
this.min = this.minsSet;
this.sec = 0;
this.off = true;
this.time(); // this needs to trigger, not this.time(), but the OTHER object's time() function
this.stopClock(this.func); // clearInterval() function below
}
}
// if clock is not at 0:00, display new time
this.sec--;
let m = this.min.toString();
let s;
if (this.sec < 10) {
s = "0" + this.sec.toString()
}
else {
s = this.sec.toString();
}
this.disp.html(m + ":" + s);
}
stopClock() {
clearInterval(this.func);
}
}
1) I tried your code and fixed some bugs, your setInterval issue is because "this" points to window object there.
2) for calling the other object time() method, first off you need something to know which object you are working with, so I have added a type variable to the class and then in the countdown function i have added a check.
Changes are in this pen :
https://codepen.io/yaduvanshi/pen/dJRdeR?editors=0010
intervalFunc() { // set the interval of the timer
var that =this;
setInterval(function() {that.countdown();}, 1000); // ERROR HERE
}
I think the solution that you are looking for, is the .bind() or the .call() that Javascript provides you with (Function.prototype.bind()). For example, the .bind() function takes the specific object instance as argument. You can read up on the same here
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind
So, I have a JavaScript issue that is probably as old as time, but I can't seem to figure it out. I have a form that takes up the whole page and has next and back buttons so the user can travel back and forth through the questions. (Single page app, no full refresh. If there was this would be easy!)
One of the requirements is to have a timeout function. So after they are idle after a cretin amount of time it logs them out to protect their information. The issue is that my interval keeps adding a new one every time I flip back and forth new pages. Thus creating multiple timers and insanity.
My code kind of looks like this.
var idle = 0;
var idleIntervalTimer = setInterval(timerIncrement, 1000); //This is what keeps being called
var counter = 0;
var timeout = 30;
var count = 20;
var resetCount = 20;
document.onmousemove = function() {
if(app.timer.get('available') === 'Yes'){ //I'm just using this to make sure that they have not already been logged out
count = resetCount;
idle = 0;
app.warning.close();
}
};
function timerIncrement() {
if(app.timer.get('available') === 'Yes'){
console.log(idle);
idle = idle + 1;
if (idle > 19 && count >= 0) {
count = count;
app.warning.show({ //This is just a warning message that shows up at the top of the page counting down before the app closes.
message: 'Logging out in <span class="warn">' + count + '</span> seconds.'
});
if(count != null || count >= 0){
count=count-1;
if (count === 0 && count != 'null'){
resetTimer();
app.workflow.resetWorkflow();
}
}
}
}
}
function resetTimer(){
count = resetCount;
idle = 0;
clearInterval(counter);
clearInterval(idleIntervalTimer);
app.warning.close();
}
I have tried a few different things. One being that I would set a blank variable at the top and use that blank variable to set the interval, but that didn't seem to work. I have also tried to detect if the interval was still active, but I don't really want to clear the interval, just not create another. Some help would be apprenticed very much!
I'm assuming that code gets called on every page flip. Don't set the interval timer immediately. You should create a function that will first clear it and then set.
For example do this instead:
var idleIntervalTimer;
function initIdleIntervalTimer(){
clearInterval(idleIntervalTimer);
idleIntervalTimer = setInterval(timerIncrement, 1000);
}
initIdleIntervalTimer();
Or try switching it the timer to the global scope, get rid of var idleIntervalTimer and do this:
clearInterval(window.idleIntervalTimer);
window.idleIntervalTimer = setInterval(timerIncrement, 1000);
It's a little ugly, but
window.clearInterval(localStorage['idleInterval'])
var idleIntervalTimer = setInterval(timerIncrement, 1000); //This is what keeps being called
window.localStorage['idleInterval'] = idleIntervalTimer;
EDIT: If you dont want to clear, wrap the entire init in a flag, eg
if (!window._intervalInitOccured){
window._intervalInitOccured = true;
//continue init
Using Javascript/jQuery, how can I pause (or resume) the following loop when the "P" key is pressed?
(function() {
var arr = [...],
len = arr.length;
(function doProcess(i) {
if (i) {
console.log(len - i);
/* do something with arr[len - i] */
setTimeout(function() { doProcess(--i); }, 20000);
}
})(len);
})();
Pausing and resuming is fairly complex. What you really have to do is this:
Start a process and store its timeout ID.
Store the time when you ran that process.
On keypress, clear the timeout using its timeout ID.
Store the unfinished time in another variable.
On next keypress, set the timeout using that unfinished time.
Set the following timeouts to the original intended delay.
Here's a more generalized jsFiddle example I whipped up.
var counterOn = true;
var delay = 3000;
var lastRun;
var tempDelay;
var intervalId;
function decrementCounter() {
// do something
lastRun = new Date();
timeoutId = setTimeout(decrementCounter, delay);
}
function toggleCounter() {
var curTime = new Date();
counterOn = !counterOn;
if (counterOn) {
lastRun = curTime.valueOf() + tempDelay - delay;
timeoutId = setTimeout(decrementCounter, tempDelay);
} else {
clearTimeout(timeoutId);
tempDelay = delay - (curTime.valueOf() - lastRun);
}
}
$(document).keydown(function(e) {
if (e.which === 80) {
toggleCounter();
}
});
decrementCounter();
You'll want to keep track of how much time has passed with your timer (see here: javascript: pause setTimeout();) and call clearTimeout on whatever event you want to stop, then call setTimeout again with your remaining time left once whatever event unpauses is fired again.