1
0
mirror of https://github.com/videojs/video.js.git synced 2024-11-24 08:42:25 +02:00

feat: show mute toggle button if the tech supports muting volume (#5052)

Currently, VideoJS combines volume control with muted support, and these actions aren't actually the same. Muting/unmuting volume work independently from the volume control. For example, iOS doesn't support controlling volume programmatically but allows muting/unmuting volume.

This change will display the volume control panel and mute toggle button if the tech supports muting volume. The volume slider will continue to be hidden if the platform doesn't allow programmatically control volume. If neither muting nor control volume is supported, volume panel will not be displayed.

Fixes #4478.
This commit is contained in:
Darius Oleskevicius 2018-06-22 04:33:09 +10:00 committed by Gary Katsevman
parent e8e4fe2745
commit 237041698c
7 changed files with 111 additions and 6 deletions

View File

@ -64,6 +64,9 @@
@include transition(width 0.1s);
}
&.vjs-mute-toggle-only {
width: 4em;
}
}
@include transition(width 1s);

View File

@ -4,7 +4,8 @@
import Button from '../button';
import Component from '../component';
import * as Dom from '../utils/dom.js';
import checkVolumeSupport from './volume-control/check-volume-support';
import checkMuteSupport from './volume-control/check-mute-support';
import * as browser from '../utils/browser.js';
/**
* A button component for muting the audio.
@ -26,7 +27,7 @@ class MuteToggle extends Button {
super(player, options);
// hide this control if volume support is missing
checkVolumeSupport(this, player);
checkMuteSupport(this, player);
this.on(player, ['loadstart', 'volumechange'], this.update);
}
@ -97,6 +98,13 @@ class MuteToggle extends Button {
const vol = this.player_.volume();
let level = 3;
// in iOS when a player is loaded with muted attribute
// and volume is changed with a native mute button
// we want to make sure muted state is updated
if (browser.IS_IOS) {
this.player_.muted(this.player_.tech_.el_.muted);
}
if (vol === 0 || this.player_.muted()) {
level = 0;
} else if (vol < 0.33) {

View File

@ -0,0 +1,28 @@
/**
* Check if muting volume is supported and if it isn't hide the mute toggle
* button.
*
* @param {Component} self
* A reference to the mute toggle button
*
* @param {Player} player
* A reference to the player
*
* @private
*/
const checkMuteSupport = function(self, player) {
// hide mute toggle button if it's not supported by the current tech
if (player.tech_ && !player.tech_.featuresMuteControl) {
self.addClass('vjs-hidden');
}
self.on(player, 'loadstart', function() {
if (!player.tech_.featuresMuteControl) {
self.addClass('vjs-hidden');
} else {
self.removeClass('vjs-hidden');
}
});
};
export default checkMuteSupport;

View File

@ -2,7 +2,6 @@
* @file volume-control.js
*/
import Component from '../component.js';
import checkVolumeSupport from './volume-control/check-volume-support';
import {isPlain} from '../utils/obj';
// Required children
@ -42,8 +41,7 @@ class VolumePanel extends Component {
super(player, options);
// hide this control if volume support is missing
checkVolumeSupport(this, player);
this.on(player, ['loadstart'], this.volumePanelState_);
// while the slider is active (the mouse has been pressed down and
// is dragging) we do not want to hide the VolumeBar
@ -72,6 +70,27 @@ class VolumePanel extends Component {
this.removeClass('vjs-slider-active');
}
/**
* Adds vjs-hidden or vjs-mute-toggle-only to the VolumePanel
* depending on MuteToggle and VolumeControl state
*
* @listens Player#loadstart
* @private
*/
volumePanelState_() {
// hide volume panel if neither volume control or mute toggle
// are displayed
if (this.volumeControl.hasClass('vjs-hidden') && this.muteToggle.hasClass('vjs-hidden')) {
this.addClass('vjs-hidden');
}
// if only mute toggle is visible we don't want
// volume panel expanding when hovered or active
if (this.volumeControl.hasClass('vjs-hidden') && !this.muteToggle.hasClass('vjs-hidden')) {
this.addClass('vjs-mute-toggle-only');
}
}
/**
* Create the `Component`'s DOM element
*

View File

@ -953,6 +953,33 @@ Html5.canControlVolume = function() {
}
};
/**
* Check if the volume can be muted in this browser/device.
* Some devices, e.g. iOS, don't allow changing volume
* but permits muting/unmuting.
*
* @return {bolean}
* - True if volume can be muted
* - False otherwise
*/
Html5.canMuteVolume = function() {
try {
const muted = Html5.TEST_VID.muted;
// in some versions of iOS muted property doesn't always
// work, so we want to set both property and attribute
Html5.TEST_VID.muted = !muted;
if (Html5.TEST_VID.muted) {
Dom.setAttribute(Html5.TEST_VID, 'muted', 'muted');
} else {
Dom.removeAttribute(Html5.TEST_VID, 'muted', 'muted');
}
return muted !== Html5.TEST_VID.muted;
} catch (e) {
return false;
}
};
/**
* Check if the playback rate can be changed in this browser/device.
*
@ -1075,6 +1102,14 @@ Html5.Events = [
*/
Html5.prototype.featuresVolumeControl = Html5.canControlVolume();
/**
* Boolean indicating whether the `Tech` supports muting volume.
*
* @type {bolean}
* @default {@link Html5.canMuteVolume}
*/
Html5.prototype.featuresMuteControl = Html5.canMuteVolume();
/**
* Boolean indicating whether the `Tech` supports changing the speed at which the media
* plays. Examples:

View File

@ -1007,6 +1007,14 @@ TRACK_TYPES.ALL.names.forEach(function(name) {
*/
Tech.prototype.featuresVolumeControl = true;
/**
* Boolean indicating whether the `Tech` supports muting volume.
*
* @type {bolean}
* @default
*/
Tech.prototype.featuresMuteControl = true;
/**
* Boolean indicating whether the `Tech` supports fullscreen resize control.
* Resizing plugins using request fullscreen reloads the plugin

View File

@ -18,12 +18,13 @@ QUnit.module('Controls', {
}
});
QUnit.test('should hide volume control if it\'s not supported', function(assert) {
QUnit.test('should hide volume and mute toggle control if it\'s not supported', function(assert) {
assert.expect(2);
const player = TestHelpers.makePlayer();
player.tech_.featuresVolumeControl = false;
player.tech_.featuresMuteControl = false;
const volumeControl = new VolumeControl(player);
const muteToggle = new MuteToggle(player);
@ -38,6 +39,7 @@ QUnit.test('should test and toggle volume control on `loadstart`', function(asse
const player = TestHelpers.makePlayer();
player.tech_.featuresVolumeControl = true;
player.tech_.featuresMuteControl = true;
const volumeControl = new VolumeControl(player);
const muteToggle = new MuteToggle(player);
@ -46,12 +48,14 @@ QUnit.test('should test and toggle volume control on `loadstart`', function(asse
assert.equal(muteToggle.hasClass('vjs-hidden'), false, 'muteToggle is hidden initially');
player.tech_.featuresVolumeControl = false;
player.tech_.featuresMuteControl = false;
player.trigger('loadstart');
assert.equal(volumeControl.hasClass('vjs-hidden'), true, 'volumeControl does not hide itself');
assert.equal(muteToggle.hasClass('vjs-hidden'), true, 'muteToggle does not hide itself');
player.tech_.featuresVolumeControl = true;
player.tech_.featuresMuteControl = true;
player.trigger('loadstart');
assert.equal(volumeControl.hasClass('vjs-hidden'), false, 'volumeControl does not show itself');