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:
parent
e8e4fe2745
commit
237041698c
@ -64,6 +64,9 @@
|
||||
|
||||
@include transition(width 0.1s);
|
||||
}
|
||||
&.vjs-mute-toggle-only {
|
||||
width: 4em;
|
||||
}
|
||||
}
|
||||
|
||||
@include transition(width 1s);
|
||||
|
@ -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) {
|
||||
|
28
src/js/control-bar/volume-control/check-mute-support.js
Normal file
28
src/js/control-bar/volume-control/check-mute-support.js
Normal 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;
|
@ -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
|
||||
*
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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');
|
||||
|
Loading…
Reference in New Issue
Block a user