Theres something about a progressing YouTube video that makes it difficult to produce a smoothly animating progress bar. Even YouTube's built in progress bar isnt smooth. Yet I think there must be a way to do it.
I'm using custom controls for a YouTube embed and my progress bar is moving along in a jumpy manner rather than smoothly. Here's the code:
vidClock = setInterval(function() {
if (state == 1) {
var time = player.getCurrentTime();
var percent = (time / duration) * 100;
$seekSlider.css({
'width' : percent + '%'
});
}
}, 100);
This variable origins aren't all shown but what they represent should be apparent. How can I make the bar move smoothly?
I tried adding a CSS transition effect to the element and also tried setting smaller intervals such as 10,20, and 30 (and after suggestion, have tried using JQuery animate).
Example, using $.animate
as suggested by an answer below is not smooth: /
Theres something about a progressing YouTube video that makes it difficult to produce a smoothly animating progress bar. Even YouTube's built in progress bar isnt smooth. Yet I think there must be a way to do it.
I'm using custom controls for a YouTube embed and my progress bar is moving along in a jumpy manner rather than smoothly. Here's the code:
vidClock = setInterval(function() {
if (state == 1) {
var time = player.getCurrentTime();
var percent = (time / duration) * 100;
$seekSlider.css({
'width' : percent + '%'
});
}
}, 100);
This variable origins aren't all shown but what they represent should be apparent. How can I make the bar move smoothly?
I tried adding a CSS transition effect to the element and also tried setting smaller intervals such as 10,20, and 30 (and after suggestion, have tried using JQuery animate).
Example, using $.animate
as suggested by an answer below is not smooth: http://jsfiddle/e11oy0eu/290/
- 1 There must be some way to get a smooth progress bar animation. I notice even youtube's progress bar isnt smooth, but it seems like there must be a way to do it. – user5536767 Commented Jun 6, 2016 at 19:12
4 Answers
Reset to default 2You can use the .animate() function:
vidClock = setInterval(function() {
if (state == 1) {
var time = player.getCurrentTime();
var percent = (time / duration) * 100;
$seekSlider.animate({
'width' : percent + '%'
},500);
}
}, 500);
Or use CSS animations:
CSS:
.progress {
width: 0%;
height: 100%;
background-color: green;
transition: all 0.5s linear;
}
JS:
vidClock = setInterval(function() {
if (state == 1) {
var time = player.getCurrentTime();
var percent = (time / duration) * 100;
$seekSlider.css({
'width' : percent + '%'
});
}
}, 500);
I chose a different approach for fixing your problem, first of all I added Velocity.js to the project because as Alon said in his answer, it gives you a little better animations, then instead of setting a timer and changing the progress bar size based on the current time of the player, I created a progress bar that always animates to 100%, then used some events to stop the progress bar when the video play stops(by user or when it stops for buffering). While this strategy has its downsides, it gives you smoother animation.
Also for an even smoother animation, instead of changing the div width, I changed the .progress
div's position to absolute
and moved it using the transform:translateX
property.(I am not sure that how much effective was this change, maybe it was not effective at all).
.progress-container {
width: 640px;
height: 30px;
background-color: #efefef;
position: relative;
overflow: hidden;
}
.progress {
width: 100%;
height: 100%;
background-color: green;
position: absolute;
left: -100%;
top: 0;
}
After these changes, the handleState function looks like this:
function handleState(state) {
var $seekSlider = $('.progress');
var $seekContainer = $('.progress-container');
if (state == 1) { // when player starts playing
duration = player.getDuration();
currentTime = player.getCurrentTime();
currentLocation = ((currentTime/duration) * 100).toFixed(4);
$seekSlider
.velocity('stop')
.velocity({
'translateX': currentLocation + '%'
}, {duration: 0})
.velocity({
'translateX': '100%'
}, {
duration: Math.floor((duration-currentTime) * 1000),
easing: 'linear'
});
}
if (state == 2 || state == 3 || state == 0) {
$seekSlider.velocity('stop');
if (state == 3) {
duration = player.getDuration();
currentTime = player.getCurrentTime();
currentLocation = ((currentTime/duration) * 100).toFixed(4);
$seekSlider.velocity({
'translateX': currentLocation + '%'
}, {duration: 0});
}
if (state == 0) {
$seekSlider.velocity({
'translateX': '100%'
}, {duration: 0});
}
}
}
You can see the working result and plete code with some ments in this JSFiddle, Also I have created a gist that shows the differences between your script and my script.
The chunky progressing is not only caused by the getCurrentTime
function, but also by the way you are resizing the progress bar. Even though you are setting the CSS width
in percent, it will always render as whole pixels. You can use transform
instead, which will render "half" pixels as well.
Next, to overe the limitation of getCurrentTime
not updating on each call, you can save its value in a variable and increase it manually with the timer interval:
function handleState(state) {
var $seekSlider = $('.progress');
var $seekContainer = $('.progress-container');
if (state == 1) {
duration = player.getDuration();
var prevTime = 0;
var elapsed = 0;
vidClock = setInterval(function() {
elapsed += 0.05; // Increase with timer interval
if ((state == 1) && (seeking == false)) {
var time = player.getCurrentTime();
if (time != prevTime) {
elapsed = time; // Update if getCurrentTime was changed
prevTime = time;
}
var percent = (elapsed / duration);
$seekSlider.css({
'transform': 'scaleX(' + percent + ')'
});
}
}, 50);
} else {
clearInterval(vidClock);
}
}
See updated fiddle
You can use Velocity.js. Its animation is a lot smoother than jQuery and the configuration is as simple as the jQuery one.
look at this parison: http://webdesign.tutsplus./tutorials/silky-smooth-web-animation-with-velocityjs--cms-24266