Merge pull request #1110 from jitsi/filmstrip-button-editions

Filmstrip button editions
This commit is contained in:
yanas 2016-11-03 15:46:00 -05:00 committed by GitHub
commit 101c413a3c
6 changed files with 250 additions and 176 deletions

120
css/_filmstrip.scss Normal file
View File

@ -0,0 +1,120 @@
%align-right {
@include flex();
flex-direction: row-reverse;
flex-wrap: nowrap;
justify-content: flex-start;
}
.filmstrip {
position: absolute;
bottom: 0;
right: 0;
padding: 10px 5px;
@extend %align-right;
&__toolbar {
@include flex();
flex-direction: column-reverse;
flex-wrap: nowrap;
position: relative;
z-index: 1; // Set z-index to make element visible
width: 17px;
button {
font-size: 14px;
line-height: 1.2;
text-align: center;
background: transparent;
opacity: 0.7;
height: auto;
width: 100%;
padding: 0;
margin: 0;
border: none;
outline: none;
-webkit-appearance: none;
&:hover {
opacity: 1;
}
i {
cursor: pointer;
}
}
}
&__videos {
@extend %align-right;
position:relative;
height:196px;
padding: 0;
bottom: 0;
width:auto;
border: 2px solid transparent;
z-index: 5;
transition: bottom 2s;
overflow: visible !important;
font-size: 0pt; /*!!!Removes the gap between the local video container and the remote videos.*/
&.hidden {
bottom: -196px;
}
.videocontainer {
display: none;
position: relative;
background-size: contain;
border: 2px solid transparent;
border-radius:1px;
margin: 0 $thumbnailVideoMargin;
&.videoContainerFocused, &:hover {
cursor: hand;
}
/**
* Focused video thumbnail.
*/
&.videoContainerFocused {
transition-duration: 0.5s;
-webkit-transition-duration: 0.5s;
-webkit-animation-name: greyPulse;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: 1;
border: 2px solid $videoThumbnailSelected !important;
box-shadow: inset 0 0 3px $videoThumbnailSelected,
0 0 3px $videoThumbnailSelected !important;
}
.remotevideomenu {
display: none;
}
/**
* Hovered video thumbnail.
*/
&:hover {
cursor: hand;
border: 2px solid $videoThumbnailHovered;
box-shadow: inset 0 0 3px $videoThumbnailHovered,
0 0 3px $videoThumbnailHovered;
.remotevideomenu {
display: inline-block;
}
}
/* With TemasysWebRTC plugin <object/> element is used
instead of <video/> */
& > video,
& > object {
cursor: hand;
border-radius:1px;
object-fit: cover;
overflow: hidden;
}
}
}
}

View File

@ -93,7 +93,7 @@
#toast-container.notification-bottom-right { #toast-container.notification-bottom-right {
bottom: 135px; bottom: 135px;
right: 13px; right: 28px;
} }
#toast-container * { #toast-container * {

View File

@ -12,34 +12,6 @@
overflow: hidden; overflow: hidden;
} }
#remoteVideos {
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
flex-direction: row-reverse;
flex-wrap: nowrap;
justify-content: flex-start;
position:absolute;
text-align:right;
height:196px;
padding: 10px 10px 17px 5px;
bottom: 0;
right: 0;
width:auto;
border: 2px solid transparent;
z-index: 5;
transition: bottom 2s;
overflow: visible !important;
font-size: 0pt; /*!!!Removes the gap between the local video container and the remote videos.*/
}
#remotevideos.hidden {
bottom: -196px;
}
.videocontainer { .videocontainer {
position: relative; position: relative;
text-align: center; text-align: center;
@ -52,15 +24,6 @@
} }
} }
#remoteVideos .videocontainer {
display: none;
position: relative;
background-size: contain;
border: 2px solid transparent;
border-radius:1px;
margin: 0 $thumbnailVideoMargin;
}
/** /**
* The toolbar of the video thumbnail. * The toolbar of the video thumbnail.
*/ */
@ -96,60 +59,10 @@
z-index: 2; z-index: 2;
} }
#remoteVideos .videocontainer.videoContainerFocused,
#remoteVideos .videocontainer:hover {
cursor: hand;
}
/**
* Focused video thumbnail.
*/
#remoteVideos .videocontainer.videoContainerFocused {
transition-duration: 0.5s;
-webkit-transition-duration: 0.5s;
-webkit-animation-name: greyPulse;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: 1;
border: 2px solid $videoThumbnailSelected !important;
box-shadow: inset 0 0 3px $videoThumbnailSelected,
0 0 3px $videoThumbnailSelected !important;
}
/**
* Hovered video thumbnail.
*/
#remoteVideos .videocontainer {
.remotevideomenu {
display: none;
}
/**
* Show/hide items for hover event here
*/
&:hover {
cursor: hand;
border: 2px solid $videoThumbnailHovered;
box-shadow: inset 0 0 3px $videoThumbnailHovered,
0 0 3px $videoThumbnailHovered;
.remotevideomenu {
display: inline-block;
}
}
}
#localVideoWrapper { #localVideoWrapper {
display:inline-block; display:inline-block;
} }
/* With TemasysWebRTC plugin <object/> element is used
instead of <video/> */
#remoteVideos .videocontainer>video,
#remoteVideos .videocontainer>object {
cursor: hand;
border-radius:1px;
object-fit: cover;
overflow: hidden;
}
.flipVideoX { .flipVideoX {
transform: scale(-1, 1); transform: scale(-1, 1);
-moz-transform: scale(-1, 1); -moz-transform: scale(-1, 1);
@ -619,38 +532,3 @@
.moveToCorner + .moveToCorner { .moveToCorner + .moveToCorner {
right: 80px; right: 80px;
} }
.filmstripToolbar {
width: 20px;
position: absolute;
right: 4px;
bottom: 20px;
z-index: 6;
button {
font-size: 14px;
line-height: 1.2;
text-align: center;
background: transparent;
opacity: 0.7;
height: auto;
width: 100%;
padding: 0;
margin: 0 1px;
border: none;
-webkit-appearance: none;
&:hover {
opacity: 1;
}
i {
cursor: pointer;
}
}
}
.filmstripToolbar + #remoteVideos {
padding-right: 24px;
}

View File

@ -63,5 +63,6 @@
@import 'aui-components/dropdown'; @import 'aui-components/dropdown';
@import '404'; @import '404';
@import 'policy'; @import 'policy';
@import 'filmstrip';
/* Modules END */ /* Modules END */

View File

@ -166,21 +166,23 @@
<img id="recordingSpinner" class="recordingSpinner" src="images/spin.svg"></img> <img id="recordingSpinner" class="recordingSpinner" src="images/spin.svg"></img>
</span> </span>
</div> </div>
<div class="filmstrip">
<div id="remoteVideos"> <div class="filmstrip__videos" id="remoteVideos">
<span id="localVideoContainer" class="videocontainer videocontainer_small"> <span id="localVideoContainer" class="videocontainer videocontainer_small">
<div class="videocontainer__background"></div> <div class="videocontainer__background"></div>
<span id="localVideoWrapper"> <span id="localVideoWrapper">
<!--<video id="localVideo" autoplay muted></video> - is now per stream generated --> <!--<video id="localVideo" autoplay muted></video> - is now per stream generated -->
</span>
<audio id="localAudio" autoplay muted></audio>
<div class="videocontainer__toolbar"></div>
<div class="videocontainer__toptoolbar"></div>
<div class="videocontainer__hoverOverlay"></div>
</span> </span>
<audio id="localAudio" autoplay muted></audio> <audio id="userJoined" src="sounds/joined.wav" preload="auto"></audio>
<div class="videocontainer__toolbar"></div> <audio id="userLeft" src="sounds/left.wav" preload="auto"></audio>
<div class="videocontainer__toptoolbar"></div> </div>
<div class="videocontainer__hoverOverlay"></div>
</span>
<audio id="userJoined" src="sounds/joined.wav" preload="auto"></audio>
<audio id="userLeft" src="sounds/left.wav" preload="auto"></audio>
</div> </div>
</div> </div>
</div> </div>
<div id="keyboard-shortcuts" class="keyboard-shortcuts" style="display:none;"> <div id="keyboard-shortcuts" class="keyboard-shortcuts" style="display:none;">

View File

@ -10,48 +10,72 @@ const FilmStrip = {
* emit/fire {UIEvents} (such as {UIEvents.TOGGLED_FILM_STRIP}). * emit/fire {UIEvents} (such as {UIEvents.TOGGLED_FILM_STRIP}).
*/ */
init (eventEmitter) { init (eventEmitter) {
this.iconMenuDownClassName = 'icon-menu-down';
this.iconMenuUpClassName = 'icon-menu-up';
this.filmStrip = $('#remoteVideos'); this.filmStrip = $('#remoteVideos');
this.eventEmitter = eventEmitter; this.eventEmitter = eventEmitter;
this.filmStripIsVisible = true; this._initFilmStripToolbar();
this.renderFilmstripToolbar(); this.registerListeners();
this.activateHideButton(); },
/**
* Initializes the filmstrip toolbar
*/
_initFilmStripToolbar() {
let toolbar = this._generateFilmStripToolbar();
let container = document.querySelector('.filmstrip');
UIUtil.prependChild(container, toolbar);
let iconSelector = '#hideVideoToolbar i';
this.toggleFilmStripIcon = document.querySelector(iconSelector);
},
/**
* Generates HTML layout for filmstrip toolbar
* @returns {HTMLElement}
* @private
*/
_generateFilmStripToolbar() {
let container = document.createElement('div');
let isVisible = this.isFilmStripVisible();
container.className = 'filmstrip__toolbar';
container.innerHTML = `
<button id="hideVideoToolbar">
<i class="icon-menu-${isVisible ? 'down' : 'up'}">
</i>
</button>
`;
return container;
}, },
/** /**
* Attach 'click' listener to "hide filmstrip" button * Attach 'click' listener to "hide filmstrip" button
*/ */
activateHideButton () { registerListeners() {
$('#videospace').on('click', '#hideVideoToolbar', () => { let toggleFilmstripMethod = this.toggleFilmStrip.bind(this);
var icon = document.querySelector('#hideVideoToolbar i'); let selector = '#hideVideoToolbar';
$('#videospace').on('click', selector, toggleFilmstripMethod);
this.filmStripIsVisible = !this.filmStripIsVisible;
this.toggleFilmStrip(this.filmStripIsVisible);
icon.classList.remove(
this.filmStripIsVisible ? 'icon-menu-up' : 'icon-menu-down');
icon.classList.add(
this.filmStripIsVisible ? 'icon-menu-down' : 'icon-menu-up');
});
}, },
/** /**
* Shows toolbar on the right of the filmstrip * Changes classes of icon for showing down state
*/ */
renderFilmstripToolbar () { showMenuDownIcon() {
// create toolbar let icon = this.toggleFilmStripIcon;
var container = document.createElement('div'); icon.classList.add(this.iconMenuDownClassName);
container.className = 'filmstripToolbar'; icon.classList.remove(this.iconMenuUpClassName);
},
container.innerHTML = ` /**
<button id="hideVideoToolbar"> * Changes classes of icon for showing up state
<i class="icon-menu-${this.filmStripIsVisible ? 'down' : 'up'}"> */
</i> showMenuUpIcon() {
</button> let icon = this.toggleFilmStripIcon;
`; icon.classList.add(this.iconMenuUpClassName);
icon.classList.remove(this.iconMenuDownClassName);
// show toolbar
document.querySelector('#videospace')
.insertBefore(container, document.querySelector('#remoteVideos'));
}, },
/** /**
@ -62,14 +86,22 @@ const FilmStrip = {
* (i.e. toggled); otherwise, the visibility will be set to the specified * (i.e. toggled); otherwise, the visibility will be set to the specified
* value. * value.
*/ */
toggleFilmStrip (visible) { toggleFilmStrip(visible) {
if (typeof visible === 'boolean' let isVisibleDefined = typeof visible === 'boolean';
&& this.isFilmStripVisible() == visible) { if (!isVisibleDefined) {
visible = this.isFilmStripVisible();
} else if (this.isFilmStripVisible() === visible) {
return; return;
} }
this.filmStrip.toggleClass("hidden"); this.filmStrip.toggleClass("hidden");
if (!visible) {
this.showMenuDownIcon();
} else {
this.showMenuUpIcon();
}
// Emit/fire UIEvents.TOGGLED_FILM_STRIP. // Emit/fire UIEvents.TOGGLED_FILM_STRIP.
var eventEmitter = this.eventEmitter; var eventEmitter = this.eventEmitter;
if (eventEmitter) { if (eventEmitter) {
@ -79,18 +111,26 @@ const FilmStrip = {
} }
}, },
isFilmStripVisible () { /**
* Shows if filmstrip is visible
* @returns {boolean}
*/
isFilmStripVisible() {
return !this.filmStrip.hasClass('hidden'); return !this.filmStrip.hasClass('hidden');
}, },
setupFilmStripOnly () { setupFilmStripOnly() {
this.filmStrip.css({ this.filmStrip.css({
padding: "0px 0px 18px 0px", padding: "0px 0px 18px 0px",
right: 0 right: 0
}); });
}, },
getFilmStripHeight () { /**
* Returns the height of filmstrip
* @returns {number} height
*/
getFilmStripHeight() {
if (this.isFilmStripVisible()) { if (this.isFilmStripVisible()) {
return this.filmStrip.outerHeight(); return this.filmStrip.outerHeight();
} else { } else {
@ -98,12 +138,20 @@ const FilmStrip = {
} }
}, },
getFilmStripWidth () { /**
* Returns the width of filmstip
* @returns {number} width
*/
getFilmStripWidth() {
return this.filmStrip.innerWidth() return this.filmStrip.innerWidth()
- parseInt(this.filmStrip.css('paddingLeft'), 10) - parseInt(this.filmStrip.css('paddingLeft'), 10)
- parseInt(this.filmStrip.css('paddingRight'), 10); - parseInt(this.filmStrip.css('paddingRight'), 10);
}, },
/**
* Calculates the size for thumbnails: local and remote one
* @returns {*|{localVideo, remoteVideo}}
*/
calculateThumbnailSize() { calculateThumbnailSize() {
let availableSizes = this.calculateAvailableSize(); let availableSizes = this.calculateAvailableSize();
let width = availableSizes.availableWidth; let width = availableSizes.availableWidth;
@ -115,7 +163,7 @@ const FilmStrip = {
/** /**
* Normalizes local and remote thumbnail ratios * Normalizes local and remote thumbnail ratios
*/ */
normalizeThumbnailRatio () { normalizeThumbnailRatio() {
let remoteHeightRatio = interfaceConfig.REMOTE_THUMBNAIL_RATIO_HEIGHT; let remoteHeightRatio = interfaceConfig.REMOTE_THUMBNAIL_RATIO_HEIGHT;
let remoteWidthRatio = interfaceConfig.REMOTE_THUMBNAIL_RATIO_WIDTH; let remoteWidthRatio = interfaceConfig.REMOTE_THUMBNAIL_RATIO_WIDTH;
@ -146,6 +194,11 @@ const FilmStrip = {
return { localRatio, remoteRatio }; return { localRatio, remoteRatio };
}, },
/**
* Calculates available size for one thumbnail according to
* the current window size
* @returns {{availableWidth: number, availableHeight: number}}
*/
calculateAvailableSize() { calculateAvailableSize() {
let availableHeight = interfaceConfig.FILM_STRIP_MAX_HEIGHT; let availableHeight = interfaceConfig.FILM_STRIP_MAX_HEIGHT;
let thumbs = this.getThumbs(true); let thumbs = this.getThumbs(true);
@ -221,6 +274,13 @@ const FilmStrip = {
return { availableWidth, availableHeight }; return { availableWidth, availableHeight };
}, },
/**
* Takes the available size for thumbnail and calculates
* final size of thumbnails
* @param availableWidth
* @param availableHeight
* @returns {{localVideo, remoteVideo}}
*/
calculateThumbnailSizeFromAvailable(availableWidth, availableHeight) { calculateThumbnailSizeFromAvailable(availableWidth, availableHeight) {
let { localRatio, remoteRatio } = this.normalizeThumbnailRatio(); let { localRatio, remoteRatio } = this.normalizeThumbnailRatio();
let { remoteThumbs } = this.getThumbs(true); let { remoteThumbs } = this.getThumbs(true);
@ -251,7 +311,15 @@ const FilmStrip = {
}; };
}, },
resizeThumbnails (local, remote, /**
* Resizes thumbnails
* @param local
* @param remote
* @param animate
* @param forceUpdate
* @returns {Promise}
*/
resizeThumbnails(local, remote,
animate = false, forceUpdate = false) { animate = false, forceUpdate = false) {
return new Promise(resolve => { return new Promise(resolve => {
@ -289,7 +357,12 @@ const FilmStrip = {
}); });
}, },
getThumbs (only_visible = false) { /**
* Returns thumbnails of the filmstrip
* @param only_visible
* @returns {object} thumbnails
*/
getThumbs(only_visible = false) {
let selector = 'span'; let selector = 'span';
if (only_visible) { if (only_visible) {
selector += ':visible'; selector += ':visible';