add fullscreen mode to video player

This commit is contained in:
rolux 2011-05-15 09:26:00 +02:00
parent 4255470e29
commit 609ffdd4d7
2 changed files with 265 additions and 144 deletions

View file

@ -9,7 +9,7 @@ Ox.load('UI', {
videoSize = getVideoSize(), videoSize = getVideoSize(),
$videos = [ $videos = [
Ox.VideoPlayer({ Ox.VideoPlayer({
controls: ['play', 'mute', 'scale', 'timeline', 'position'], controls: ['play', 'mute', 'fullscreen', 'scale', 'timeline', 'position'],
enableKeyboard: true, enableKeyboard: true,
focus: 'mouseenter', focus: 'mouseenter',
height: 192, height: 192,
@ -27,9 +27,11 @@ Ox.load('UI', {
width: 360 width: 360
}) })
.css({ .css({
margin: '16px' left: '16px',
}), top: '16px'
}),
Ox.VideoPlayer({ Ox.VideoPlayer({
enableKeyboard: true,
height: 192, height: 192,
'in': 3128.725, 'in': 3128.725,
//keepIconVisible: true, //keepIconVisible: true,
@ -38,13 +40,13 @@ Ox.load('UI', {
playInToOut: true, playInToOut: true,
//preload: 'none', //preload: 'none',
showIcon: true, showIcon: true,
showIconOnLoad: true,
timeline: timeline, timeline: timeline,
video: url + '?' + + Ox.random(1000000), video: url + '?' + + Ox.random(1000000),
width: 360 width: 360
}) })
.css({ .css({
margin: '16px' left: '16px',
top: '16px'
}), }),
Ox.VideoPlayer({ Ox.VideoPlayer({
controls: ['play', 'playInToOut', 'mute', 'size', 'space', 'position'], controls: ['play', 'playInToOut', 'mute', 'size', 'space', 'position'],
@ -60,7 +62,13 @@ Ox.load('UI', {
width: videoSize.width width: videoSize.width
}) })
.css({ .css({
margin: '16px' left: '16px',
top: '16px'
})
.bindEvent({
size: function() {
}
}) })
]; ];
window.$videos = $videos window.$videos = $videos

View file

@ -18,6 +18,7 @@ Ox.VideoPlayer <f> Generic Video Player
externalControls <b|false> If true, controls are outside the video externalControls <b|false> If true, controls are outside the video
focus <s|'click'> focus on 'click', 'load' or 'mouseover' focus <s|'click'> focus on 'click', 'load' or 'mouseover'
fps <n|25> Frames per second fps <n|25> Frames per second
fullscreen <b|false> If true, video is in fullscreen
height <n|144> Height in px (excluding external controls) height <n|144> Height in px (excluding external controls)
in <n> In point (sec) in <n> In point (sec)
keepIconVisible <b|false> If true, play icon stays visible after mouseleave keepIconVisible <b|false> If true, play icon stays visible after mouseleave
@ -69,6 +70,7 @@ Ox.VideoPlayer = function(options, self) {
externalControls: false, externalControls: false,
focus: 'click', focus: 'click',
fps: 25, fps: 25,
fullscreen: false,
height: 144, height: 144,
'in': 0, 'in': 0,
keepIconVisible: false, keepIconVisible: false,
@ -108,6 +110,12 @@ Ox.VideoPlayer = function(options, self) {
position: 'absolute' position: 'absolute'
}); });
Ox.UI.$window.bind({
resize: function() {
self.options.fullscreen && setSizes();
}
});
self['in'] = self.options.playInToOut ? self.options['in'] : 0, self['in'] = self.options.playInToOut ? self.options['in'] : 0,
self.out = self.options.playInToOut ? self.options.out : self.options.duration; self.out = self.options.playInToOut ? self.options.out : self.options.duration;
self.options.duration = self.out - self['in']; self.options.duration = self.out - self['in'];
@ -117,14 +125,18 @@ Ox.VideoPlayer = function(options, self) {
self.millisecondsPerFrame = 1000 / self.options.fps; self.millisecondsPerFrame = 1000 / self.options.fps;
self.secondsPerFrame = 1 / self.options.fps; self.secondsPerFrame = 1 / self.options.fps;
self.barHeight = 16; 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) { if (self.options.enableKeyboard) {
that.bindEvent({ that.bindEvent({
key_left: function() { key_left: function() {
setPosition(self.options.position + self.secondsPerFrame); setPosition(self.options.position - self.secondsPerFrame);
self.video.currentTime = self.options.position;
}, },
key_right: function() { key_right: function() {
setPosition(self.options.position - self.secondsPerFrame); setPosition(self.options.position + self.secondsPerFrame);
self.video.currentTime = self.options.position;
}, },
key_space: function() { key_space: function() {
self.$playButton && self.$playButton.toggleTitle(); self.$playButton && self.$playButton.toggleTitle();
@ -260,6 +272,9 @@ Ox.VideoPlayer = function(options, self) {
opacity: 0 opacity: 0
}) })
.appendTo(self.$videoContainer); .appendTo(self.$videoContainer);
if (self.options.showIcon) {
self.$playIcon.addClass('OxInterface');
}
if (self.options.showIconOnLoad) { if (self.options.showIconOnLoad) {
self.iconIsVisible = true; self.iconIsVisible = true;
} }
@ -284,7 +299,6 @@ Ox.VideoPlayer = function(options, self) {
.addClass('OxInterface') .addClass('OxInterface')
.css({ .css({
position: 'absolute', position: 'absolute',
width: self.options.width + 'px',
height: (self.barHeight - 1) + 'px', height: (self.barHeight - 1) + 'px',
paddingTop: '1px', paddingTop: '1px',
textAlign: 'center', textAlign: 'center',
@ -372,21 +386,6 @@ Ox.VideoPlayer = function(options, self) {
} else if (control == 'volume') { } 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') { } else if (control == 'scale') {
self.$scaleButton = Ox.Button({ self.$scaleButton = Ox.Button({
@ -402,7 +401,37 @@ Ox.VideoPlayer = function(options, self) {
.bindEvent('click', toggleScale) .bindEvent('click', toggleScale)
.appendTo(self.$controls); .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() self.$timeline = Ox.Element()
.addClass('timeline') .addClass('timeline')
@ -543,14 +572,18 @@ Ox.VideoPlayer = function(options, self) {
}) })
.bind({ .bind({
blur: function() { blur: function() {
self.inputHasFocus = false;
}, },
focus: function() { focus: function() {
self.inputHasFocus = true;
} }
}) })
.bindEvent({ .bindEvent({
blur: submitPositionInput focus: function() {
self.inputHasFocus = true;
},
blur: function() {
self.inputHasFocus = false;
submitPositionInput();
}
}) })
.hide() .hide()
.appendTo(self.$controls.$element); .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() { function getProgressImageURL() {
Ox.print('---', self.timelineImageWidth) Ox.print('---', self.timelineImageWidth)
if (!self.timelineImageWidth) return; if (!self.timelineImageWidth) return;
@ -676,32 +700,6 @@ Ox.VideoPlayer = function(options, self) {
return subtitle; 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() { function hideInterface() {
if (!self.inputHasFocus) { if (!self.inputHasFocus) {
self.controlsTimeout = setTimeout(function() { self.controlsTimeout = setTimeout(function() {
@ -738,7 +736,7 @@ Ox.VideoPlayer = function(options, self) {
self.options.out : self.video.duration; self.options.out : self.video.duration;
self.options.duration = self.out - self['in']; self.options.duration = self.out - self['in'];
self.video.currentTime = self.options.position; self.video.currentTime = self.options.position;
self.$video.css(getVideoCSS()); self.$video.css(getCSS('video'));
hideLoadingIcon(); hideLoadingIcon();
if (self.options.showIcon || self.options.showIconOnLoad) { if (self.options.showIcon || self.options.showIconOnLoad) {
@ -848,11 +846,16 @@ Ox.VideoPlayer = function(options, self) {
} }
function seeked() { function seeked() {
clearTimeout(self.seekTimeout);
hideLoadingIcon(); hideLoadingIcon();
self.$playIcon && self.$playIcon.show();
} }
function seeking() { function seeking() {
showLoadingIcon(); self.seekTimeout = setTimeout(function() {
self.$playIcon && self.$playIcon.hide();
showLoadingIcon();
}, 250);
} }
function setPosition(position) { function setPosition(position) {
@ -875,101 +878,176 @@ Ox.VideoPlayer = function(options, self) {
self.posterIsVisible = false; self.posterIsVisible = false;
} }
self.$subtitle && setSubtitle(); self.$subtitle && setSubtitle();
self.$timeline && self.$positionMarker.css(getPositionMarkerCSS()); self.$timeline && self.$positionMarker.css(getCSS('positionMarker'));
self.$position && self.$position.html(formatPosition()); self.$position && self.$position.html(formatPosition());
} }
function setSizes() { function getCSS(element) {
var iconSize = Math.max(Math.round(self.options.height / 10), 16), var css;
iconLeft = parseInt((self.options.width - iconSize) / 2), if (element == 'controls') {
iconTop = parseInt((self.options.height - iconSize) / 2), css = {
logoHeight, width: self.width + 'px'
playIconPadding, playIconSize, };
playerHeight = self.options.height + ( } else if (element == 'loadingIcon') {
self.options.externalControls css = {
? (!!self.options.controls.length + !!self.options.title) * self.barHeight left: self.iconLeft + 'px',
: 0 top: self.iconTop + 'px',
), width: self.iconSize + 'px',
timelineWidth; height: self.iconSize + 'px'
that.css({ };
position: 'absolute', } else if (element == 'logo') {
width: self.options.width + 'px', var logoHeight = Math.round(self.height / 10);
height: playerHeight + 'px' self.logoMargin = Math.round(self.height / 20);
}); css = {
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({
left: self.logoMargin + 'px', left: self.logoMargin + 'px',
top: self.logoMargin + 'px', top: self.logoMargin + 'px',
height: logoHeight + 'px', height: logoHeight + 'px',
}); };
} } else if (element == 'player') {
self.$loadingIcon.css({ if (!self.offset) {
left: iconLeft + 'px', self.offset = that.offset();
top: iconTop + 'px', }
width: iconSize + 'px', css = {
height: iconSize + 'px' left: self.options.fullscreen ? 0 : self.offset.left + 'px',
}); top: self.options.fullscreen ? 0 : self.offset.top + 'px',
if (self.$playIcon) { width: self.width + 'px',
playIconPadding = Math.round(iconSize * 1/8); height: (self.options.fullscreen
playIconSize = iconSize - 2 * playIconPadding - 4; ? window.innerHeight
self.$playIcon.css({ : self.height + (
left: iconLeft + 'px', self.options.externalControls
top: iconTop + 'px', ? (!!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', width: playIconSize + 'px',
height: playIconSize + 'px', height: playIconSize + 'px',
padding: playIconPadding + '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({ return css;
bottom: parseInt(self.options.height / 16) + 'px', }
width: self.options.width + 'px',
fontSize: parseInt(self.options.height / 20) + 'px', function setSizes(callback) {
WebkitTextStroke: (self.options.height / 1000) + 'px rgb(0, 0, 0)' var ms = callback ? 250 : 0;
}); self.width = self.options.fullscreen ? window.innerWidth : self.options.width;
self.$titlebar && self.$titlebar.css({ self.height = self.options.fullscreen ? window.innerHeight : self.options.height;
width: self.options.width + 'px' self.iconSize = Math.max(Math.round(self.height / 10), 16),
}); self.iconLeft = parseInt((self.width - self.iconSize) / 2),
self.$controls && self.$controls.css({ self.iconTop = parseInt((self.height - self.iconSize) / 2);
width: self.options.width + 'px'
});
if (self.$timeline || self.$space) { 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 + ( return prev + (
curr == 'timeline' || curr == 'space' ? 0 : curr == 'timeline' || curr == 'space' ? 0 :
curr == 'position' ? self.positionWidth : 16 curr == 'position' ? self.positionWidth : 16
); );
}, 0); }, 0);
if (self.$timeline) { if (self.$timeline) {
self.timelineImageWidth = timelineWidth - self.barHeight; self.timelineImageWidth = self.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'});
} }
} }
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() { 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() { function toggleMuted() {
self.options.muted = !self.options.muted; self.options.muted = !self.options.muted;
self.video.muted = self.options.muted; self.video.muted = self.options.muted;
@ -1062,7 +1173,7 @@ Ox.VideoPlayer = function(options, self) {
if (self.options.showIcon) { if (self.options.showIcon) {
self.options.showIcon && self.$playIcon.animate({ self.options.showIcon && self.$playIcon.animate({
opacity: 0 opacity: 0
}, 250/*, togglePlayIcon()*/); }, 250, togglePlayIcon);
} }
} }
} }
@ -1077,7 +1188,7 @@ Ox.VideoPlayer = function(options, self) {
function toggleScale() { function toggleScale() {
self.options.scaleToFill = !self.options.scaleToFill; self.options.scaleToFill = !self.options.scaleToFill;
self.$video.animate(getVideoCSS(), 250); self.$video.animate(getCSS('video'), 250);
} }
function toggleSize() { function toggleSize() {
@ -1088,7 +1199,9 @@ Ox.VideoPlayer = function(options, self) {
} }
self.setOption = function(key, value) { self.setOption = function(key, value) {
if (key == 'height' || key == 'width') { if (key == 'fullscreen') {
toggleFullscreen();
} else if (key == 'height' || key == 'width') {
setSizes(); setSizes();
} else if (key == 'muted') { } else if (key == 'muted') {
toggleMuted(); toggleMuted();