Skip to content

Commit cf9ef30

Browse files
committed
Make speed plugin work consistent like quality
1 parent b2f1915 commit cf9ef30

File tree

2 files changed

+106
-87
lines changed

2 files changed

+106
-87
lines changed

src/speed/speed.css

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
.mejs__speed-selector.mejs__offscreen {
2+
display: none;
3+
}
4+
15
.mejs__speed-button,
26
.mejs-speed-button {
37
position: relative;
@@ -24,15 +28,9 @@
2428
padding: 0;
2529
position: absolute;
2630
top: -100px;
27-
visibility: hidden;
2831
width: 60px;
2932
}
3033

31-
.mejs__speed-selector,
32-
.mejs-speed-selector {
33-
visibility: visible;
34-
}
35-
3634
.mejs__speed-selector-list,
3735
.mejs-speed-selector-list {
3836
display: block;
@@ -45,6 +43,7 @@
4543
.mejs__speed-selector-list-item,
4644
.mejs-speed-selector-list-item {
4745
color: #fff;
46+
border: 0.06rem solid transparent;
4847
display: block;
4948
list-style-type: none !important;
5049
margin: 0 0 6px;
@@ -58,6 +57,11 @@
5857
background-color: rgba(255, 255, 255, 0.4) !important;
5958
}
6059

60+
.mejs__speed-selector-list-item:focus-within,
61+
.mejs-speed-selector-list-item:focus-within {
62+
border-color: #fff;
63+
}
64+
6165
.mejs__speed-selector-input,
6266
.mejs-speed-selector-input {
6367
clear: both;
@@ -82,13 +86,4 @@
8286
.mejs__speed-selected,
8387
.mejs-speed-selected {
8488
color: rgba(33, 248, 248, 1);
85-
}
86-
87-
.mejs__speed-selector,
88-
.mejs-speed-selector {
89-
visibility: hidden;
90-
}
91-
.mejs__speed-button:hover .mejs__speed-selector,
92-
.mejs-speed-button:hover .mejs-speed-selector {
93-
visibility: visible;
94-
}
89+
}

src/speed/speed.js

Lines changed: 95 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ Object.assign(MediaElementPlayer.prototype, {
7171
// And always include the defaultSpeed
7272
{
7373
const length = t.options.speeds.length;
74-
for(let i = 0; i < length; i++) {
74+
for (let i = 0; i < length; i++) {
7575
const speed = t.options.speeds[i];
7676

7777
if (typeof speed === 'string') {
@@ -114,13 +114,15 @@ Object.assign(MediaElementPlayer.prototype, {
114114

115115
// Build up the main button and the <div><ul></ul></div> next to it within the player bar.
116116
{
117-
player.speedButton = document.createElement('div');
118-
player.speedButton.className = `${t.options.classPrefix}button ${t.options.classPrefix}speed-button`;
117+
player.speedContainer = document.createElement('div');
118+
player.speedContainer.className = `${t.options.classPrefix}button ${t.options.classPrefix}speed-button`;
119+
120+
const generateId = Date.now() + '.' + Math.floor(Math.random() * 100);
119121

120-
player.speedButton.innerHTML =
122+
player.speedContainer.innerHTML =
121123
'<button ' +
122124
'type="button" ' +
123-
`aria-controls="${t.id}" `+
125+
`aria-controls="${generateId}" `+
124126
'aria-expanded="false" ' +
125127
`title="${speedTitle}" ` +
126128
`aria-label="${speedTitle}" ` +
@@ -129,17 +131,17 @@ Object.assign(MediaElementPlayer.prototype, {
129131
getSpeedNameFromValue(t.options.defaultSpeed) +
130132
'</button>' +
131133
`<div class="${t.options.classPrefix}speed-selector ${t.options.classPrefix}offscreen">` +
132-
`<ul class="${t.options.classPrefix}speed-selector-list"></ul>` +
133-
'</div>' +
134+
`<ul id="${generateId}" class="${t.options.classPrefix}speed-selector-list" tabindex="-1"></ul>` +
135+
'</div>'
134136

135137
// Add this button into the DOM
136-
t.addControlElement(player.speedButton, 'speed');
138+
t.addControlElement(player.speedContainer, 'speed');
137139

138140

139141
// Add speed li items.
140142
let ulHTML = ''
141143
const length = speeds.length;
142-
for(let i = 0; i < length; i++) {
144+
for (let i = 0; i < length; i++) {
143145
const speed = speeds[i]
144146

145147
const inputId = `${t.id}-speed-${speeds[i].value}`;
@@ -149,7 +151,7 @@ Object.assign(MediaElementPlayer.prototype, {
149151
const liHTML =
150152
`<li class="${t.options.classPrefix}speed-selector-list-item">` +
151153
`<input `+
152-
`class="${t.options.classPrefix}speed-selector-input" `+
154+
`class="${t.options.classPrefix}speed-selector-input ${(speedIsDefaultSpeed ? `${t.options.classPrefix}speed-selected-input` : '')}" ` +
153155
`type="radio" `+
154156
`name="${t.id}_speed"` +
155157
`disabled="disabled" `+
@@ -159,8 +161,7 @@ Object.assign(MediaElementPlayer.prototype, {
159161
'/>' +
160162
'<label ' +
161163
`for="${inputId}" `+
162-
`class="${t.options.classPrefix}speed-selector-label` +
163-
`${(speedIsDefaultSpeed ? ` ${t.options.classPrefix}speed-selected` : '')}"`+
164+
`class="${t.options.classPrefix}speed-selector-label` + `${(speedIsDefaultSpeed ? ` ${t.options.classPrefix}speed-selected` : '')}"`+
164165
'>' +
165166
`${speed.name}`+
166167
'</label>' +
@@ -170,38 +171,55 @@ Object.assign(MediaElementPlayer.prototype, {
170171

171172
}
172173

173-
player.speedButton.querySelector('ul').innerHTML = ulHTML
174+
player.speedContainer.querySelector('ul').innerHTML = ulHTML
174175
}
175176

176-
player.speedSelector = player.speedButton.querySelector(`.${t.options.classPrefix}speed-selector`)
177+
player.speedSelector = player.speedContainer.querySelector(`.${t.options.classPrefix}speed-selector`)
177178

178179
// Enable inputs after they have been appended to controls to avoid tab and up/down arrow focus issues
179180
const
180-
radios = player.speedButton.querySelectorAll('input[type="radio"]'),
181-
labels = player.speedButton.querySelectorAll(`.${t.options.classPrefix}speed-selector-label`),
182-
speedList = player.speedButton.querySelector(`.${t.options.classPrefix}speed-selector-list`)
181+
speedButton = player.speedContainer.querySelector('button'),
182+
radios = player.speedContainer.querySelectorAll('input[type="radio"]'),
183+
labels = player.speedContainer.querySelectorAll(`.${t.options.classPrefix}speed-selector-label`),
184+
speedList = player.speedContainer.querySelector(`.${t.options.classPrefix}speed-selector-list`)
183185
;
184186

185187
// Handle the events.
186188
let menuIsHidden = true;
187189

190+
let lastShowChange = Date.now();
188191
function showMenu() {
192+
const now = Date.now();
193+
const diff = now - lastShowChange;
194+
if(diff < 16) {
195+
return;
196+
}
197+
lastShowChange = now;
198+
189199
// show the ul menu.
190200
mejs.Utils.removeClass(player.speedSelector, `${t.options.classPrefix}offscreen`);
191201
player.speedSelector.style.height = `${player.speedSelector.querySelector('ul').offsetHeight}px`;
192202
player.speedSelector.style.top = `${(-1 * Number(player.speedSelector.offsetHeight))}px`;
193-
player.speedButton.setAttribute('aria-expanded', 'true');
194-
195-
// focus first radio input
203+
speedButton.setAttribute('aria-expanded', 'true');
196204

205+
// focus on selected radio input
206+
speedList.querySelector('.' + t.options.classPrefix + 'speed-selected-input').focus();
197207

198208
menuIsHidden = false;
199209
}
200210

201211
function hideMenu() {
212+
const now = Date.now();
213+
const diff = now - lastShowChange;
214+
if(diff < 16) {
215+
return;
216+
}
217+
lastShowChange = now;
218+
202219
// hide ul menu
203220
mejs.Utils.addClass(player.speedSelector, `${t.options.classPrefix}offscreen`);
204-
player.speedButton.setAttribute('aria-expanded', 'true');
221+
speedButton.setAttribute('aria-expanded', 'true');
222+
speedButton.focus()
205223

206224
menuIsHidden = true;
207225
}
@@ -215,65 +233,34 @@ Object.assign(MediaElementPlayer.prototype, {
215233
}
216234
}
217235

218-
player.speedButton.addEventListener('mouseenter', showMenu);
219-
// player.speedButton.addEventListener('focusin', showMenu);
220236

221-
player.speedButton.addEventListener('mouseleave', hideMenu);
237+
speedButton.addEventListener('mouseenter', showMenu);
238+
239+
player.speedContainer.addEventListener('mouseleave', hideMenu);
222240
speedList.addEventListener('focusout', (event) => {
223-
if (!player.speedButton.contains(event.relatedTarget)) {
241+
if (!player.speedContainer.contains(event.relatedTarget)) {
224242
hideMenu();
225243
}
226244
});
227245

228-
player.speedButton.addEventListener('click', hideShowMenu);
246+
speedButton.addEventListener('click', hideShowMenu);
229247

230248

249+
// Close with Escape key.
250+
// Allow up/down arrow to change the selected radio without changing the volume.
251+
player.speedContainer.addEventListener('keydown', (event) => {
252+
if(event.key === "Escape"){
253+
hideMenu();
254+
}
255+
256+
event.stopPropagation();
257+
});
258+
231259
// handle speed change when the value of the radio changes.
232260
for (let i = 0, total = radios.length; i < total; i++) {
233261
const radio = radios[i];
234262
radio.disabled = false;
235-
radio.addEventListener('change', function () {
236-
237-
238-
// loop through all of the options.
239-
for(let i = 0; i < total; i++) {
240-
const radio = radios[i]
241-
242-
// remove the speed-selected class from the label
243-
const siblings = mejs.Utils.siblings(radio, (el) => mejs.Utils.hasClass(el, `${t.options.classPrefix}speed-selector-label`));
244-
for (let i = 0, total = siblings.length; i < total; i++) {
245-
mejs.Utils.removeClass(siblings[i], `${t.options.classPrefix}speed-selected`);
246-
}
247-
248-
if(radio.checked) {
249-
250-
251-
// add the speed-selected class to the label
252-
const siblings = mejs.Utils.siblings(radio, (el) => mejs.Utils.hasClass(el, `${t.options.classPrefix}speed-selector-label`));
253-
for (let i = 0, total = siblings.length; i < total; i++) {
254-
mejs.Utils.addClass(siblings[i], `${t.options.classPrefix}speed-selected`);
255-
}
256-
257-
// set the speed onto the media
258-
const newSpeed = Number(radio.value)
259-
media.playbackRate = newSpeed
260-
currentPlaybackSpeed = newSpeed
261-
}
262-
263-
}
264-
// debugger
265-
/*const
266-
radioEl = this,
267-
newSpeed = Number(radioEl.value)
268-
;
269-
270-
// set the speed.
271-
currentPlaybackSpeed = newSpeed;
272-
media.playbackRate = newSpeed
273-
*/
274-
// debugger
275-
// t.changeSpeed(this, player, )
276-
});
263+
radio.addEventListener('change', handleChangeSpeed);
277264
}
278265

279266
// simulate clicks on radio elements.
@@ -283,14 +270,51 @@ Object.assign(MediaElementPlayer.prototype, {
283270
radio = mejs.Utils.siblings(this, (el) => el.tagName === 'INPUT')[0],
284271
event = mejs.Utils.createEvent('click', radio)
285272
;
273+
286274
radio.dispatchEvent(event);
287275
});
288276
}
289277

290278

291279

292-
// @TODO: make events work.
280+
media.addEventListener('loadedmetadata', () => {
281+
if (currentPlaybackSpeed) {
282+
media.playbackRate = Number(currentPlaybackSpeed);
283+
}
284+
});
285+
293286

287+
function handleChangeSpeed() {
288+
const total = radios.length;
289+
for(let i = 0; i < total; i++) {
290+
const radio = radios[i]
291+
292+
// remove the speed-selected class from the previous selected speed label
293+
mejs.Utils.removeClass(radio, `${t.options.classPrefix}speed-selected-input`);
294+
const siblings = mejs.Utils.siblings(radio, (el) => mejs.Utils.hasClass(el, `${t.options.classPrefix}speed-selector-label`));
295+
for (let i = 0, total = siblings.length; i < total; i++) {
296+
mejs.Utils.removeClass(siblings[i], `${t.options.classPrefix}speed-selected`);
297+
}
298+
299+
// handle the new speed.
300+
if (radio.checked) {
301+
302+
mejs.Utils.addClass(radio, `${t.options.classPrefix}speed-selected-input`);
303+
304+
// add the speed-selected class to the label
305+
const siblings = mejs.Utils.siblings(radio, (el) => mejs.Utils.hasClass(el, `${t.options.classPrefix}speed-selector-label`));
306+
for (let i = 0, total = siblings.length; i < total; i++) {
307+
mejs.Utils.addClass(siblings[i], `${t.options.classPrefix}speed-selected`);
308+
}
309+
310+
// set the speed onto the media
311+
const newSpeed = Number(radio.value)
312+
media.playbackRate = newSpeed
313+
currentPlaybackSpeed = newSpeed
314+
speedButton.innerHTML = getSpeedNameFromValue(newSpeed)
315+
}
316+
}
317+
}
294318

295319
// gets the speed name from a speed value.
296320
function getSpeedNameFromValue (speedValue) {
@@ -316,8 +340,8 @@ Object.assign(MediaElementPlayer.prototype, {
316340
*/
317341
cleanspeed (player) {
318342
if (player) {
319-
if (player.speedButton) {
320-
player.speedButton.parentNode.removeChild(player.speedButton);
343+
if (player.speedContainer) {
344+
player.speedContainer.parentNode.removeChild(player.speedContainer);
321345
}
322346
if (player.speedSelector) {
323347
player.speedSelector.parentNode.removeChild(player.speedSelector);

0 commit comments

Comments
 (0)