mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge pull request #4760 from dmitrylyzo/slider-bubble-value
Simplify subtitle sync
This commit is contained in:
commit
c1ef338df1
4 changed files with 55 additions and 50 deletions
|
@ -46,15 +46,8 @@ function init(instance) {
|
||||||
if (inputOffset) {
|
if (inputOffset) {
|
||||||
inputOffset = inputOffset[0];
|
inputOffset = inputOffset[0];
|
||||||
inputOffset = parseFloat(inputOffset);
|
inputOffset = parseFloat(inputOffset);
|
||||||
inputOffset = Math.min(30, Math.max(-30, inputOffset));
|
|
||||||
|
|
||||||
// replace current text by considered offset
|
subtitleSyncSlider.updateOffset(inputOffset);
|
||||||
this.textContent = inputOffset + 's';
|
|
||||||
// set new offset
|
|
||||||
playbackManager.setSubtitleOffset(inputOffset, player);
|
|
||||||
// synchronize with slider value
|
|
||||||
subtitleSyncSlider.updateOffset(
|
|
||||||
getSliderValueFromOffset(inputOffset));
|
|
||||||
} else {
|
} else {
|
||||||
this.textContent = (playbackManager.getPlayerSubtitleOffset(player) || 0) + 's';
|
this.textContent = (playbackManager.getPlayerSubtitleOffset(player) || 0) + 's';
|
||||||
}
|
}
|
||||||
|
@ -79,23 +72,26 @@ function init(instance) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function updateSubtitleOffset() {
|
||||||
|
const value = parseFloat(subtitleSyncSlider.value);
|
||||||
|
// set new offset
|
||||||
|
playbackManager.setSubtitleOffset(value, player);
|
||||||
|
// synchronize with textField value
|
||||||
|
subtitleSyncTextField.updateOffset(value);
|
||||||
|
}
|
||||||
|
|
||||||
subtitleSyncSlider.updateOffset = function (sliderValue) {
|
subtitleSyncSlider.updateOffset = function (sliderValue) {
|
||||||
// default value is 0s = 0ms
|
// default value is 0s = 0ms
|
||||||
this.value = sliderValue === undefined ? 0 : sliderValue;
|
this.value = sliderValue === undefined ? 0 : sliderValue;
|
||||||
|
|
||||||
|
updateSubtitleOffset();
|
||||||
};
|
};
|
||||||
|
|
||||||
subtitleSyncSlider.addEventListener('change', function () {
|
subtitleSyncSlider.addEventListener('change', () => updateSubtitleOffset());
|
||||||
// set new offset
|
|
||||||
playbackManager.setSubtitleOffset(getOffsetFromSliderValue(this.value), player);
|
|
||||||
// synchronize with textField value
|
|
||||||
subtitleSyncTextField.updateOffset(
|
|
||||||
getOffsetFromSliderValue(this.value));
|
|
||||||
});
|
|
||||||
|
|
||||||
subtitleSyncSlider.getBubbleHtml = function (value) {
|
subtitleSyncSlider.getBubbleHtml = function (_, value) {
|
||||||
const newOffset = getOffsetFromPercentage(value);
|
|
||||||
return '<h1 class="sliderBubbleText">'
|
return '<h1 class="sliderBubbleText">'
|
||||||
+ (newOffset > 0 ? '+' : '') + parseFloat(newOffset) + 's'
|
+ (value > 0 ? '+' : '') + parseFloat(value) + 's'
|
||||||
+ '</h1>';
|
+ '</h1>';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -107,25 +103,6 @@ function init(instance) {
|
||||||
instance.element = parent;
|
instance.element = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOffsetFromPercentage(value) {
|
|
||||||
// convert percentage to fraction
|
|
||||||
let offset = (value - 50) / 50;
|
|
||||||
// multiply by offset min/max range value (-x to +x) :
|
|
||||||
offset *= 30;
|
|
||||||
return offset.toFixed(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getOffsetFromSliderValue(value) {
|
|
||||||
// convert slider value to offset
|
|
||||||
const offset = value / 10;
|
|
||||||
return offset.toFixed(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSliderValueFromOffset(value) {
|
|
||||||
const sliderValue = value * 10;
|
|
||||||
return Math.min(300, Math.max(-300, sliderValue.toFixed(1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
class SubtitleSync {
|
class SubtitleSync {
|
||||||
constructor(currentPlayer) {
|
constructor(currentPlayer) {
|
||||||
player = currentPlayer;
|
player = currentPlayer;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<button type="button" is="paper-icon-button-light" class="subtitleSync-closeButton"><span class="material-icons close" aria-hidden="true"></span></button>
|
<button type="button" is="paper-icon-button-light" class="subtitleSync-closeButton"><span class="material-icons close" aria-hidden="true"></span></button>
|
||||||
<div class="subtitleSyncTextField" contenteditable="true" spellcheck="false">0s</div>
|
<div class="subtitleSyncTextField" contenteditable="true" spellcheck="false">0s</div>
|
||||||
<div class="sliderContainer subtitleSyncSliderContainer">
|
<div class="sliderContainer subtitleSyncSliderContainer">
|
||||||
<input is="emby-slider" type="range" step="1" min="-300" max="300" value="0" class="subtitleSyncSlider" data-slider-keep-progress="true" />
|
<input is="emby-slider" type="range" step="0.1" min="-30" max="30" value="0" class="subtitleSyncSlider" data-slider-keep-progress="true" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,6 +6,7 @@ import './emby-slider.scss';
|
||||||
import 'webcomponents.js/webcomponents-lite';
|
import 'webcomponents.js/webcomponents-lite';
|
||||||
import '../emby-input/emby-input';
|
import '../emby-input/emby-input';
|
||||||
import globalize from '../../scripts/globalize';
|
import globalize from '../../scripts/globalize';
|
||||||
|
import { decimalCount } from '../../utils/number';
|
||||||
|
|
||||||
const EmbySliderPrototype = Object.create(HTMLInputElement.prototype);
|
const EmbySliderPrototype = Object.create(HTMLInputElement.prototype);
|
||||||
|
|
||||||
|
@ -75,13 +76,23 @@ function mapClientToFraction(range, clientX) {
|
||||||
function mapFractionToValue(range, fraction) {
|
function mapFractionToValue(range, fraction) {
|
||||||
let value = (range.max - range.min) * fraction;
|
let value = (range.max - range.min) * fraction;
|
||||||
|
|
||||||
|
let decimals = null;
|
||||||
|
|
||||||
// Snap to step
|
// Snap to step
|
||||||
if (range.step !== 'any') {
|
if (range.step !== 'any') {
|
||||||
const step = normalizeSliderStep(range);
|
const step = normalizeSliderStep(range);
|
||||||
|
decimals = decimalCount(step);
|
||||||
value = Math.round(value / step) * step;
|
value = Math.round(value / step) * step;
|
||||||
}
|
}
|
||||||
|
|
||||||
value += parseFloat(range.min);
|
const min = parseFloat(range.min);
|
||||||
|
|
||||||
|
value += min;
|
||||||
|
|
||||||
|
if (decimals != null) {
|
||||||
|
decimals = Math.max(decimals, decimalCount(min));
|
||||||
|
value = parseFloat(value.toFixed(decimals));
|
||||||
|
}
|
||||||
|
|
||||||
return Math.min(Math.max(value, range.min), range.max);
|
return Math.min(Math.max(value, range.min), range.max);
|
||||||
}
|
}
|
||||||
|
@ -135,12 +146,12 @@ function updateValues(isValueSet) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateBubble(range, value, bubble) {
|
function updateBubble(range, percent, value, bubble) {
|
||||||
requestAnimationFrame(function () {
|
requestAnimationFrame(function () {
|
||||||
const bubbleTrackRect = range.sliderBubbleTrack.getBoundingClientRect();
|
const bubbleTrackRect = range.sliderBubbleTrack.getBoundingClientRect();
|
||||||
const bubbleRect = bubble.getBoundingClientRect();
|
const bubbleRect = bubble.getBoundingClientRect();
|
||||||
|
|
||||||
let bubblePos = bubbleTrackRect.width * value / 100;
|
let bubblePos = bubbleTrackRect.width * percent / 100;
|
||||||
if (globalize.getIsElementRTL(range)) {
|
if (globalize.getIsElementRTL(range)) {
|
||||||
bubblePos = bubbleTrackRect.width - bubblePos;
|
bubblePos = bubbleTrackRect.width - bubblePos;
|
||||||
}
|
}
|
||||||
|
@ -148,18 +159,20 @@ function updateBubble(range, value, bubble) {
|
||||||
|
|
||||||
bubble.style.left = bubblePos + 'px';
|
bubble.style.left = bubblePos + 'px';
|
||||||
|
|
||||||
|
let html;
|
||||||
|
|
||||||
if (range.getBubbleHtml) {
|
if (range.getBubbleHtml) {
|
||||||
value = range.getBubbleHtml(value);
|
html = range.getBubbleHtml(percent, value);
|
||||||
} else {
|
} else {
|
||||||
if (range.getBubbleText) {
|
if (range.getBubbleText) {
|
||||||
value = range.getBubbleText(value);
|
html = range.getBubbleText(percent, value);
|
||||||
} else {
|
} else {
|
||||||
value = mapFractionToValue(range, value / 100).toLocaleString();
|
html = value.toLocaleString();
|
||||||
}
|
}
|
||||||
value = '<h1 class="sliderBubbleText">' + value + '</h1>';
|
html = '<h1 class="sliderBubbleText">' + html + '</h1>';
|
||||||
}
|
}
|
||||||
|
|
||||||
bubble.innerHTML = value;
|
bubble.innerHTML = html;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,8 +308,8 @@ EmbySliderPrototype.attachedCallback = function () {
|
||||||
updateValues.call(this);
|
updateValues.call(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
const bubbleValue = mapValueToFraction(this, this.value) * 100;
|
const percent = mapValueToFraction(this, this.value) * 100;
|
||||||
updateBubble(this, bubbleValue, sliderBubble);
|
updateBubble(this, percent, parseFloat(this.value), sliderBubble);
|
||||||
|
|
||||||
if (hasHideBubbleClass) {
|
if (hasHideBubbleClass) {
|
||||||
sliderBubble.classList.remove('hide');
|
sliderBubble.classList.remove('hide');
|
||||||
|
@ -322,9 +335,11 @@ EmbySliderPrototype.attachedCallback = function () {
|
||||||
/* eslint-disable-next-line compat/compat */
|
/* eslint-disable-next-line compat/compat */
|
||||||
dom.addEventListener(this, (window.PointerEvent ? 'pointermove' : 'mousemove'), function (e) {
|
dom.addEventListener(this, (window.PointerEvent ? 'pointermove' : 'mousemove'), function (e) {
|
||||||
if (!this.dragging) {
|
if (!this.dragging) {
|
||||||
const bubbleValue = mapClientToFraction(this, e.clientX) * 100;
|
const fraction = mapClientToFraction(this, e.clientX);
|
||||||
|
const percent = fraction * 100;
|
||||||
|
const value = mapFractionToValue(this, fraction);
|
||||||
|
|
||||||
updateBubble(this, bubbleValue, sliderBubble);
|
updateBubble(this, percent, value, sliderBubble);
|
||||||
|
|
||||||
if (hasHideBubbleClass) {
|
if (hasHideBubbleClass) {
|
||||||
sliderBubble.classList.remove('hide');
|
sliderBubble.classList.remove('hide');
|
||||||
|
|
|
@ -32,3 +32,16 @@ export function toPercent(value: number | null | undefined, locale: string): str
|
||||||
|
|
||||||
return `${Math.round(value * 100)}%`;
|
return `${Math.round(value * 100)}%`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets decimal count of a Number.
|
||||||
|
* @param {number} value Number.
|
||||||
|
* @returns {number} Decimal count of a Number.
|
||||||
|
*/
|
||||||
|
export function decimalCount(value: number): number {
|
||||||
|
if (Number.isInteger(value)) return 0;
|
||||||
|
|
||||||
|
const arr = value.toString().split('.');
|
||||||
|
|
||||||
|
return arr[1].length;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue