I have being trying to figuring out a simple directive pattern for controlling html5 video/youtube video.
I wanted to do it the "Angular way", thus bind video's property to a object model. However I have some problem when dealing with "currentTime" property of a video, since it's constantly updating.
Here is what I got so far:
html control:
<!--range input that both show and control $scope.currentTime -->
<input type="range" min=0 max=60 ng-model="currentTime">
<!--bind main $scope.currentTime to someVideo directive's videoCurrentTime -->
<video some-video video-current-time="currentTime"> </video>
directive:
app.controller('MainCtrl', function ($scope) {
$scope.currentTime = 0;
})
app.directive('someVideo', function ($window) {
return{
scope: {
videoCurrentTime: "=videoCurrentTime"
},
controller: function ($scope, $element) {
$scope.onTimeUpdate = function () {
$scope.videoCurrentTime = $element[0].currentTime;
$scope.$apply();
}
},
link: function (scope, elm) {
scope.$watch('videoCurrentTime', function (newVar) {
elm[0].currentTime = newVar;
});
elm.bind('timeupdate', scope.onTimeUpdate);
}
}
})
JSFiddler: http://jsfiddle.net/vQ5wQ/
::
While this seems works, notice that everytime onTimeUpdate
fires, it triggers the $watch
.
For example, when the video runs to 10 secs, it notify onTimeUpdate to change the model to 10, and $watch will catch this change and ask the video to seek to 10 sec again.
This sometime creates a loop that causes video to lag from times to times.
Do you think there is a better way to do this? A way that won't trigger unwanted $watch ? Any suggestion is appreciated.
An excerpt from some timeupdate documentation says that the function bound to timeupdate is invoked when you
- Play the video
- Move the position indicator on the playback controls
So the event gets fired when the playback gets modified, and you don't need to explicitly $watch the model attached to that playback control.
This means that you can do both your updates in the timeupdate listener, instead of having the external-model-to-video assignment in a watch statement... but make sure you check a threshold like jkjustjoshing mentioned in his comment in order to be sure that the control has actually been moved.
$scope.onTimeUpdate = function () {
var currTime = $element[0].currentTime;
if (currTime - $scope.videoCurrentTime > 0.5 ||
$scope.videoCurrentTime - currTime > 0.5) {
$element[0].currentTime = $scope.videoCurrentTime;
}
$scope.$apply(function () {
$scope.videoCurrentTime = $element[0].currentTime;
});
};
Another note: I'm not sure this is relevant to you, but once the video has ended, it won't play again until you explicitly call play()
even if you set its currentTime
. To get around this you can use a $watch statement on videoCurrenTime
that restarts the video if it has ended.
Your original code wasn't very laggy, but here's an updated fiddle that seemed to have a couple seconds less lag (from my limited testing at least): http://jsfiddle.net/B7hT5/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With