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 {
bottom: 135px;
right: 13px;
right: 28px;
}
#toast-container * {

View File

@ -12,34 +12,6 @@
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 {
position: relative;
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.
*/
@ -96,60 +59,10 @@
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 {
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 {
transform: scale(-1, 1);
-moz-transform: scale(-1, 1);
@ -618,39 +531,4 @@
.moveToCorner + .moveToCorner {
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 '404';
@import 'policy';
@import 'filmstrip';
/* Modules END */

View File

@ -166,21 +166,23 @@
<img id="recordingSpinner" class="recordingSpinner" src="images/spin.svg"></img>
</span>
</div>
<div id="remoteVideos">
<span id="localVideoContainer" class="videocontainer videocontainer_small">
<div class="videocontainer__background"></div>
<span id="localVideoWrapper">
<!--<video id="localVideo" autoplay muted></video> - is now per stream generated -->
<div class="filmstrip">
<div class="filmstrip__videos" id="remoteVideos">
<span id="localVideoContainer" class="videocontainer videocontainer_small">
<div class="videocontainer__background"></div>
<span id="localVideoWrapper">
<!--<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>
<audio id="localAudio" autoplay muted></audio>
<div class="videocontainer__toolbar"></div>
<div class="videocontainer__toptoolbar"></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>
<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 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}).
*/
init (eventEmitter) {
this.iconMenuDownClassName = 'icon-menu-down';
this.iconMenuUpClassName = 'icon-menu-up';
this.filmStrip = $('#remoteVideos');
this.eventEmitter = eventEmitter;
this.filmStripIsVisible = true;
this.renderFilmstripToolbar();
this.activateHideButton();
this._initFilmStripToolbar();
this.registerListeners();
},
/**
* 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
*/
activateHideButton () {
$('#videospace').on('click', '#hideVideoToolbar', () => {
var icon = document.querySelector('#hideVideoToolbar i');
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');
});
registerListeners() {
let toggleFilmstripMethod = this.toggleFilmStrip.bind(this);
let selector = '#hideVideoToolbar';
$('#videospace').on('click', selector, toggleFilmstripMethod);
},
/**
* Shows toolbar on the right of the filmstrip
* Changes classes of icon for showing down state
*/
renderFilmstripToolbar () {
// create toolbar
var container = document.createElement('div');
container.className = 'filmstripToolbar';
showMenuDownIcon() {
let icon = this.toggleFilmStripIcon;
icon.classList.add(this.iconMenuDownClassName);
icon.classList.remove(this.iconMenuUpClassName);
},
container.innerHTML = `
<button id="hideVideoToolbar">
<i class="icon-menu-${this.filmStripIsVisible ? 'down' : 'up'}">
</i>
</button>
`;
// show toolbar
document.querySelector('#videospace')
.insertBefore(container, document.querySelector('#remoteVideos'));
/**
* Changes classes of icon for showing up state
*/
showMenuUpIcon() {
let icon = this.toggleFilmStripIcon;
icon.classList.add(this.iconMenuUpClassName);
icon.classList.remove(this.iconMenuDownClassName);
},
/**
@ -62,14 +86,22 @@ const FilmStrip = {
* (i.e. toggled); otherwise, the visibility will be set to the specified
* value.
*/
toggleFilmStrip (visible) {
if (typeof visible === 'boolean'
&& this.isFilmStripVisible() == visible) {
toggleFilmStrip(visible) {
let isVisibleDefined = typeof visible === 'boolean';
if (!isVisibleDefined) {
visible = this.isFilmStripVisible();
} else if (this.isFilmStripVisible() === visible) {
return;
}
this.filmStrip.toggleClass("hidden");
if (!visible) {
this.showMenuDownIcon();
} else {
this.showMenuUpIcon();
}
// Emit/fire UIEvents.TOGGLED_FILM_STRIP.
var eventEmitter = this.eventEmitter;
if (eventEmitter) {
@ -79,18 +111,26 @@ const FilmStrip = {
}
},
isFilmStripVisible () {
/**
* Shows if filmstrip is visible
* @returns {boolean}
*/
isFilmStripVisible() {
return !this.filmStrip.hasClass('hidden');
},
setupFilmStripOnly () {
setupFilmStripOnly() {
this.filmStrip.css({
padding: "0px 0px 18px 0px",
right: 0
});
},
getFilmStripHeight () {
/**
* Returns the height of filmstrip
* @returns {number} height
*/
getFilmStripHeight() {
if (this.isFilmStripVisible()) {
return this.filmStrip.outerHeight();
} else {
@ -98,12 +138,20 @@ const FilmStrip = {
}
},
getFilmStripWidth () {
/**
* Returns the width of filmstip
* @returns {number} width
*/
getFilmStripWidth() {
return this.filmStrip.innerWidth()
- parseInt(this.filmStrip.css('paddingLeft'), 10)
- parseInt(this.filmStrip.css('paddingRight'), 10);
},
/**
* Calculates the size for thumbnails: local and remote one
* @returns {*|{localVideo, remoteVideo}}
*/
calculateThumbnailSize() {
let availableSizes = this.calculateAvailableSize();
let width = availableSizes.availableWidth;
@ -115,7 +163,7 @@ const FilmStrip = {
/**
* Normalizes local and remote thumbnail ratios
*/
normalizeThumbnailRatio () {
normalizeThumbnailRatio() {
let remoteHeightRatio = interfaceConfig.REMOTE_THUMBNAIL_RATIO_HEIGHT;
let remoteWidthRatio = interfaceConfig.REMOTE_THUMBNAIL_RATIO_WIDTH;
@ -146,6 +194,11 @@ const FilmStrip = {
return { localRatio, remoteRatio };
},
/**
* Calculates available size for one thumbnail according to
* the current window size
* @returns {{availableWidth: number, availableHeight: number}}
*/
calculateAvailableSize() {
let availableHeight = interfaceConfig.FILM_STRIP_MAX_HEIGHT;
let thumbs = this.getThumbs(true);
@ -221,6 +274,13 @@ const FilmStrip = {
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) {
let { localRatio, remoteRatio } = this.normalizeThumbnailRatio();
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) {
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';
if (only_visible) {
selector += ':visible';