分离事件中心

This commit is contained in:
Harvey Zack committed 2018-11-09 12:17:19 +08:00
1 parent b5694fea89
commit 1d40d1c46b
31 files changed
+728 -403

No files matched your search

-25
View File
@@ -1,25 +0,0 @@
import { append, tooltip, setStyle } from '../utils';
import icons from '../icons';
export default class Danmu {
constructor(option) {
this.option = option;
}
apply(art, $control) {
const { events: { proxy }, i18n, danmu } = art;
this.$danmu = append($control, icons.danmu);
tooltip(this.$danmu, i18n.get('Hide danmu'));
proxy($control, 'click', () => {
if (danmu.isOpen) {
danmu.hide();
setStyle(this.$danmu, 'opacity', '1');
tooltip(this.$danmu, i18n.get('Show danmu'));
} else {
danmu.show();
setStyle(this.$danmu, 'opacity', '0.8');
tooltip(this.$danmu, i18n.get('Hide danmu'));
}
});
}
}
+11 -25
View File
@@ -1,18 +1,16 @@
import { append, clamp, secondToTime, setStyle } from '../utils';
import { append, clamp, secondToTime, setStyle, getStyle } from '../utils';
export default class Progress {
constructor(option) {
this.option = option;
this.isDroging = false;
this.getLoaded = this.getLoaded.bind(this);
this.getPlayed = this.getPlayed.bind(this);
this.set = this.set.bind(this);
}
apply(art, $control) {
this.art = art;
this.$control = $control;
const { option: { highlight, theme }, events: { proxy }, refs: { $video }, player } = art;
const { option: { highlight, theme }, events: { proxy }, player } = art;
append(
$control,
@@ -33,18 +31,19 @@ export default class Progress {
this.$indicator = $control.querySelector('.art-progress-indicator');
this.$tip = $control.querySelector('.art-progress-tip');
this.set('loaded', this.getLoaded());
highlight.forEach(item => {
const left = Number(item.time) / $video.duration;
const left = Number(item.time) / player.duration;
append(this.$highlight, `<span data-text="${item.text}" data-time="${item.time}" style="left: ${left * 100}%"></span>`);
});
this.set('loaded', player.loaded);
this.art.on('video:progress', () => {
this.set('loaded', this.getLoaded());
this.set('loaded', this.loaded);
});
this.art.on('video:timeupdate', () => {
this.set('played', this.getPlayed());
this.set('played', this.played);
});
this.art.on('video:ended', () => {
@@ -94,11 +93,10 @@ export default class Progress {
}
showHighlight(event) {
const { $video } = this.art.refs;
const { text, time } = event.target.dataset;
this.$tip.innerHTML = text;
const left =
Number(time) / $video.duration * this.$control.clientWidth +
Number(time) / this.art.player.duration * this.$control.clientWidth +
event.target.clientWidth / 2 -
this.$tip.clientWidth / 2;
setStyle(this.$tip, 'left', `${left}px`);
@@ -117,23 +115,11 @@ export default class Progress {
}
}
getPlayed() {
const { $video } = this.art.refs;
return $video.currentTime / $video.duration;
}
getLoaded() {
const { $video } = this.art.refs;
return $video.buffered.length
? $video.buffered.end($video.buffered.length - 1) / $video.duration
: 0;
}
getPosFromEvent(event) {
const { $video, $progress } = this.art.refs;
const { player, refs: { $progress } } = this.art;
const { left } = $progress.getBoundingClientRect();
const width = clamp(event.x - left, 0, $progress.clientWidth);
const second = width / $progress.clientWidth * $video.duration;
const second = width / $progress.clientWidth * player.duration;
const time = secondToTime(second);
const percentage = clamp(width / $progress.clientWidth, 0, 1);
return { second, time, width, percentage };
@@ -142,7 +128,7 @@ export default class Progress {
set(type, percentage) {
setStyle(this[`$${type}`], 'width', `${percentage * 100}%`);
if (type === 'played') {
setStyle(this.$indicator, 'left', `calc(${percentage * 100}% - 6.5px)`);
setStyle(this.$indicator, 'left', `calc(${percentage * 100}% - ${getStyle(this.$indicator, 'width') / 2}px)`);
}
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ export default class Time {
$control.innerHTML = '00:00 / 00:00';
function setTime() {
$control.innerHTML = `${secondToTime(art.player.currentTime())} / ${secondToTime(art.player.duration())}`;
$control.innerHTML = `${secondToTime(art.player.currentTime)} / ${secondToTime(art.player.duration)}`;
}
art.on('video:canplay', () => {
+50 -67
View File
@@ -1,4 +1,4 @@
import { append, getStorage, clamp, tooltip, setStyle } from '../utils';
import { append, clamp, tooltip, setStyle, sleep, getStyle } from '../utils';
import icons from '../icons';
export default class Volume {
@@ -8,72 +8,15 @@ export default class Volume {
}
apply(art, $control) {
this.art = art;
this.$control = $control;
this.init();
}
init() {
const { events: { proxy }, player, i18n } = this.art;
this.$volume = append(this.$control, icons.volume);
this.$volumeClose = append(this.$control, icons.volumeClose);
this.$volumePanel = append(this.$control, '<div class="art-volume-panel"></div>');
const { events: { proxy, hover }, player, i18n, storage } = art;
this.$volume = append($control, icons.volume);
this.$volumeClose = append($control, icons.volumeClose);
this.$volumePanel = append($control, '<div class="art-volume-panel"></div>');
this.$volumeHandle = append(this.$volumePanel, '<div class="art-volume-slider-handle"></div>');
tooltip(this.$volume, i18n.get('Mute'));
setStyle(this.$volumeClose, 'display', 'none');
const volume = getStorage('volume');
this.setVolumeHandle(volume);
player.volume(volume);
proxy(this.$volume, 'click', () => {
setStyle(this.$volume, 'display', 'none');
setStyle(this.$volumeClose, 'display', 'block');
player.volume(0);
});
proxy(this.$volumeClose, 'click', () => {
setStyle(this.$volume, 'display', 'block');
setStyle(this.$volumeClose, 'display', 'none');
player.volume(getStorage('volume'));
});
proxy(this.$control, 'mouseenter', () => {
this.$volumePanel.classList.add('art-volume-panel-hover');
// TODO
setTimeout(() => {
this.setVolumeHandle(player.volume());
}, 200);
});
proxy(this.$control, 'mouseleave', () => {
this.$volumePanel.classList.remove('art-volume-panel-hover');
});
proxy(this.$volumePanel, 'click', event => {
this.volumeChangeFromEvent(event);
});
proxy(this.$volumeHandle, 'mousedown', () => {
this.isDroging = true;
});
proxy(this.$volumeHandle, 'mousemove', event => {
if (this.isDroging) {
this.volumeChangeFromEvent(event);
}
});
proxy(document, 'mouseup', () => {
if (this.isDroging) {
this.isDroging = false;
}
});
this.art.on('video:volumechange', () => {
const percentage = player.volume();
this.setVolumeHandle(percentage);
art.on('volume', percentage => {
if (percentage === 0) {
setStyle(this.$volume, 'display', 'none');
setStyle(this.$volumeClose, 'display', 'block');
@@ -82,19 +25,59 @@ export default class Volume {
setStyle(this.$volumeClose, 'display', 'none');
}
});
art.on('video:volumechange', () => {
this.setVolumeHandle(player.volume);
});
proxy(this.$volume, 'click', () => {
player.volume = 0;
});
proxy(this.$volumeClose, 'click', () => {
player.volume = storage.get('volume');
});
hover($control, () => {
this.$volumePanel.classList.add('art-volume-panel-hover');
sleep(200).then(() => {
this.setVolumeHandle(player.volume);
});
}, () => {
this.$volumePanel.classList.remove('art-volume-panel-hover');
});
proxy(this.$volumePanel, 'click', event => {
player.volume = this.volumeChangeFromEvent(event);
});
proxy(this.$volumeHandle, 'mousedown', () => {
this.isDroging = true;
});
proxy(this.$volumeHandle, 'mousemove', event => {
if (this.isDroging) {
player.volume = this.volumeChangeFromEvent(event);
}
});
proxy(document, 'mouseup', () => {
if (this.isDroging) {
this.isDroging = false;
}
});
}
volumeChangeFromEvent(event) {
const { player } = this.art;
const { left: panelLeft, width: panelWidth } = this.$volumePanel.getBoundingClientRect();
const { width: handleWidth } = this.$volumeHandle.getBoundingClientRect();
const percentage = clamp(event.x - panelLeft - handleWidth / 2, 0, panelWidth - handleWidth / 2) / (panelWidth - handleWidth);
player.volume(percentage);
return percentage;
}
setVolumeHandle(percentage = 0.7) {
const { width: panelWidth } = this.$volumePanel.getBoundingClientRect();
const { width: handleWidth } = this.$volumeHandle.getBoundingClientRect();
const panelWidth = getStyle(this.$volumePanel, 'width');
const handleWidth = getStyle(this.$volumeHandle, 'width');
const width = handleWidth / 2 + (panelWidth - handleWidth) * percentage - handleWidth / 2;
setStyle(this.$volumeHandle, 'left', `${width}px`);
}
-72
View File
@@ -1,72 +0,0 @@
import { debounce } from './utils';
export default class Events {
constructor(art) {
this.art = art;
this.destroyEvents = [];
this.proxy = this.proxy.bind(this);
this.hover = this.hover.bind(this);
this.init();
}
init() {
const { refs: { $player } } = this.art;
this.hover($player, () => {
$player.classList.add('artplayer-hover');
this.art.emit('hoverenter');
}, () => {
$player.classList.remove('artplayer-hover');
$player.classList.remove('artplayer-hide-cursor');
this.art.emit('hoverleave');
});
this.proxy(document, ['click', 'contextmenu'], event => {
if (event.composedPath().indexOf($player) > -1) {
this.art.isFocus = true;
} else {
this.art.isFocus = false;
this.art.contextmenu.hide();
}
});
const hideCursor = debounce(() => {
$player.classList.add('artplayer-hide-cursor');
if (this.art.player.fullscreenState || this.art.player.fullscreenWebState) {
$player.classList.remove('artplayer-hover');
this.art.controls.hide();
}
}, 5000);
this.proxy($player, 'mousemove', () => {
$player.classList.remove('artplayer-hide-cursor');
this.art.controls.show();
if (!this.art.player.pipState) {
hideCursor();
} else {
hideCursor.clearTimeout();
}
});
}
proxy(target, name, callback, option = {}) {
if (Array.isArray(name)) {
name.forEach(item => this.proxy(target, item, callback, option));
return;
}
target.addEventListener(name, callback, option);
this.destroyEvents.push(() => {
target.removeEventListener(name, callback, option);
});
}
hover(target, mouseenter, mouseleave) {
this.proxy(target, 'mouseenter', mouseenter);
this.proxy(target, 'mouseleave', mouseleave);
}
destroy() {
this.destroyEvents.forEach(event => event());
}
}
@@ -0,0 +1,11 @@
export default function clickInit(art, events) {
const { refs: { $player } } = art;
events.proxy(document, ['click', 'contextmenu'], event => {
if (event.composedPath().indexOf($player) > -1) {
art.isFocus = true;
} else {
art.isFocus = false;
art.contextmenu.hide();
}
});
}
@@ -0,0 +1,15 @@
export default function hoverInit(art, events) {
const { refs: { $player } } = art;
events.hover(
$player,
() => {
$player.classList.add('artplayer-hover');
art.emit('hoverenter');
},
() => {
$player.classList.remove('artplayer-hover');
$player.classList.remove('artplayer-hide-cursor');
art.emit('hoverleave');
}
);
}
+37
View File
@@ -0,0 +1,37 @@
import clickInit from './clickInit';
import hoverInit from './hoverInit';
import mousemoveInit from './mousemoveInit';
import resizeInit from './resizeInit';
export default class Events {
constructor(art) {
this.destroyEvents = [];
this.proxy = this.proxy.bind(this);
this.hover = this.hover.bind(this);
clickInit(art, this);
hoverInit(art, this);
mousemoveInit(art, this);
resizeInit(art, this);
}
proxy(target, name, callback, option = {}) {
if (Array.isArray(name)) {
name.forEach(item => this.proxy(target, item, callback, option));
return;
}
target.addEventListener(name, callback, option);
this.destroyEvents.push(() => {
target.removeEventListener(name, callback, option);
});
}
hover(target, mouseenter, mouseleave) {
this.proxy(target, 'mouseenter', mouseenter);
this.proxy(target, 'mouseleave', mouseleave);
}
destroy() {
this.destroyEvents.forEach(event => event());
}
}
@@ -0,0 +1,23 @@
import { debounce } from '../utils';
export default function mousemoveInitInit(art, events) {
const { refs: { $player } } = art;
const hideCursor = debounce(() => {
$player.classList.add('artplayer-hide-cursor');
if (art.player.fullscreenState || art.player.fullscreenWebState) {
$player.classList.remove('artplayer-hover');
art.controls.hide();
}
}, 5000);
events.proxy($player, 'mousemove', () => {
$player.classList.remove('artplayer-hide-cursor');
art.controls.show();
if (!art.player.pipState) {
hideCursor();
} else {
hideCursor.clearTimeout();
}
});
}
@@ -0,0 +1,16 @@
import { sleep } from '../utils';
import { ResizeObserver } from 'resize-observer';
export default function clickInit(art, events) {
const { refs: { $player } } = art;
const resizeObserver = new ResizeObserver(() => {
sleep().then(() => {
art.player.aspectRatioReset();
art.emit('resize');
});
});
resizeObserver.observe($player);
events.destroyEvents.push(() => {
resizeObserver.unobserve($player);
});
}
+2
View File
@@ -19,6 +19,7 @@ import Loading from './loading';
import Notice from './notice';
import Mask from './mask';
import Setting from './setting';
import Storage from './storage';
let id = 0;
class Artplayer extends Emitter {
@@ -117,6 +118,7 @@ class Artplayer extends Emitter {
}
this.template = new Template(this);
this.storage = new Storage(this);
this.i18n = new I18n(this);
this.notice = new Notice(this);
this.events = new Events(this);
@@ -1,5 +1,8 @@
export default function currentTimeMix(art, player) {
Object.defineProperty(player, 'currentTime', {
value: () => art.refs.$video.currentTime || 0
get: () => art.refs.$video.currentTime || 0,
set: currentTime => {
art.refs.$video.currentTime = currentTime;
}
});
}
+1 -1
View File
@@ -1,5 +1,5 @@
export default function durationMix(art, player) {
Object.defineProperty(player, 'duration', {
value: () => art.refs.$video.duration || 0
get: () => art.refs.$video.duration || 0
});
}
+2 -1
View File
@@ -71,7 +71,8 @@ export default function eventInit(art, player) {
if (player.reconnectTime < player.maxReconnectTime) {
sleep(1000).then(() => {
player.reconnectTime++;
$video.src = option.url;
art.emit('beforeMountUrl', option.url);
$video.src = player.mountUrl(option.url);
notice.show(`${i18n.get('Reconnect')}: ${player.reconnectTime}`);
});
} else {
@@ -4,7 +4,6 @@ export default function fullscreenMix(art, player) {
const { notice, events: { destroyEvents }, refs: { $player } } = art;
const screenfullChange = () => {
player.aspectRatioReset();
art.emit('fullscreen', screenfull.isFullscreen);
};
@@ -11,7 +11,6 @@ export default function fullscreenWebMix(art, player) {
player.fullscreenExit();
}
$player.classList.add('artplayer-web-fullscreen');
player.aspectRatioReset();
art.emit('fullscreenWeb:enabled');
}
});
@@ -22,7 +21,6 @@ export default function fullscreenWebMix(art, player) {
player.fullscreenExit();
}
$player.classList.remove('artplayer-web-fullscreen');
player.aspectRatioReset();
art.emit('fullscreenWeb:exit');
}
});
+4
View File
@@ -15,6 +15,8 @@ import screenshotMix from './screenshotMix';
import fullscreenMix from './fullscreenMix';
import fullscreenWebMix from './fullscreenWebMix';
import pipMix from './pipMix';
import loadedMix from './loadedMix';
import playedMix from './playedMix';
export default class Player {
constructor(art) {
@@ -38,5 +40,7 @@ export default class Player {
fullscreenMix(art, this);
fullscreenWebMix(art, this);
pipMix(art, this);
loadedMix(art, this);
playedMix(art, this);
}
}
@@ -0,0 +1,10 @@
export default function seekMix(art, player) {
Object.defineProperty(player, 'loaded', {
get: () => {
const { $video } = art.refs;
return $video.buffered.length
? $video.buffered.end($video.buffered.length - 1) / $video.duration
: 0;
}
});
}
@@ -0,0 +1,8 @@
export default function seekMix(art, player) {
Object.defineProperty(player, 'played', {
get: () => {
const { $video } = art.refs;
return $video.currentTime / $video.duration;
}
});
}
Whitespace-only changes.
+5 -5
View File
@@ -1,16 +1,16 @@
import { secondToTime } from '../utils';
export default function seekMix(art, player) {
const { refs: { $video }, notice } = art;
const { notice } = art;
Object.defineProperty(player, 'seek', {
value: time => {
let newTime = Math.max(time, 0);
if ($video.duration) {
newTime = Math.min(newTime, $video.duration);
if (player.duration) {
newTime = Math.min(newTime, player.duration);
}
$video.currentTime = newTime;
notice.show(`${secondToTime(newTime)} / ${secondToTime($video.duration)}`);
player.currentTime = newTime;
notice.show(`${secondToTime(newTime)} / ${secondToTime(player.duration)}`);
art.emit('seek', newTime);
}
});
+1 -1
View File
@@ -3,7 +3,7 @@ export default function switchMix(art, player) {
Object.defineProperty(player, 'switch', {
value: (url, name = 'unknown') => {
const currentTime = player.currentTime();
const { currentTime } = player;
art.emit('beforeMountUrl', url);
$video.src = player.mountUrl(url);
option.url = url;
+5 -5
View File
@@ -1,19 +1,19 @@
import { clamp, setStorage } from '../utils';
import { clamp } from '../utils';
export default function volumeMix(art, player) {
const { refs: { $video }, i18n, notice } = art;
const { refs: { $video }, i18n, notice, storage } = art;
Object.defineProperty(player, 'volume', {
value: percentage => {
get: () => $video.volume || 0,
set: percentage => {
if (percentage !== undefined) {
$video.volume = clamp(percentage, 0, 1);
notice.show(`${i18n.get('Volume')}: ${parseInt($video.volume * 100)}`);
if ($video.volume !== 0) {
setStorage('volume', $video.volume);
storage.set('volume', $video.volume);
}
art.emit('volume', $video.volume);
}
return $video.volume || 0;
}
});
}
+31
View File
@@ -0,0 +1,31 @@
import { sleep } from './utils';
export default class Storage {
constructor(art) {
this.art = art;
this.storageName = 'artplayer_settings';
sleep().then(() => {
this.initVolume();
});
}
get(key) {
const storage = JSON.parse(localStorage.getItem(this.storageName));
return storage[key];
}
set(key, value) {
const storage = Object.assign({}, this.get() || {}, {
[key]: value
});
localStorage.setItem(this.storageName, JSON.stringify(storage));
}
initVolume() {
const { player } = this.art;
const volume = this.get('volume');
if (volume) {
player.volume = volume;
}
}
}
+5 -12
View File
@@ -70,6 +70,11 @@ export function setStyles(element, styles) {
return element;
}
export function getStyle(element, key, numberType = true) {
const value = window.getComputedStyle(element, null).getPropertyValue(key);
return numberType ? parseFloat(value) : value;
}
export function secondToTime(second) {
const add0 = num => num < 10 ? `0${num}` : String(num);
const hour = Math.floor(second / 3600);
@@ -105,18 +110,6 @@ export function deepMerge(...sources) {
return returnValue;
}
export function getStorage(key) {
const storage = JSON.parse(localStorage.getItem('artplayer_settings')) || {};
return key ? storage[key] : storage;
}
export function setStorage(key, value) {
const storage = Object.assign({}, getStorage(), {
[key]: value
});
localStorage.setItem('artplayer_settings', JSON.stringify(storage));
}
export function tooltip(target, msg, pos = 'up') {
target.setAttribute('data-balloon', msg);
target.setAttribute('data-balloon-pos', pos);