@@ -222,10 +222,17 @@ class Player extends Component {
222
222
// Default state of video is paused
223
223
this . addClass ( 'vjs-paused' ) ;
224
224
225
- // Make box use width/height of tag, or rely on default implementation
226
- // Enforce with CSS since width/height attrs don't work on divs
227
- this . width ( this . options_ [ 'width' ] , true ) ; // (true) Skip resize listener on load
228
- this . height ( this . options_ [ 'height' ] , true ) ;
225
+ // Add a style element in the player that we'll use to set the width/height
226
+ // of the player in a way that's still overrideable by CSS, just like the
227
+ // video element
228
+ this . styleEl_ = document . createElement ( 'style' ) ;
229
+ el . appendChild ( this . styleEl_ ) ;
230
+
231
+ // Pass in the width/height/aspectRatio options which will update the style el
232
+ this . width ( this . options_ [ 'width' ] ) ;
233
+ this . height ( this . options_ [ 'height' ] ) ;
234
+ this . fluid ( this . options_ [ 'fluid' ] ) ;
235
+ this . aspectRatio ( this . options_ [ 'aspectRatio' ] ) ;
229
236
230
237
// Lib.insertFirst seems to cause the networkState to flicker from 3 to 2, so
231
238
// keep track of the original for later so we can know if the source originally failed
@@ -242,6 +249,129 @@ class Player extends Component {
242
249
return el ;
243
250
}
244
251
252
+ width ( value ) {
253
+ return this . dimension ( 'width' , value ) ;
254
+ }
255
+
256
+ height ( value ) {
257
+ return this . dimension ( 'height' , value ) ;
258
+ }
259
+
260
+ dimension ( dimension , value ) {
261
+ let privDimension = dimension + '_' ;
262
+
263
+ if ( value === undefined ) {
264
+ return this [ privDimension ] || 0 ;
265
+ }
266
+
267
+ if ( value === '' ) {
268
+ // If an empty string is given, reset the dimension to be automatic
269
+ this [ privDimension ] = undefined ;
270
+ } else {
271
+ let parsedVal = parseFloat ( value ) ;
272
+
273
+ if ( isNaN ( parsedVal ) ) {
274
+ Lib . log . error ( `Improper value "${ value } " supplied for for ${ dimension } ` ) ;
275
+ return this ;
276
+ }
277
+
278
+ this [ privDimension ] = parsedVal ;
279
+ }
280
+
281
+ this . updateStyleEl_ ( ) ;
282
+ return this ;
283
+ }
284
+
285
+ fluid ( bool ) {
286
+ if ( bool === undefined ) {
287
+ return ! ! this . fluid_ ;
288
+ }
289
+
290
+ this . fluid_ = ! ! bool ;
291
+
292
+ if ( bool ) {
293
+ this . addClass ( 'vjs-fluid' ) ;
294
+ } else {
295
+ this . removeClass ( 'vjs-fluid' ) ;
296
+ }
297
+ }
298
+
299
+ aspectRatio ( ratio ) {
300
+ if ( ratio === undefined ) {
301
+ return this . aspectRatio_ ;
302
+ }
303
+
304
+ // Check for width:height format
305
+ if ( ! / ^ \d + \: \d + $ / . test ( ratio ) ) {
306
+ throw new Error ( 'Improper value suplied for aspect ratio. The format should be width:height, for example 16:9.' ) ;
307
+ }
308
+ this . aspectRatio_ = ratio ;
309
+
310
+ // We're assuming if you set an aspect ratio you want fluid mode,
311
+ // because in fixed mode you could calculate width and height yourself.
312
+ this . fluid ( true ) ;
313
+
314
+ this . updateStyleEl_ ( ) ;
315
+ }
316
+
317
+ updateStyleEl_ ( ) {
318
+ let width ;
319
+ let height ;
320
+ let aspectRatio ;
321
+
322
+ // The aspect ratio is either used directly or to calculate width and height.
323
+ if ( this . aspectRatio_ !== undefined && this . aspectRatio_ !== 'auto' ) {
324
+ // Use any aspectRatio that's been specifically set
325
+ aspectRatio = this . aspectRatio_ ;
326
+ } else if ( this . videoWidth ( ) ) {
327
+ // Otherwise try to get the aspect ratio from the video metadata
328
+ aspectRatio = this . videoWidth ( ) + ':' + this . videoHeight ( ) ;
329
+ } else {
330
+ // Or use a default. The video element's is 2:1, but 16:9 is more common.
331
+ aspectRatio = '16:9' ;
332
+ }
333
+
334
+ // Get the ratio as a decimal we can use to calculate dimensions
335
+ let ratioParts = aspectRatio . split ( ':' ) ;
336
+ let ratioMultiplier = ratioParts [ 1 ] / ratioParts [ 0 ] ;
337
+
338
+ if ( this . width_ !== undefined ) {
339
+ // Use any width that's been specifically set
340
+ width = this . width_ ;
341
+ } else if ( this . height_ !== undefined ) {
342
+ // Or calulate the width from the aspect ratio if a height has been set
343
+ width = this . height_ / ratioMultiplier ;
344
+ } else {
345
+ // Or use the video's metadata, or use the video el's default of 300
346
+ width = this . videoWidth ( ) || 300 ;
347
+ }
348
+
349
+ if ( this . height_ !== undefined ) {
350
+ // Use any height that's been specifically set
351
+ height = this . height_ ;
352
+ } else {
353
+ // Otherwise calculate the height from the ratio and the width
354
+ height = width * ratioMultiplier ;
355
+ }
356
+
357
+ let idClass = this . id ( ) + '-dimensions' ;
358
+
359
+ // Ensure the right class is still on the player for the style element
360
+ this . addClass ( idClass ) ;
361
+
362
+ // Create the width/height CSS
363
+ var css = `.${ idClass } { width: ${ width } px; height: ${ height } px; }` ;
364
+ // Add the aspect ratio CSS for when using a fluid layout
365
+ css += `.${ idClass } .vjs-fluid { padding-top: ${ ratioMultiplier * 100 } %; }` ;
366
+
367
+ // Update the style el
368
+ if ( this . styleEl_ . styleSheet ) {
369
+ this . styleEl_ . styleSheet . cssText = css ;
370
+ } else {
371
+ this . styleEl_ . innerHTML = css ;
372
+ }
373
+ }
374
+
245
375
/**
246
376
* Load the Media Playback Technology (tech)
247
377
* Load/Create an instance of playback technology including element and API methods
@@ -323,6 +453,7 @@ class Player extends Component {
323
453
this . on ( this . tech , 'ratechange' , this . handleTechRateChange ) ;
324
454
this . on ( this . tech , 'volumechange' , this . handleTechVolumeChange ) ;
325
455
this . on ( this . tech , 'texttrackchange' , this . onTextTrackChange ) ;
456
+ this . on ( this . tech , 'loadedmetadata' , this . updateStyleEl_ ) ;
326
457
327
458
if ( this . controls ( ) && ! this . usingNativeControls ( ) ) {
328
459
this . addTechControlsListeners ( ) ;
@@ -1922,15 +2053,21 @@ class Player extends Component {
1922
2053
this . tech && this . tech [ 'removeRemoteTextTrack' ] ( track ) ;
1923
2054
}
1924
2055
2056
+ videoWidth ( ) {
2057
+ return this . tech && this . tech . videoWidth && this . tech . videoWidth ( ) || 0 ;
2058
+ }
2059
+
2060
+ videoHeight ( ) {
2061
+ return this . tech && this . tech . videoHeight && this . tech . videoHeight ( ) || 0 ;
2062
+ }
2063
+
1925
2064
// Methods to add support for
1926
2065
// initialTime: function(){ return this.techCall('initialTime'); },
1927
2066
// startOffsetTime: function(){ return this.techCall('startOffsetTime'); },
1928
2067
// played: function(){ return this.techCall('played'); },
1929
2068
// seekable: function(){ return this.techCall('seekable'); },
1930
2069
// videoTracks: function(){ return this.techCall('videoTracks'); },
1931
2070
// audioTracks: function(){ return this.techCall('audioTracks'); },
1932
- // videoWidth: function(){ return this.techCall('videoWidth'); },
1933
- // videoHeight: function(){ return this.techCall('videoHeight'); },
1934
2071
// defaultPlaybackRate: function(){ return this.techCall('defaultPlaybackRate'); },
1935
2072
// mediaGroup: function(){ return this.techCall('mediaGroup'); },
1936
2073
// controller: function(){ return this.techCall('controller'); },
0 commit comments