diff --git a/demos/video/js/video.js b/demos/video/js/video.js index 54e1bc44..0af32b8d 100644 --- a/demos/video/js/video.js +++ b/demos/video/js/video.js @@ -9,7 +9,7 @@ Ox.load('UI', { videoSize = getVideoSize(), $videos = [ Ox.VideoPlayer({ - controls: ['play', 'mute', 'scale', 'timeline', 'position'], + controls: ['play', 'mute', 'fullscreen', 'scale', 'timeline', 'position'], enableKeyboard: true, focus: 'mouseenter', height: 192, @@ -27,9 +27,11 @@ Ox.load('UI', { width: 360 }) .css({ - margin: '16px' - }), + left: '16px', + top: '16px' + }), Ox.VideoPlayer({ + enableKeyboard: true, height: 192, 'in': 3128.725, //keepIconVisible: true, @@ -38,13 +40,13 @@ Ox.load('UI', { playInToOut: true, //preload: 'none', showIcon: true, - showIconOnLoad: true, timeline: timeline, video: url + '?' + + Ox.random(1000000), width: 360 }) .css({ - margin: '16px' + left: '16px', + top: '16px' }), Ox.VideoPlayer({ controls: ['play', 'playInToOut', 'mute', 'size', 'space', 'position'], @@ -60,7 +62,13 @@ Ox.load('UI', { width: videoSize.width }) .css({ - margin: '16px' + left: '16px', + top: '16px' + }) + .bindEvent({ + size: function() { + + } }) ]; window.$videos = $videos diff --git a/source/Ox.UI/js/Video/Ox.VideoPlayer.js b/source/Ox.UI/js/Video/Ox.VideoPlayer.js index b99b732f..dd6a9924 100644 --- a/source/Ox.UI/js/Video/Ox.VideoPlayer.js +++ b/source/Ox.UI/js/Video/Ox.VideoPlayer.js @@ -18,6 +18,7 @@ Ox.VideoPlayer Generic Video Player externalControls If true, controls are outside the video focus focus on 'click', 'load' or 'mouseover' fps Frames per second + fullscreen If true, video is in fullscreen height Height in px (excluding external controls) in In point (sec) keepIconVisible If true, play icon stays visible after mouseleave @@ -69,6 +70,7 @@ Ox.VideoPlayer = function(options, self) { externalControls: false, focus: 'click', fps: 25, + fullscreen: false, height: 144, 'in': 0, keepIconVisible: false, @@ -108,6 +110,12 @@ Ox.VideoPlayer = function(options, self) { position: 'absolute' }); + Ox.UI.$window.bind({ + resize: function() { + self.options.fullscreen && setSizes(); + } + }); + self['in'] = self.options.playInToOut ? self.options['in'] : 0, self.out = self.options.playInToOut ? self.options.out : self.options.duration; self.options.duration = self.out - self['in']; @@ -117,14 +125,18 @@ Ox.VideoPlayer = function(options, self) { self.millisecondsPerFrame = 1000 / self.options.fps; self.secondsPerFrame = 1 / self.options.fps; self.barHeight = 16; + self.width = self.options.fullscreen ? window.innerWidth : self.options.width; + self.height = self.options.fullscreen ? window.innerHeight : self.options.height; if (self.options.enableKeyboard) { that.bindEvent({ key_left: function() { - setPosition(self.options.position + self.secondsPerFrame); + setPosition(self.options.position - self.secondsPerFrame); + self.video.currentTime = self.options.position; }, key_right: function() { - setPosition(self.options.position - self.secondsPerFrame); + setPosition(self.options.position + self.secondsPerFrame); + self.video.currentTime = self.options.position; }, key_space: function() { self.$playButton && self.$playButton.toggleTitle(); @@ -260,6 +272,9 @@ Ox.VideoPlayer = function(options, self) { opacity: 0 }) .appendTo(self.$videoContainer); + if (self.options.showIcon) { + self.$playIcon.addClass('OxInterface'); + } if (self.options.showIconOnLoad) { self.iconIsVisible = true; } @@ -284,7 +299,6 @@ Ox.VideoPlayer = function(options, self) { .addClass('OxInterface') .css({ position: 'absolute', - width: self.options.width + 'px', height: (self.barHeight - 1) + 'px', paddingTop: '1px', textAlign: 'center', @@ -372,21 +386,6 @@ Ox.VideoPlayer = function(options, self) { } else if (control == 'volume') { - } else if (control == 'size') { - - self.$sizeButton = Ox.Button({ - style: 'symbol', - title: [ - {id: 'grow', title: 'grow', selected: !self.options.sizeIsLarge}, - {id: 'shrink', title: 'shrink', selected: self.options.sizeIsLarge} - ], - tooltip: ['Larger', 'Smaller'], - type: 'image' - }) - .css({float: 'left'}) - .bindEvent('click', toggleSize) - .appendTo(self.$controls); - } else if (control == 'scale') { self.$scaleButton = Ox.Button({ @@ -402,7 +401,37 @@ Ox.VideoPlayer = function(options, self) { .bindEvent('click', toggleScale) .appendTo(self.$controls); - } else if (control == 'timeline') { + } else if (control == 'fullscreen') { + + self.$fullscreenButton = Ox.Button({ + style: 'symbol', + title: [ + {id: 'grow', title: 'grow', selected: !self.options.fullscreen}, + {id: 'shrink', title: 'shrink', selected: self.options.fullscreen} + ], + tooltip: ['Enter Fullscreen', 'Exit Fullscreen'], + type: 'image' + }) + .css({float: 'left'}) + .bindEvent('click', toggleFullscreen) + .appendTo(self.$controls); + + } else if (control == 'size') { + + self.$sizeButton = Ox.Button({ + style: 'symbol', + title: [ + {id: 'grow', title: 'grow', selected: !self.options.sizeIsLarge}, + {id: 'shrink', title: 'shrink', selected: self.options.sizeIsLarge} + ], + tooltip: ['Larger', 'Smaller'], + type: 'image' + }) + .css({float: 'left'}) + .bindEvent('click', toggleSize) + .appendTo(self.$controls); + + }else if (control == 'timeline') { self.$timeline = Ox.Element() .addClass('timeline') @@ -543,14 +572,18 @@ Ox.VideoPlayer = function(options, self) { }) .bind({ blur: function() { - self.inputHasFocus = false; }, focus: function() { - self.inputHasFocus = true; } }) .bindEvent({ - blur: submitPositionInput + focus: function() { + self.inputHasFocus = true; + }, + blur: function() { + self.inputHasFocus = false; + submitPositionInput(); + } }) .hide() .appendTo(self.$controls.$element); @@ -623,15 +656,6 @@ Ox.VideoPlayer = function(options, self) { } } - function getPositionMarkerCSS() { - var position = self.options.duration ? - (self.options.position - self['in']) / self.options.duration : 0; - return { - marginLeft: position * self.timelineImageWidth - - self.timelineImageWidth - 8 + 'px', - }; - } - function getProgressImageURL() { Ox.print('---', self.timelineImageWidth) if (!self.timelineImageWidth) return; @@ -676,32 +700,6 @@ Ox.VideoPlayer = function(options, self) { return subtitle; } - function getVideoCSS() { - var playerWidth = self.options.width, - playerHeight = self.options.height, - playerRatio = playerWidth / playerHeight, - videoWidth = self.video.videoWidth, - videoHeight = self.video.videoHeight, - videoRatio = videoWidth / videoHeight, - videoIsWider = videoRatio > playerRatio, - width, height; - if (self.options.scaleToFill) { - width = videoIsWider ? playerHeight * videoRatio : playerWidth; - height = videoIsWider ? playerHeight : playerWidth / videoRatio; - } else { - width = videoIsWider ? playerWidth : playerHeight * videoRatio; - height = videoIsWider ? playerWidth / videoRatio : playerHeight; - } - width = Math.round(width); - height = Math.round(height); - return { - width: width + 'px', - height: height + 'px', - marginLeft: parseInt((playerWidth - width) / 2), - marginTop: parseInt((playerHeight - height) / 2) - }; - } - function hideInterface() { if (!self.inputHasFocus) { self.controlsTimeout = setTimeout(function() { @@ -738,7 +736,7 @@ Ox.VideoPlayer = function(options, self) { self.options.out : self.video.duration; self.options.duration = self.out - self['in']; self.video.currentTime = self.options.position; - self.$video.css(getVideoCSS()); + self.$video.css(getCSS('video')); hideLoadingIcon(); if (self.options.showIcon || self.options.showIconOnLoad) { @@ -848,11 +846,16 @@ Ox.VideoPlayer = function(options, self) { } function seeked() { + clearTimeout(self.seekTimeout); hideLoadingIcon(); + self.$playIcon && self.$playIcon.show(); } function seeking() { - showLoadingIcon(); + self.seekTimeout = setTimeout(function() { + self.$playIcon && self.$playIcon.hide(); + showLoadingIcon(); + }, 250); } function setPosition(position) { @@ -875,101 +878,176 @@ Ox.VideoPlayer = function(options, self) { self.posterIsVisible = false; } self.$subtitle && setSubtitle(); - self.$timeline && self.$positionMarker.css(getPositionMarkerCSS()); + self.$timeline && self.$positionMarker.css(getCSS('positionMarker')); self.$position && self.$position.html(formatPosition()); } - function setSizes() { - var iconSize = Math.max(Math.round(self.options.height / 10), 16), - iconLeft = parseInt((self.options.width - iconSize) / 2), - iconTop = parseInt((self.options.height - iconSize) / 2), - logoHeight, - playIconPadding, playIconSize, - playerHeight = self.options.height + ( - self.options.externalControls - ? (!!self.options.controls.length + !!self.options.title) * self.barHeight - : 0 - ), - timelineWidth; - that.css({ - position: 'absolute', - width: self.options.width + 'px', - height: playerHeight + 'px' - }); - self.$videoContainer.css({ - width: self.options.width + 'px', - height: self.options.height + 'px' - }); - self.loaded && self.$video.css(getVideoCSS()); - self.$poster && self.$poster.css({ - width: self.options.width + 'px', - height: self.options.height + 'px' - }); - if (self.$logo) { - logoHeight = Math.round(self.options.height / 10); - self.logoMargin = Math.round(self.options.height / 20); - self.$logo.css({ + function getCSS(element) { + var css; + if (element == 'controls') { + css = { + width: self.width + 'px' + }; + } else if (element == 'loadingIcon') { + css = { + left: self.iconLeft + 'px', + top: self.iconTop + 'px', + width: self.iconSize + 'px', + height: self.iconSize + 'px' + }; + } else if (element == 'logo') { + var logoHeight = Math.round(self.height / 10); + self.logoMargin = Math.round(self.height / 20); + css = { left: self.logoMargin + 'px', top: self.logoMargin + 'px', height: logoHeight + 'px', - }); - } - self.$loadingIcon.css({ - left: iconLeft + 'px', - top: iconTop + 'px', - width: iconSize + 'px', - height: iconSize + 'px' - }); - if (self.$playIcon) { - playIconPadding = Math.round(iconSize * 1/8); - playIconSize = iconSize - 2 * playIconPadding - 4; - self.$playIcon.css({ - left: iconLeft + 'px', - top: iconTop + 'px', + }; + } else if (element == 'player') { + if (!self.offset) { + self.offset = that.offset(); + } + css = { + left: self.options.fullscreen ? 0 : self.offset.left + 'px', + top: self.options.fullscreen ? 0 : self.offset.top + 'px', + width: self.width + 'px', + height: (self.options.fullscreen + ? window.innerHeight + : self.height + ( + self.options.externalControls + ? (!!self.options.controls.length + !!self.options.title) * self.barHeight + : 0)) + 'px' + } + } else if (element == 'playIcon') { + var playIconPadding = Math.round(self.iconSize * 1/8), + playIconSize = self.iconSize - 2 * playIconPadding - 4; + css = { + left: self.iconLeft + 'px', + top: self.iconTop + 'px', width: playIconSize + 'px', height: playIconSize + 'px', padding: playIconPadding + 'px', - borderRadius: Math.round(iconSize / 2) + 'px' - }); + borderRadius: Math.round(self.iconSize / 2) + 'px' + }; + } else if (element == 'positionMarker') { + var position = self.options.duration ? + (self.options.position - self['in']) / self.options.duration : 0; + css = { + marginLeft: position * self.timelineImageWidth - + self.timelineImageWidth - 8 + 'px', + }; + } else if (element == 'poster') { + css = { + width: self.width + 'px', + height: self.height + 'px' + }; + } else if (element == 'progress') { + css = { + width: self.timelineImageWidth + 'px', + marginLeft: -self.timelineImageWidth + 'px' + }; + } else if (element == 'subtitle') { + css = { + bottom: parseInt(self.height / 16) + 'px', + width: self.width + 'px', + fontSize: parseInt(self.height / 20) + 'px', + WebkitTextStroke: (self.height / 1000) + 'px rgb(0, 0, 0)' + }; + } else if (element == 'space') { + css = { + width: self.timelineWidth + 'px' + }; + } else if (element == 'timeline') { + css = { + width: self.timelineWidth + 'px' + }; + } else if (element == 'timelineImage') { + css = { + width: self.timelineImageWidth + 'px' + }; + } else if (element == 'timelineImages') { + css = { + width: self.timelineImageWidth + 'px' + }; + } else if (element == 'timelineInterface') { + css = { + width: self.timelineWidth + 'px', + marginLeft: -self.timelineWidth + 'px' + }; + } else if (element == 'titlebar') { + css = { + width: self.width + 'px' + }; + } else if (element == 'video') { + var playerWidth = self.width, + playerHeight = self.height, + playerRatio = playerWidth / playerHeight, + videoWidth = self.video.videoWidth, + videoHeight = self.video.videoHeight, + videoRatio = videoWidth / videoHeight, + videoIsWider = videoRatio > playerRatio, + width, height; + if (self.options.scaleToFill) { + width = videoIsWider ? playerHeight * videoRatio : playerWidth; + height = videoIsWider ? playerHeight : playerWidth / videoRatio; + } else { + width = videoIsWider ? playerWidth : playerHeight * videoRatio; + height = videoIsWider ? playerWidth / videoRatio : playerHeight; + } + width = Math.round(width); + height = Math.round(height); + css = { + width: width + 'px', + height: height + 'px', + marginLeft: parseInt((playerWidth - width) / 2), + marginTop: parseInt((playerHeight - height) / 2) + }; + } else if (element == 'videoContainer') { + css = { + width: self.width + 'px', + height: self.height + 'px' + }; } - self.$subtitle && self.$subtitle.css({ - bottom: parseInt(self.options.height / 16) + 'px', - width: self.options.width + 'px', - fontSize: parseInt(self.options.height / 20) + 'px', - WebkitTextStroke: (self.options.height / 1000) + 'px rgb(0, 0, 0)' - }); - self.$titlebar && self.$titlebar.css({ - width: self.options.width + 'px' - }); - self.$controls && self.$controls.css({ - width: self.options.width + 'px' - }); + return css; + } + + function setSizes(callback) { + var ms = callback ? 250 : 0; + self.width = self.options.fullscreen ? window.innerWidth : self.options.width; + self.height = self.options.fullscreen ? window.innerHeight : self.options.height; + self.iconSize = Math.max(Math.round(self.height / 10), 16), + self.iconLeft = parseInt((self.width - self.iconSize) / 2), + self.iconTop = parseInt((self.height - self.iconSize) / 2); if (self.$timeline || self.$space) { - timelineWidth = self.options.width - self.options.controls.reduce(function(prev, curr) { + self.timelineWidth = self.width - self.options.controls.reduce(function(prev, curr) { return prev + ( curr == 'timeline' || curr == 'space' ? 0 : curr == 'position' ? self.positionWidth : 16 ); }, 0); if (self.$timeline) { - self.timelineImageWidth = timelineWidth - self.barHeight; - self.$timeline.css({width: timelineWidth + 'px'}); - self.$timelineImages.css({width: self.timelineImageWidth + 'px'}); - self.$timelineImage.css({width: self.timelineImageWidth + 'px'}); - self.$progress && self.$progress.css({ - width: self.timelineImageWidth + 'px', - marginLeft: -self.timelineImageWidth + 'px' - }); - self.$positionMarker.css(getPositionMarkerCSS()); - self.$timelineInterface.css({ - width: timelineWidth + 'px', - marginLeft: -timelineWidth + 'px' - }); - } else { - self.$space.css({width: timelineWidth + 'px'}); + self.timelineImageWidth = self.timelineWidth - self.barHeight; } } - + that.animate(getCSS('player'), ms, callback); + self.$videoContainer.animate(getCSS('videoContainer'), ms); + self.loaded && self.$video.animate(getCSS('video'), ms); + self.$poster && self.$poster.animate(getCSS('poster'), ms); + self.$logo && self.$logo.animate(getCSS('logo'), ms); + self.$loadingIcon.animate(getCSS('loadingIcon'), ms); + self.$playIcon && self.$playIcon.animate(getCSS('playIcon'), ms); + self.$subtitle && self.$subtitle.animate(getCSS('subtitle'), ms); + self.$titlebar && self.$titlebar.animate(getCSS('titlebar'), ms); + self.$controls && self.$controls.animate(getCSS('controls'), ms); + if (self.$timeline) { + self.$timeline.animate(getCSS('timeline'), ms); + self.$timelineImages.animate(getCSS('timelineImages'), ms); + self.$timelineImage.animate(getCSS('timelineImage'), ms); + self.$progress && self.$progress.animate(getCSS('progress'), ms); + self.$positionMarker.animate(getCSS('positionMarker'), ms); + self.$timelineInterface.animate(getCSS('timelineInterface'), ms); + } + self.$space && self.$space.animate(getCSS('space'), ms); } function setSubtitle() { @@ -1033,6 +1111,39 @@ Ox.VideoPlayer = function(options, self) { } } + function toggleFullscreen() { + var wasPlaying; + self.options.fullscreen = !self.options.fullscreen; + if (!self.options.paused) { + self.video.pause(); + wasPlaying = true; + } + if (self.options.fullscreen) { + self.$parent = that.parent(); + self.offset = that.offset(); + that.detach() + .css({ + left: self.offset.left + 'px', + top: self.offset.top + 'px', + zIndex: 1000 + }) + .appendTo(Ox.UI.$body); + setSizes(function() { + wasPlaying && self.video.play(); + }); + } else { + setSizes(function() { + that.detach() + .css({ + left: self.offset.left + 'px', + top: self.offset.top + 'px' + }) + .appendTo(self.$parent); + wasPlaying && self.video.play(); + }); + } + } + function toggleMuted() { self.options.muted = !self.options.muted; self.video.muted = self.options.muted; @@ -1062,7 +1173,7 @@ Ox.VideoPlayer = function(options, self) { if (self.options.showIcon) { self.options.showIcon && self.$playIcon.animate({ opacity: 0 - }, 250/*, togglePlayIcon()*/); + }, 250, togglePlayIcon); } } } @@ -1077,7 +1188,7 @@ Ox.VideoPlayer = function(options, self) { function toggleScale() { self.options.scaleToFill = !self.options.scaleToFill; - self.$video.animate(getVideoCSS(), 250); + self.$video.animate(getCSS('video'), 250); } function toggleSize() { @@ -1088,7 +1199,9 @@ Ox.VideoPlayer = function(options, self) { } self.setOption = function(key, value) { - if (key == 'height' || key == 'width') { + if (key == 'fullscreen') { + toggleFullscreen(); + } else if (key == 'height' || key == 'width') { setSizes(); } else if (key == 'muted') { toggleMuted();