Skip to content
This repository was archived by the owner on Nov 25, 2021. It is now read-only.
22 changes: 21 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ <h4 class="margin-top-30">Aditional options:</h4>
<span class="color-grey">// <span class="color-error">IMPORTANT</span> On iOS can't be used together with autoplay, autoplay will be disabled</span>
audio: false, <span class="color-grey">// can be true/false (it will use video file for audio), or selector for a separate audio file</span>
resetOnLastFrame: true, <span class="color-grey">// should video reset back to the first frame after it finishes playing</span>
loop: false
cuepoints: [], <span class="color-grey">// if set player will trigger "cuepoint" "event" on provided timepoints (timepoints are float seconds from video start)</span>
loop: false
});
</pre>

Expand All @@ -118,6 +119,25 @@ <h4 class="margin-top-30">Methods:</h4>

<span class="color-grey">// Draws current frame on canvas, should not be called manually</span>
canvasVideo.drawFrame()

<span class="color-grey">// subscribe callback on eventName "event". Callback will receive one argument - plain object with keys "time" (value of occured cuepoint) and "index" (index of cuepoint, starting from zero)</span>
canvasVideo.on(eventName, callback)

<span class="color-grey">// unsubscribes given callback from eventName (for this to work it is required that callback is saved as a variable, as opposed to providing anonymous function). If no callback is given, unsubscribes all callbacks from eventName.</span>
canvasVideo.off(eventName, callback)

<span class="color-grey">// fires eventName with eventData which will be passed to all callbacks registered for this eventName (normally this is called by player itself)</span>
canvasVideo.fire(eventName, eventData)

<span class="color-grey">/* supported "events" (they are not real JavaScript events. only player knows about them)
* ready - happens after drawing first frame on video (this happens on initialization). Data passed to callbacks is { width: pixels, height: pixels, duration: float seconds }
* progress - happens after new frame is drawed. Data is current video progress (float seconds)
* cuepoint - happens at time positions passed in "cuepoints" option or set via setCuepoints method. Data passed is { "time": cuepoint_value, "index": cuepoint_index }
* finish - happens after video is ended. No data is passed.
*/</span>

<span class="color-grey">// set cuepoints property (which also could be set via options on player creation). This is useful when you do not know in advance what is your cuepoints are and calculate them in "ready" callback using video duration for example.</span>
canvasVideo.setCuepoints()
</pre>

<h4 class="margin-top-30">Detecting iPhone:</h4>
Expand Down
59 changes: 57 additions & 2 deletions js/canvas-video-player.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ var CanvasVideoPlayer = function(options) {
audio: false,
timelineSelector: false,
resetOnLastFrame: true,
cuepoints: [],
loop: false
};

Expand Down Expand Up @@ -50,7 +51,7 @@ var CanvasVideoPlayer = function(options) {
if (this.options.audio) {
if (typeof(this.options.audio) === 'string'){
// Use audio selector from options if specified
this.audio = document.querySelectorAll(this.options.audio)[0];
this.audio = document.querySelector(this.options.audio);

if (!this.audio) {
console.error('Element for the "audio" not found');
Expand Down Expand Up @@ -92,6 +93,8 @@ CanvasVideoPlayer.prototype.init = function() {
if (this.options.hideVideo) {
this.video.style.display = 'none';
}

this.eventHandlers = {};
};

// Used most of the jQuery code for the .offset() method
Expand Down Expand Up @@ -140,6 +143,15 @@ CanvasVideoPlayer.prototype.bind = function() {
}
});

this.__fire_ready = function () {
self.fire('ready', {
'width': self.video.videoWidth,
'height': self.video.videoHeight,
'duration': self.video.duration
});
delete self.__fire_ready;
};

// Draws first frame
this.video.addEventListener('canplay', cvpHandlers.videoCanPlayHandler = function() {
self.drawFrame();
Expand All @@ -151,7 +163,7 @@ CanvasVideoPlayer.prototype.bind = function() {
}

if (self.options.autoplay) {
self.play();
self.play();
}

// Click on the video seek video
Expand Down Expand Up @@ -227,6 +239,36 @@ CanvasVideoPlayer.prototype.playPause = function() {
}
};

CanvasVideoPlayer.prototype.setCuepoints = function(cuepoints) {
this.options.cuepoints = cuepoints;
};

CanvasVideoPlayer.prototype.on = function(eventName, callback) {
if (!eventName || !callback) return this;
if (!this.eventHandlers[eventName]) this.eventHandlers[eventName] = [];
this.eventHandlers[eventName].push(callback);
return this;
};

CanvasVideoPlayer.prototype.off = function(eventName, callback) {
if (!callback) {
this.eventHandlers[eventName] = [];
return this;
}
this.eventHandlers[eventName] = this.eventHandlers[eventName].filter(function (handler) {
return (callback !== handler);
});
return this;
};

CanvasVideoPlayer.prototype.fire = function(eventName, eventData) {
if (!this.eventHandlers[eventName] || !this.eventHandlers[eventName].length) return;
var self = this;
this.eventHandlers[eventName].forEach(function (handler) {
handler.call(self, eventData);
});
};

CanvasVideoPlayer.prototype.loop = function() {
var self = this;

Expand All @@ -236,6 +278,16 @@ CanvasVideoPlayer.prototype.loop = function() {
// Render
if(elapsed >= (1 / this.options.framesPerSecond)) {
this.video.currentTime = this.video.currentTime + elapsed;
this.fire('progress', this.video.currentTime);
if (this.options.cuepoints.length) {
var latestCuepoint, index = 0;
while (typeof(this.options.cuepoints[index]) !== 'undefined' && this.options.cuepoints[index] < this.video.currentTime) {
latestCuepoint = this.options.cuepoints[index++];
}
if (latestCuepoint && latestCuepoint > this.video.currentTime - elapsed) {
this.fire('cuepoint', { 'time': latestCuepoint, 'index': index-1 });
}
}
this.lastTime = time;
// Resync audio and video if they drift more than 300ms apart
if(this.audio && Math.abs(this.audio.currentTime - this.video.currentTime) > 0.3){
Expand All @@ -246,6 +298,8 @@ CanvasVideoPlayer.prototype.loop = function() {
// If we are at the end of the video stop
if (this.video.currentTime >= this.video.duration) {
this.playing = false;
this.fire('finish');
this.off('finish');

if (this.options.resetOnLastFrame === true) {
this.video.currentTime = 0;
Expand All @@ -269,4 +323,5 @@ CanvasVideoPlayer.prototype.loop = function() {

CanvasVideoPlayer.prototype.drawFrame = function() {
this.ctx.drawImage(this.video, 0, 0, this.width, this.height);
if (this.__fire_ready) this.__fire_ready();
};