/
alexxhub
/
playerjs
Обзор
Документация
Войти
/
alexxhub
/
playerjs
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
1
CI/CD
Аналитика
Безопасность
patch
src/js/controls.js
329 строк
8 KB
apavlov3rednet
doca
02 фев 2026, 20:44
02 фев 2026, 20:44
1368ae9
Код
Авторство
О чём код?
// controls.js /** * Bind core methods to the player instance. * @param player * @private * @returns {void} * @example * bindCoreMethods(player); */ export function bindCoreMethods(player) { player.togglePlay = togglePlay.bind(player); player.toggleMute = toggleMute.bind(player); player.toggleFullscreen = toggleFullscreen.bind(player); player.skipBy = skipBy.bind(player); player._updatePlayStateUI = _updatePlayStateUI.bind(player); player._updateVolume = _updateVolume.bind(player); player._onKeyDown = _onKeyDown.bind(player); player._initSpeed = _initSpeed.bind(player); player._destroySpeed = _destroySpeed.bind(player); player._setPlaybackRate = _setPlaybackRate.bind(player); player._updateSpeedMenuActive = _updateSpeedMenuActive.bind(player); player._onSpeedToggleClick = _onSpeedToggleClick.bind(player); player._onSpeedMenuClick = _onSpeedMenuClick.bind(player); player._initControls = _initControls.bind(player); player._destroyControls = _destroyControls.bind(player); } /** * Initialize controls. * @private * @returns {void} */ function _initControls() { this.playBtn?.addEventListener("click", this.togglePlay); this.bigPlayBtn?.addEventListener("click", this.togglePlay); this.video?.addEventListener("click", this.togglePlay); this.video?.addEventListener("play", this._updatePlayStateUI); this.video?.addEventListener("pause", this._updatePlayStateUI); this.video?.addEventListener("ended", this._updatePlayStateUI); this.volumeRange?.addEventListener("input", this._updateVolume); this.muteBtn?.addEventListener("click", this.toggleMute); this.fullscreenBtn?.addEventListener("click", this.toggleFullscreen); this.skipBackBtn?.addEventListener("click", () => this.skipBy(-10)); this.skipForwardBtn?.addEventListener("click", () => this.skipBy(10)); if (this.speedRoot && this.speedToggleBtn && this.speedMenu) { this._initSpeed(); } document.addEventListener("keydown", this._onKeyDown); } /** * Destroy controls. * @private * @returns {void} */ function _destroyControls() { this.playBtn?.removeEventListener("click", this.togglePlay); this.bigPlayBtn?.removeEventListener("click", this.togglePlay); this.video?.removeEventListener("click", this.togglePlay); this.video?.removeEventListener("play", this._updatePlayStateUI); this.video?.removeEventListener("pause", this._updatePlayStateUI); this.video?.removeEventListener("ended", this._updatePlayStateUI); this.volumeRange?.removeEventListener("input", this._updateVolume); this.muteBtn?.removeEventListener("click", this.toggleMute); this.fullscreenBtn?.removeEventListener("click", this.toggleFullscreen); this.skipBackBtn?.removeEventListener("click", () => this.skipBy(-10)); this.skipForwardBtn?.removeEventListener("click", () => this.skipBy(10)); if (this.speedRoot && this.speedToggleBtn && this.speedMenu) { this._destroySpeed(); } document.removeEventListener("keydown", this._onKeyDown); } /** * Toggle play/pause. * @private * @returns {void} */ function togglePlay() { if (this.video.paused || this.video.ended) { this.video.play(); } else { this.video.pause(); } } /** * Toggle mute. * @private * @returns {void} */ function toggleMute() { this.video.muted = !this.video.muted; this.muteBtn?.classList.toggle("is-muted", this.video.muted); if (!this.video.muted && this.video.volume === 0) { this.video.volume = 0.5; if (this.volumeRange) this.volumeRange.value = 0.5; } } /** * Toggle fullscreen. * @private * @returns {void} */ function toggleFullscreen() { const container = this.root; if (!document.fullscreenElement) { container.requestFullscreen?.(); } else { document.exitFullscreen?.(); } } /** * Skip by delta. * @param delta {number} - Skip time in seconds. * @private * @returns {void} */ function skipBy(delta) { if (!this.video || !Number.isFinite(this.video.duration)) return; const target = Math.min( this.video.duration, Math.max(0, this.video.currentTime + delta), ); this.video.currentTime = target; } /** * Update play state UI. * @private * @returns {void} */ function _updatePlayStateUI() { const isPaused = this.video.paused || this.video.ended; this.playBtn?.classList.toggle("is-paused", !isPaused); this.bigPlayBtn?.classList.toggle("is-hidden", !isPaused); } /** * Update volume. * @param e {Event} - Event object. * @private * @returns {void} */ function _updateVolume(e) { const value = Number(e.target.value); this.video.volume = value; this.video.muted = value === 0; this.muteBtn?.classList.toggle("is-muted", this.video.muted); } /** * Handle keydown event. * @param e {Event} - Event object. * @private * @returns {void} */ function _onKeyDown(e) { if (e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA") return; switch (e.key.toLowerCase()) { case " ": case "k": e.preventDefault(); this.togglePlay(); break; case "m": this.toggleMute(); break; case "f": this.toggleFullscreen(); break; case "arrowright": this.skipBy(10); break; case "arrowleft": this.skipBy(-10); break; case "arrowup": this.video.volume = Math.min(1, this.video.volume + 0.05); if (this.volumeRange) this.volumeRange.value = this.video.volume; break; case "arrowdown": this.video.volume = Math.max(0, this.video.volume - 0.05); if (this.volumeRange) this.volumeRange.value = this.video.volume; break; case ">": case ".": this._setPlaybackRate(Math.min(2, (this.video.playbackRate || 1) + 0.25)); break; case "<": case ",": this._setPlaybackRate( Math.max(0.25, (this.video.playbackRate || 1) - 0.25), ); break; default: break; } } /** * Initialize speed. * @private * @returns {void} */ function _initSpeed() { // список поддерживаемых скоростей this.availableSpeeds = this.options.speeds || [0.5, 1, 1.25, 1.5, 2]; this.speedMenu.innerHTML = ""; this.availableSpeeds.forEach((rate) => { const item = document.createElement("div"); item.className = "speed__menu-item"; item.dataset.rate = String(rate); item.textContent = `${rate}x`; this.speedMenu.appendChild(item); }); this.speedToggleBtn.textContent = `${this.video.playbackRate || 1}x`; this.speedToggleBtn.addEventListener("click", this._onSpeedToggleClick); this.speedMenu.addEventListener("click", this._onSpeedMenuClick); document.addEventListener("click", _onDocumentClickSpeed.bind(this)); this._speedDocHandler = _onDocumentClickSpeed.bind(this); this._updateSpeedMenuActive(); } /** * Destroy speed. * @private * @returns {void} */ function _destroySpeed() { this.speedToggleBtn.removeEventListener("click", this._onSpeedToggleClick); this.speedMenu.removeEventListener("click", this._onSpeedMenuClick); if (this._speedDocHandler) { document.removeEventListener("click", this._speedDocHandler); } } /** * Handle speed toggle click. * @param e {Event} - Event object. * @private * @returns {void} */ function _onSpeedToggleClick(e) { e.stopPropagation(); this.speedRoot.classList.toggle("is-open"); } /** * Handle speed menu click. * @param e {Event} - Event object. * @private * @returns {void} */ function _onSpeedMenuClick(e) { const item = e.target.closest(".speed__menu-item"); if (!item) return; const rate = Number(item.dataset.rate); if (!Number.isFinite(rate) || rate <= 0) return; this._setPlaybackRate(rate); this.speedRoot.classList.remove("is-open"); } /** * Handle document click. * @param e {Event} - Event object. * @private * @returns {void} */ function _onDocumentClickSpeed(e) { if (!this.speedRoot.contains(e.target)) { this.speedRoot.classList.remove("is-open"); } } /** * Set playback rate. * @param rate {number} - Playback rate. * @private * @returns {void} */ function _setPlaybackRate(rate) { this.video.playbackRate = rate; if (this.speedToggleBtn) { this.speedToggleBtn.textContent = `${rate}x`; } this._updateSpeedMenuActive(); } /** * Update speed menu active. * @private * @returns {void} */ function _updateSpeedMenuActive() { if (!this.speedMenu) return; const items = this.speedMenu.querySelectorAll(".speed__menu-item"); const current = this.video.playbackRate || 1; items.forEach((el) => { const rate = Number(el.dataset.rate); el.classList.toggle("is-active", rate === current); }); }