浏览代码

feat(tiles): Add responsive behaviour.

* Enforce fixed column number at various width breakpoints.
* Bring back the filmstrip at small sizes but hide it.
* Change default maximum columns to 7.
master
Mihai-Andrei Uscat 4 年前
父节点
当前提交
db84889143

+ 0
- 10
css/_responsive.scss 查看文件

@@ -143,16 +143,6 @@
143 143
 
144 144
     @media only screen and (max-width: $verySmallScreen) {
145 145
         @include very-small-button-size();
146
-
147
-        #videoResolutionLabel {
148
-            display: none;
149
-        }
150
-        .vertical-filmstrip .filmstrip {
151
-            display: none;
152
-        }
153
-        .chrome-extension-banner {
154
-            display: none;
155
-        }
156 146
     }
157 147
 
158 148
     &.shift-right {

+ 9
- 0
css/filmstrip/_tile_view.scss 查看文件

@@ -100,6 +100,15 @@
100 100
         video {
101 101
             object-fit: contain;
102 102
         }
103
+
104
+        /**
105
+         * Max-width corresponding to the ASPECT_RATIO_BREAKPOINT from features/filmstrip/constants.
106
+         */
107
+        @media only screen and (max-width: 500px) {
108
+            video {
109
+                object-fit: cover;
110
+            }
111
+        }
103 112
     }
104 113
 
105 114
     .has-overflow#filmstripRemoteVideosContainer {

+ 33
- 0
react/features/filmstrip/constants.js 查看文件

@@ -10,7 +10,40 @@ export const FILMSTRIP_SIZE = 90;
10 10
  */
11 11
 export const TILE_ASPECT_RATIO = 16 / 9;
12 12
 
13
+/**
14
+ * The aspect ratio of a square tile in tile view.
15
+ */
16
+export const SQUARE_TILE_ASPECT_RATIO = 1;
17
+
13 18
 /**
14 19
  * Width below which the overflow menu(s) will be displayed as drawer(s).
15 20
  */
16 21
 export const DISPLAY_DRAWER_THRESHOLD = 512;
22
+
23
+/**
24
+ * Breakpoint past which a single column view is enforced in tile view.
25
+ */
26
+export const SINGLE_COLUMN_BREAKPOINT = 300;
27
+
28
+/**
29
+ * Breakpoint past which a two column view is enforced in tile view.
30
+ */
31
+export const TWO_COLUMN_BREAKPOINT = 1000;
32
+
33
+/**
34
+ * Breakpoint past which the aspect ratio is switched in tile view.
35
+ * Also, past this breakpoint, if there are two participants in the conference, we enforce
36
+ * single column view.
37
+ * If this is to be modified, please also change the related media query from the tile_view scss file.
38
+ */
39
+export const ASPECT_RATIO_BREAKPOINT = 500;
40
+
41
+/**
42
+ * The default number of columns for tile view.
43
+ */
44
+export const DEFAULT_MAX_COLUMNS = 5;
45
+
46
+/**
47
+ * An extended number of columns for tile view.
48
+ */
49
+export const ABSOLUTE_MAX_COLUMNS = 7;

+ 4
- 3
react/features/filmstrip/functions.web.js 查看文件

@@ -16,7 +16,7 @@ import {
16 16
     isRemoteTrackMuted
17 17
 } from '../base/tracks/functions';
18 18
 
19
-import { TILE_ASPECT_RATIO } from './constants';
19
+import { ASPECT_RATIO_BREAKPOINT, SQUARE_TILE_ASPECT_RATIO, TILE_ASPECT_RATIO } from './constants';
20 20
 
21 21
 declare var interfaceConfig: Object;
22 22
 
@@ -142,12 +142,13 @@ export function calculateThumbnailSizeForTileView({
142 142
     clientWidth,
143 143
     clientHeight
144 144
 }: Object) {
145
+    const aspectRatio = clientWidth < ASPECT_RATIO_BREAKPOINT ? SQUARE_TILE_ASPECT_RATIO : TILE_ASPECT_RATIO;
145 146
     const viewWidth = clientWidth - TILE_VIEW_SIDE_MARGINS;
146 147
     const viewHeight = clientHeight - TILE_VIEW_SIDE_MARGINS;
147 148
     const initialWidth = viewWidth / columns;
148
-    const aspectRatioHeight = initialWidth / TILE_ASPECT_RATIO;
149
+    const aspectRatioHeight = initialWidth / aspectRatio;
149 150
     const height = Math.floor(Math.min(aspectRatioHeight, viewHeight / visibleRows));
150
-    const width = Math.floor(TILE_ASPECT_RATIO * height);
151
+    const width = Math.floor(aspectRatio * height);
151 152
 
152 153
     return {
153 154
         height,

+ 71
- 1
react/features/filmstrip/subscriber.web.js 查看文件

@@ -3,11 +3,17 @@
3 3
 import Filmstrip from '../../../modules/UI/videolayout/Filmstrip';
4 4
 import VideoLayout from '../../../modules/UI/videolayout/VideoLayout';
5 5
 import { StateListenerRegistry, equals } from '../base/redux';
6
+import { setFilmstripVisible } from '../filmstrip/actions';
6 7
 import { setOverflowDrawer } from '../toolbox/actions.web';
7 8
 import { getCurrentLayout, getTileViewGridDimensions, shouldDisplayTileView, LAYOUTS } from '../video-layout';
8 9
 
9 10
 import { setHorizontalViewDimensions, setTileViewDimensions } from './actions.web';
10
-import { DISPLAY_DRAWER_THRESHOLD } from './constants';
11
+import {
12
+    ASPECT_RATIO_BREAKPOINT,
13
+    DISPLAY_DRAWER_THRESHOLD,
14
+    SINGLE_COLUMN_BREAKPOINT,
15
+    TWO_COLUMN_BREAKPOINT
16
+} from './constants';
11 17
 
12 18
 /**
13 19
  * Listens for changes in the number of participants to calculate the dimensions of the tile view grid and the tiles.
@@ -134,3 +140,67 @@ StateListenerRegistry.register(
134 140
     /* listener */ (widthBelowThreshold, store) => {
135 141
         store.dispatch(setOverflowDrawer(widthBelowThreshold));
136 142
     });
143
+
144
+/**
145
+ * Gracefully hide/show the filmstrip when going past threshold.
146
+ */
147
+StateListenerRegistry.register(
148
+    /* selector */ state => state['features/base/responsive-ui'].clientWidth < ASPECT_RATIO_BREAKPOINT,
149
+    /* listener */ (widthBelowThreshold, store) => {
150
+        store.dispatch(setFilmstripVisible(!widthBelowThreshold));
151
+    });
152
+
153
+/**
154
+ * Symbol mapping used for the tile view responsiveness computation.
155
+ */
156
+const responsiveColumnMapping = {
157
+    singleColumn: Symbol('singleColumn'),
158
+    twoColumns: Symbol('twoColumns'),
159
+    twoParticipantsSingleColumn: Symbol('twoParticipantsSingleColumn')
160
+};
161
+
162
+/**
163
+ * Listens for changes in the screen size to recompute
164
+ * the dimensions of the tile view grid and the tiles for responsiveness.
165
+ */
166
+StateListenerRegistry.register(
167
+    /* selector */ state => {
168
+        const { clientWidth } = state['features/base/responsive-ui'];
169
+
170
+        if (clientWidth < TWO_COLUMN_BREAKPOINT && clientWidth >= ASPECT_RATIO_BREAKPOINT) {
171
+            // Forcing the recomputation of tiles when screen switches in or out of
172
+            // the (TWO_COLUMN_BREAKPOINT, ASPECT_RATIO_BREAKPOINT] interval.
173
+            return responsiveColumnMapping.twoColumns;
174
+        } else if (clientWidth < ASPECT_RATIO_BREAKPOINT && clientWidth >= SINGLE_COLUMN_BREAKPOINT) {
175
+            // Forcing the recomputation of tiles when screen switches in or out of
176
+            // the (ASPECT_RATIO_BREAKPOINT, SINGLE_COLUMN_BREAKPOINT] interval.
177
+            return responsiveColumnMapping.twoParticipantsSingleColumn;
178
+        }
179
+
180
+        /**
181
+         * This gets called either when the width of the screen is above {@code TWO_COLUMN_BREAKPOINT}
182
+         * or below {@CODE SINGLE_COLUMN_BREAKPOINT}, however, the internal logic from {@code getMaxColumnCount}
183
+         * only takes the second case into consideration.
184
+         */
185
+        return responsiveColumnMapping.singleColumn;
186
+    },
187
+    /* listener */ (_, store) => {
188
+        const state = store.getState();
189
+
190
+        if (shouldDisplayTileView(state)) {
191
+            const gridDimensions = getTileViewGridDimensions(state);
192
+            const { clientHeight, clientWidth } = state['features/base/responsive-ui'];
193
+            const { isOpen } = state['features/chat'];
194
+
195
+            store.dispatch(
196
+                setTileViewDimensions(
197
+                    gridDimensions,
198
+                    {
199
+                        clientHeight,
200
+                        clientWidth
201
+                    },
202
+                    isOpen
203
+                )
204
+            );
205
+        }
206
+    });

+ 39
- 5
react/features/video-layout/functions.js 查看文件

@@ -2,6 +2,14 @@
2 2
 
3 3
 import { getFeatureFlag, TILE_VIEW_ENABLED } from '../base/flags';
4 4
 import { getPinnedParticipant, getParticipantCount } from '../base/participants';
5
+import { CHAT_SIZE } from '../chat/constants';
6
+import {
7
+    ASPECT_RATIO_BREAKPOINT,
8
+    DEFAULT_MAX_COLUMNS,
9
+    ABSOLUTE_MAX_COLUMNS,
10
+    SINGLE_COLUMN_BREAKPOINT,
11
+    TWO_COLUMN_BREAKPOINT
12
+} from '../filmstrip/constants';
5 13
 import { isYoutubeVideoPlaying } from '../youtube-player/functions';
6 14
 
7 15
 import { LAYOUTS } from './constants';
@@ -27,14 +35,38 @@ export function getCurrentLayout(state: Object) {
27 35
 
28 36
 /**
29 37
  * Returns how many columns should be displayed in tile view. The number
30
- * returned will be between 1 and 5, inclusive.
38
+ * returned will be between 1 and 7, inclusive.
31 39
  *
40
+ * @param {Object} state - The redux store state.
32 41
  * @returns {number}
33 42
  */
34
-export function getMaxColumnCount() {
35
-    const configuredMax = interfaceConfig.TILE_VIEW_MAX_COLUMNS || 5;
43
+export function getMaxColumnCount(state: Object) {
44
+    const configuredMax = interfaceConfig.TILE_VIEW_MAX_COLUMNS || DEFAULT_MAX_COLUMNS;
45
+    const { clientWidth } = state['features/base/responsive-ui'];
46
+    let availableWidth = clientWidth;
47
+    const participantCount = getParticipantCount(state);
48
+    const { isOpen } = state['features/chat'];
49
+
50
+    if (isOpen) {
51
+        availableWidth -= CHAT_SIZE;
52
+    }
53
+
54
+    // If there are just two participants in a conference, enforce single-column view for mobile size.
55
+    if (participantCount === 2 && availableWidth < ASPECT_RATIO_BREAKPOINT) {
56
+        return Math.min(1, Math.max(configuredMax, 1));
57
+    }
36 58
 
37
-    return Math.min(Math.max(configuredMax, 1), 5);
59
+    // Enforce single column view at very small screen widths.
60
+    if (availableWidth < SINGLE_COLUMN_BREAKPOINT) {
61
+        return Math.min(1, Math.max(configuredMax, 1));
62
+    }
63
+
64
+    // Enforce two column view below breakpoint.
65
+    if (availableWidth < TWO_COLUMN_BREAKPOINT) {
66
+        return Math.min(2, Math.max(configuredMax, 1));
67
+    }
68
+
69
+    return Math.min(Math.max(configuredMax, 1), ABSOLUTE_MAX_COLUMNS);
38 70
 }
39 71
 
40 72
 /**
@@ -48,7 +80,9 @@ export function getMaxColumnCount() {
48 80
  * @returns {Object} An object is return with the desired number of columns,
49 81
  * rows, and visible rows (the rest should overflow) for the tile view layout.
50 82
  */
51
-export function getTileViewGridDimensions(state: Object, maxColumns: number = getMaxColumnCount()) {
83
+export function getTileViewGridDimensions(state: Object) {
84
+    const maxColumns = getMaxColumnCount(state);
85
+
52 86
     // When in tile view mode, we must discount ourselves (the local participant) because our
53 87
     // tile is not visible.
54 88
     const { iAmRecorder } = state['features/base/config'];

正在加载...
取消
保存