有关此问题的演示,请参见此处:
https://gyazo.com/06e423d07afecfa2fbdb06a6da77f66a
我在取消暂停通知时出现跳跃行为。这也受鼠标停留在通知上多长时间以及进度接近结束的影响。
我已经尝试了很多事情,但我不确定是否确实是setTimeout问题。
这就像自从this.timerFinishesAt的计算到requestAnimationFrame的第一次迭代以来,由于等待cpu时间而导致进度跳跃?但是话又说回来,为什么它会受到悬停时间和进度的影响。
如何减轻跳跃行为?
在阅读其他stackoverflow问题时,我阅读/尝试从以下资源中实施此修复程序:
https://gist.github.com/tanepiper/4215634
How to create an accurate timer in javascript?
What is the reason JavaScript setTimeout is so inaccurate?
https://www.sitepoint.com/creating-accurate-timers-in-javascript/
https://github.com/Falc/Tock.js/tree/master
https://github.com/philipyoungg/timer
https://github.com/Aaronik/accurate_timer
https://github.com/husa/timer.js
timerStart(){
// new future date = future date + elapsed time since pausing
this.timerFinishesAt = new Date( this.timerFinishesAt.getTime() + (Date.now() - this.timerPausedAt.getTime()) );
// set new timeout
this.timerId = window.setTimeout(this.toggleVisibility, (this.timerFinishesAt.getTime() - Date.now()));
// animation start
this.progressId = requestAnimationFrame(this.progressBar);
},
timerPause(){
// stop notification from closing
window.clearTimeout(this.timerId);
// set to null so animation won't stay in a loop
this.timerId = null;
// stop loader animation from progressing
cancelAnimationFrame(this.progressId);
this.progressId = null;
this.timerPausedAt = new Date();
},
progressBar(){
if (this.progress < 100) {
let elapsed = Date.now() - this.timerStarted.getTime();
let wholeTime = this.timerFinishesAt.getTime() - this.timerStarted.getTime();
this.progress = Math.ceil((elapsed / wholeTime) * 100);
if (this.timerId) {
this.progressId = requestAnimationFrame(this.progressBar);
}
} else {
this.progressId = cancelAnimationFrame(this.progressId);
}
}
答案 0 :(得分:0)
我认为只需使用SetInterval就足够了:
const progressBar = {
MsgBox : document.querySelector('#Message'),
Info : document.querySelector('#Message h1'),
barr : document.querySelector('#Message progress'),
interV : 0,
DTime : 0,
D_Max : 0,
Init() {
this.MsgBox.onmouseover=_=> { // pause
clearInterval( this.interV )
}
this.MsgBox.onmouseout=_=>{ // restart
this._run()
}
},
Start(t,txt)
{
this.DTime = this.D_Max = t * 1000
this.barr.value = 0
this.barr.max = this.D_Max
this.Info.textContent = txt
this._run()
},
_run()
{
let D_End = new Date(Date.now() + this.DTime )
this.interV = setInterval(_=>{
this.DTime = D_End - (new Date(Date.now()))
if (this.DTime > 0) { this.barr.value = this.D_Max - this.DTime }
else { clearInterval( this.interV ); console.clear(); console.log( "finish" ) }
}, 100);
}
}
progressBar.Init()
progressBar.Start(10, 'Hello!') // 10 seconds
#Message {
box-sizing: border-box;
display: block;
float: right;
width: 200px;
height: 80px;
background-color: darkslategrey;
padding: 0 1em;
color:#e4a8b4;
cursor: pointer;
margin-right:1.5em;
}
#Message h1 { margin: .3em 0 0 0}
#Message progress { height: .1em; margin: 0; width:100%; background-color:black; }
#Message progress::-moz-progress-bar,
#Message progress::-webkit-progress-value { background-color:greenyellow; }
<div id="Message">
<progress value="50" max="100" ></progress>
<h1> </h1>
</div>
答案 1 :(得分:0)
在计算计时器的当前进度时,您没有考虑暂停时间。因此跳转:代码的这一部分仅知道startTime和currentTime,不会受到暂停的影响。
要避免这种情况,您可以在startTimer函数中累积所有这些暂停时间
class Timer {
constructor() {
this.progress = 0;
this.totalPauseDuration = 0;
const d = this.timerFinishesAt = new Date(Date.now() + 10000);
this.timerStarted = new Date();
this.timerPausedAt = new Date();
}
timerStart() {
const pauseDuration = (Date.now() - this.timerPausedAt.getTime())
this.totalPauseDuration += pauseDuration;
// new future date = future date + elapsed time since pausing
this.timerFinishesAt = new Date(this.timerFinishesAt.getTime() + pauseDuration);
// set new timeout
this.timerId = window.setTimeout(this.toggleVisibility.bind(this), (this.timerFinishesAt.getTime() - Date.now()));
// animation start
this.progressId = requestAnimationFrame(this.progressBar.bind(this));
}
timerPause() {
// stop notification from closing
window.clearTimeout(this.timerId);
// set to null so animation won't stay in a loop
this.timerId = null;
// stop loader animation from progressing
cancelAnimationFrame(this.progressId);
this.progressId = null;
this.timerPausedAt = new Date();
}
progressBar() {
if (this.progress < 100) {
let elapsed = (Date.now() - this.timerStarted.getTime()) - this.totalPauseDuration;
let wholeTime = this.timerFinishesAt.getTime() - this.timerStarted.getTime();
this.progress = Math.ceil((elapsed / wholeTime) * 100);
log.textContent = this.progress;
if (this.timerId) {
this.progressId = requestAnimationFrame(this.progressBar.bind(this));
}
} else {
this.progressId = cancelAnimationFrame(this.progressId);
}
}
toggleVisibility() {
console.log("done");
}
};
const timer = new Timer();
btn.onclick = e => {
if (timer.timerId) timer.timerPause();
else timer.timerStart();
};
<pre id="log"></pre>
<button id="btn">toggle</button>
或更新startTime,这似乎更可靠:
class Timer {
constructor() {
this.progress = 0;
const d = this.timerFinishesAt = new Date(Date.now() + 10000);
this.timerStarted = new Date();
this.timerPausedAt = new Date();
}
timerStart() {
const pauseDuration = (Date.now() - this.timerPausedAt.getTime())
// update timerStarted
this.timerStarted = new Date(this.timerStarted.getTime() + pauseDuration);
// new future date = future date + elapsed time since pausing
this.timerFinishesAt = new Date(this.timerFinishesAt.getTime() + pauseDuration);
// set new timeout
this.timerId = window.setTimeout(this.toggleVisibility.bind(this), (this.timerFinishesAt.getTime() - Date.now()));
// animation start
this.progressId = requestAnimationFrame(this.progressBar.bind(this));
}
timerPause() {
// stop notification from closing
window.clearTimeout(this.timerId);
// set to null so animation won't stay in a loop
this.timerId = null;
// stop loader animation from progressing
cancelAnimationFrame(this.progressId);
this.progressId = null;
this.timerPausedAt = new Date();
}
progressBar() {
if (this.progress < 100) {
let elapsed = Date.now() - this.timerStarted.getTime();
let wholeTime = this.timerFinishesAt.getTime() - this.timerStarted.getTime();
this.progress = Math.ceil((elapsed / wholeTime) * 100);
log.textContent = this.progress;
if (this.timerId) {
this.progressId = requestAnimationFrame(this.progressBar.bind(this));
}
} else {
this.progressId = cancelAnimationFrame(this.progressId);
}
}
toggleVisibility() {
console.log("done");
}
};
const timer = new Timer();
btn.onclick = e => {
if (timer.timerId) timer.timerPause();
else timer.timerStart();
};
<pre id="log"></pre>
<button id="btn">toggle</button>
关于最后的差距,没有看到此代码如何与您的UI链接,很难知道会发生什么。