UNPKG

filmstrip

Version:

html5 video preview widget

357 lines (312 loc) 11.3 kB
/* * Filmstrip.js: an HTML5 video preview widget that will rock your world * Copyright © <2013> Instituto Nacional de Associativismo y Economia Social. * Copyright © <2013> Cooperativa de Trabajo OpCode Limitada <info@opcode.coop>. * Copyright © <2013> Leonardo Vidarte <leo@opcode.coop> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ var Filmstrip = function(model, args) { this.model = null; this.video = null; this.canvas = null; this.context = null; this._frameHeight = 0; this._frameWidth = 0; this._framePadding = 0; this._count = 0; this._step = 0; this._maxSteps = 0; this._canvasDrawn = false; this._createCanvas = function() { return $('<canvas />') .attr({ width: this.width, height: this.height }) .get(0); }; __construct = function(self) { var opts = { width: 80, height: 400, bgColor: '#333', drawBackground: true, drawHoles: true, holesColor: '#222', holesDispersion: .8, bandsPadding: 20, orientation: 'vertical', autoOrientation: true, startAt: 5, strechOnResize: true, autoResize: true, } for (opt in opts) { self[opt] = (args[opt] !== undefined) ? args[opt] : opts[opt]; } self.model = model; self.canvas = self._createCanvas(); self.context = self.canvas.getContext('2d'); }(this); this.load = function() { var self = this; var video = $('<video preload="metadata" />') .attr('src', self.model.src) .bind('loadedmetadata', function() { self.video = this; if (self.autoResize) { self.resize(self.width, self.height); } $(self).trigger('loaded'); }) .bind('seeked', function() { self._drawFrame(); if (self._count < self._maxSteps - 1) { self._count++; self.video.currentTime += self._step; } else { self.setCanvas(self._tmpCanvas); self._destroyTmpCanvas(); self._canvasDrawn = true; $(self).trigger('draw:finished'); } }); }; this.resize = function(width, height) { if (width == 0 || height == 0) { return; } if (this.autoOrientation) { this.orientation = (width > height * 2) ? 'horizontal' : 'vertical'; } switch (this.orientation) { case 'horizontal': this.width = width; if (height !== undefined) { this.height = height; } this.attrTrans = { x: 'x', y: 'y', width: 'width', height: 'height', x0: 'x0', x1: 'x1', y0: 'y0', y1: 'y1', videoWidth: 'videoWidth', videoHeight: 'videoHeight', _frameWidth: '_frameWidth', _frameHeight: '_frameHeight', }; break; case 'vertical': if (height === undefined) { this.height = width; } else { this.width = width; this.height = height; } this.attrTrans = { x: 'y', y: 'x', width: 'height', height: 'width', x0: 'y0', x1: 'y1', y0: 'x0', y1: 'x1', videoWidth: 'videoHeight', videoHeight: 'videoWidth', _frameWidth: '_frameHeight', _frameHeight: '_frameWidth', }; break; } this[this.attrTrans._frameHeight] = ( this[this.attrTrans.height] - (this.bandsPadding * 2) ); this[this.attrTrans._frameWidth] = Math.ceil( this.video[this.attrTrans.videoWidth] * this[this.attrTrans._frameHeight] / this.video[this.attrTrans.videoHeight] ); this._maxSteps = Math.floor( this[this.attrTrans.width] / this[this.attrTrans._frameWidth] ); this._framePadding = ( (this[this.attrTrans.width] - (this._maxSteps * this[this.attrTrans._frameWidth]) ) / (this._maxSteps - 1) ); this._count = 0; if (this._maxSteps == 1) { this._framePadding = 0; this._step = this.video.duration - (this.startAt * 2); } else { this._step = ( (this.video.duration - (this.startAt * 2)) / (this._maxSteps - 1) ); } if (this._maxSteps) { this._createTmpCanvas(); $(this).trigger('draw:started'); this.video.currentTime = this.startAt; } }; this._drawFrame = function() { var _ = {}; _[this.attrTrans.y] = this.bandsPadding; _[this.attrTrans.x] = this[this.attrTrans._frameWidth] * this._count; if (this._maxSteps <= 5) { var extraPadding = ( (this[this.attrTrans.width] - (this[this.attrTrans._frameWidth] * this._maxSteps) ) / (this._maxSteps + 1) ); _[this.attrTrans.x] += extraPadding * (this._count + 1); } else { _[this.attrTrans.x] += this._framePadding * this._count; } this._tmpContext.drawImage( this.video, _.x, _.y, this._frameWidth, this._frameHeight ); $(this).trigger('draw:frame', { x: _.x, y: _.y, width: this._frameWidth, height: this._frameHeight, pos: this._count, steps: this._maxSteps, }); }; this.on = function(event, handler) { $(this).on(event, handler); }; this._createTmpCanvas = function() { this._tmpCanvas = this._createCanvas(); this._tmpContext = this._tmpCanvas.getContext('2d'); this.drawBaseFilmstrip(this._tmpContext); }; this._destroyTmpCanvas = function() { delete this._tmpCanvas; delete this._tmpContext; }; this.setCanvas = function(canvas) { this.width = canvas.width; this.height = canvas.height; this.canvas.width = canvas.width; this.canvas.height = canvas.height; this.context.drawImage(canvas, 0, 0); }; this.clearCanvas = function() { this.canvas.width = this.canvas.width; this.canvas.height = this.canvas.height; $(self).trigger('draw:finished'); }, this.drawCanvas = function(elem) { elem.width(this.width); elem.height(this.height); elem.get(0).width = this.width; elem.get(0).height = this.height; var context = elem.get(0).getContext('2d'); context.drawImage(this.canvas, 0, 0); }; this.drawFrame = function(elem, args) { elem.width(this.width); elem.height(this.height); var canvas = elem.get(0); var context = canvas.getContext('2d'); if (args.pos == 0) { canvas.width = this.width; canvas.height = this.height; if (this.strechOnResize && this._canvasDrawn) { context.drawImage( this.canvas, 0, 0, this._tmpCanvas.width, this._tmpCanvas.height ); } } var _ = {}; if (args.pos) { _[this.attrTrans.x] = args[this.attrTrans.x] - this._framePadding; _[this.attrTrans.width] = args[this.attrTrans.width] + this._framePadding; } else { _[this.attrTrans.x] = args[this.attrTrans.x]; _[this.attrTrans.width] = args[this.attrTrans.width]; } _[this.attrTrans.y] = 0; _[this.attrTrans.height] = this[this.attrTrans.height]; if (args.pos == args.steps - 1) { _[this.attrTrans.x] = Math.floor(_[this.attrTrans.x]); _[this.attrTrans.width] = this._tmpCanvas[this.attrTrans.width] - _[this.attrTrans.x]; } context.drawImage( this._tmpCanvas, _.x, _.y, _.width, _.height, _.x, _.y, _.width, _.height ); }; this.drawBaseFilmstrip = function(context) { if (this.drawBackground) { context.fillStyle = this.bgColor; context.fillRect(0, 0, context.canvas.width, context.canvas.height); } if (this.drawHoles && this.bandsPadding) { context.fillStyle = this.holesColor; var _ = {}; _[this.attrTrans.height] = this.bandsPadding / 2; _[this.attrTrans.width] = _[this.attrTrans.height] / 2; _[this.attrTrans.y0] = ( (this.bandsPadding - _[this.attrTrans.height]) / 2 ); _[this.attrTrans.y1] = ( context.canvas[this.attrTrans.height] - _[this.attrTrans.y0] - _[this.attrTrans.height] ); var total = Math.ceil( ((context.canvas[this.attrTrans.width] - (_[this.attrTrans.y0] * 2)) / _[this.attrTrans.width] / 2) * this.holesDispersion ); var sep = ( (context.canvas[this.attrTrans.width] - _[this.attrTrans.height] - (_[this.attrTrans.width] * total) ) / (total - 1) ); for (var i = 0; i < total; i++) { _[this.attrTrans.x0] = _[this.attrTrans.y0] + ( (_[this.attrTrans.width] + sep) * i ); _[this.attrTrans.x1] = _[this.attrTrans.x0]; context.fillRect(_.x0, _.y0, _.width, _.height); context.fillRect(_.x1, _.y1, _.width, _.height); } } }; };