创建组件父类

This commit is contained in:
Harvey committed 2019-11-22 15:35:21 +08:00
1 parent 0c1c1273a6
commit 1d859e2049
20 files changed
+770 -921

No files matched your search

+82 -101
View File
@@ -1,4 +1,5 @@
import { setStyle, addClass, removeClass } from '../utils';
import { setStyle } from '../utils';
import Component from '../utils/components';
import component from '../utils/component';
import playbackRate from './playbackRate';
import aspectRatio from './aspectRatio';
@@ -6,13 +7,89 @@ import info from './info';
import version from './version';
import close from './close';
export default class Contextmenu {
export default class Contextmenu extends Component {
constructor(art) {
this.id = 0;
this.art = art;
super(art);
art.on('ready', () => {
this.init();
const {
option,
template: { $player, $contextmenu },
events: { proxy },
} = art;
this.add(
playbackRate({
disable: !option.playbackRate,
name: 'playbackRate',
index: 10,
}),
);
this.add(
aspectRatio({
disable: !option.aspectRatio,
name: 'aspectRatio',
index: 20,
}),
);
this.add(
info({
disable: false,
name: 'info',
index: 30,
}),
);
this.add(
version({
disable: false,
name: 'version',
index: 40,
}),
);
this.add(
close({
disable: false,
name: 'close',
index: 50,
}),
);
option.contextmenu.forEach(item => {
this.add(item);
});
proxy($player, 'contextmenu', event => {
event.preventDefault();
this.show = true;
const mouseX = event.clientX;
const mouseY = event.clientY;
const { height: cHeight, width: cWidth, left: cLeft, top: cTop } = $player.getBoundingClientRect();
const { height: mHeight, width: mWidth } = $contextmenu.getBoundingClientRect();
let menuLeft = mouseX - cLeft;
let menuTop = mouseY - cTop;
if (mouseX + mWidth > cLeft + cWidth) {
menuLeft = cWidth - mWidth;
}
if (mouseY + mHeight > cTop + cHeight) {
menuTop = cHeight - mHeight;
}
setStyle($contextmenu, 'left', `${menuLeft}px`);
setStyle($contextmenu, 'top', `${menuTop}px`);
});
proxy($player, 'click', event => {
if (!event.composedPath().includes($contextmenu)) {
this.show = false;
}
});
});
art.on('blur', () => {
@@ -20,105 +97,9 @@ export default class Contextmenu {
});
}
init() {
const {
option,
template: { $player, $contextmenu },
events: { proxy },
} = this.art;
this.add(
playbackRate({
disable: !option.playbackRate,
name: 'playbackRate',
index: 10,
}),
);
this.add(
aspectRatio({
disable: !option.aspectRatio,
name: 'aspectRatio',
index: 20,
}),
);
this.add(
info({
disable: false,
name: 'info',
index: 30,
}),
);
this.add(
version({
disable: false,
name: 'version',
index: 40,
}),
);
this.add(
close({
disable: false,
name: 'close',
index: 50,
}),
);
option.contextmenu.forEach(item => {
this.add(item);
});
proxy($player, 'contextmenu', event => {
event.preventDefault();
this.show = true;
this.setPos(event);
});
proxy($player, 'click', event => {
if (!event.composedPath().includes($contextmenu)) {
this.show = false;
}
});
}
add(item, callback) {
this.id += 1;
const { $contextmenu } = this.art.template;
return component(this.art, this, $contextmenu, item, callback, 'contextmenu');
}
setPos(event) {
const { $player, $contextmenu } = this.art.template;
const mouseX = event.clientX;
const mouseY = event.clientY;
const { height: cHeight, width: cWidth, left: cLeft, top: cTop } = $player.getBoundingClientRect();
const { height: mHeight, width: mWidth } = $contextmenu.getBoundingClientRect();
let menuLeft = mouseX - cLeft;
let menuTop = mouseY - cTop;
if (mouseX + mWidth > cLeft + cWidth) {
menuLeft = cWidth - mWidth;
}
if (mouseY + mHeight > cTop + cHeight) {
menuTop = cHeight - mHeight;
}
setStyle($contextmenu, 'left', `${menuLeft}px`);
setStyle($contextmenu, 'top', `${menuTop}px`);
}
set show(value) {
const { $player } = this.art.template;
if (value) {
addClass($player, 'art-contextmenu-show');
this.art.emit('contextmenu:show');
} else {
removeClass($player, 'art-contextmenu-show');
this.art.emit('contextmenu:hide');
}
}
}
+105 -119
View File
@@ -1,4 +1,5 @@
import { errorHandle, addClass, removeClass } from '../utils';
import { errorHandle, addClass } from '../utils';
import Component from '../utils/components';
import component from '../utils/component';
import fullscreen from './fullscreen';
import fullscreenWeb from './fullscreenWeb';
@@ -13,128 +14,124 @@ import thumbnails from './thumbnails';
import screenshot from './screenshot';
import quality from './quality';
export default class Controls {
export default class Controls extends Component {
constructor(art) {
this.id = 0;
this.art = art;
this.art.on('ready', () => {
this.init();
});
}
super(art);
init() {
const { option } = this.art;
art.on('ready', () => {
const { option } = art;
this.add(
progress({
name: 'progress',
disable: option.isLive,
position: 'top',
index: 10,
}),
);
this.add(
progress({
name: 'progress',
disable: option.isLive,
position: 'top',
index: 10,
}),
);
this.add(
thumbnails({
name: 'thumbnails',
disable: !option.thumbnails.url || option.isLive,
position: 'top',
index: 20,
}),
);
this.add(
thumbnails({
name: 'thumbnails',
disable: !option.thumbnails.url || option.isLive,
position: 'top',
index: 20,
}),
);
this.add(
playAndPause({
name: 'playAndPause',
disable: false,
position: 'left',
index: 10,
}),
);
this.add(
playAndPause({
name: 'playAndPause',
disable: false,
position: 'left',
index: 10,
}),
);
this.add(
volume({
name: 'volume',
disable: false,
position: 'left',
index: 20,
}),
);
this.add(
volume({
name: 'volume',
disable: false,
position: 'left',
index: 20,
}),
);
this.add(
time({
name: 'time',
disable: option.isLive,
position: 'left',
index: 30,
}),
);
this.add(
time({
name: 'time',
disable: option.isLive,
position: 'left',
index: 30,
}),
);
this.add(
quality({
name: 'quality',
disable: option.quality.length === 0,
position: 'right',
index: 10,
}),
);
this.add(
quality({
name: 'quality',
disable: option.quality.length === 0,
position: 'right',
index: 10,
}),
);
this.add(
screenshot({
name: 'screenshot',
disable: !option.screenshot,
position: 'right',
index: 20,
}),
);
this.add(
screenshot({
name: 'screenshot',
disable: !option.screenshot,
position: 'right',
index: 20,
}),
);
this.add(
subtitle({
name: 'subtitle',
disable: !option.subtitle.url,
position: 'right',
index: 30,
}),
);
this.add(
subtitle({
name: 'subtitle',
disable: !option.subtitle.url,
position: 'right',
index: 30,
}),
);
this.add(
setting({
name: 'setting',
disable: !option.setting,
position: 'right',
index: 40,
}),
);
this.add(
setting({
name: 'setting',
disable: !option.setting,
position: 'right',
index: 40,
}),
);
this.add(
pip({
name: 'pip',
disable: !option.pip,
position: 'right',
index: 50,
}),
);
this.add(
pip({
name: 'pip',
disable: !option.pip,
position: 'right',
index: 50,
}),
);
this.add(
fullscreenWeb({
name: 'fullscreenWeb',
disable: !option.fullscreenWeb,
position: 'right',
index: 60,
}),
);
this.add(
fullscreenWeb({
name: 'fullscreenWeb',
disable: !option.fullscreenWeb,
position: 'right',
index: 60,
}),
);
this.add(
fullscreen({
name: 'fullscreen',
disable: !option.fullscreen,
position: 'right',
index: 70,
}),
);
this.add(
fullscreen({
name: 'fullscreen',
disable: !option.fullscreen,
position: 'right',
index: 70,
}),
);
option.controls.forEach(item => {
this.add(item);
option.controls.forEach(item => {
this.add(item);
});
});
}
@@ -168,15 +165,4 @@ export default class Controls {
}
return control;
}
set show(value) {
const { $player } = this.art.template;
if (value) {
addClass($player, 'art-controls-show');
this.art.emit('controls:show');
} else {
removeClass($player, 'art-controls-show');
this.art.emit('controls:hide');
}
}
}
+8 -8
View File
@@ -18,14 +18,14 @@ export default function subtitle(option) {
subtitle.toggle();
});
art.on('subtitle:show', () => {
setStyle($subtitle, 'opacity', '1');
tooltip($subtitle, i18n.get('Hide subtitle'));
});
art.on('subtitle:hide', () => {
setStyle($subtitle, 'opacity', '0.8');
tooltip($subtitle, i18n.get('Show subtitle'));
art.on('subtitle:toggle', value => {
if (value) {
setStyle($subtitle, 'opacity', '1');
tooltip($subtitle, i18n.get('Hide subtitle'));
} else {
setStyle($subtitle, 'opacity', '0.8');
tooltip($subtitle, i18n.get('Show subtitle'));
}
});
},
});
+24 -42
View File
@@ -1,59 +1,41 @@
import { addClass, removeClass, queryAll } from './utils';
import { queryAll } from './utils';
import Component from './utils/components';
export default class Info {
export default class Info extends Component {
constructor(art) {
this.art = art;
this.timer = null;
super(art);
const {
template: { $infoPanel, $infoClose },
template: { $infoPanel, $infoClose, $video },
events: { proxy },
} = art;
this.types = queryAll('[data-video]', $infoPanel);
proxy($infoClose, 'click', () => {
this.show = false;
});
this.art.on('destroy', () => {
clearTimeout(this.timer);
let timer = null;
const types = queryAll('[data-video]', $infoPanel);
art.on('destroy', () => {
clearTimeout(timer);
});
}
readInfo() {
const { $video } = this.art.template;
this.types.forEach(item => {
const value = $video[item.dataset.video];
item.innerHTML = typeof value === 'number' ? value.toFixed(2) : value;
});
}
loop() {
this.readInfo();
this.timer = setTimeout(() => {
this.readInfo();
this.loop();
}, 1000);
}
set show(value) {
const { $player } = this.art.template;
if (value) {
this.state = true;
addClass($player, 'art-info-show');
clearTimeout(this.timer);
this.loop();
this.art.emit('info:show');
} else {
this.state = false;
removeClass($player, 'art-info-show');
clearTimeout(this.timer);
this.art.emit('info:hide');
function loop() {
types.forEach(item => {
const value = $video[item.dataset.video];
item.innerHTML = typeof value === 'number' ? value.toFixed(2) : value;
});
timer = setTimeout(() => {
loop();
}, 1000);
}
}
toggle() {
this.show = !this.state;
art.on('info:toggle', value => {
clearTimeout(timer);
if (value) {
loop();
}
});
}
}
+3 -21
View File
@@ -1,10 +1,9 @@
import { addClass, removeClass } from './utils';
import component from './utils/component';
import Component from './utils/components';
export default class Layers {
export default class Layers extends Component {
constructor(art) {
this.id = 0;
this.art = art;
super(art);
this.add = this.add.bind(this);
this.art.on('ready', () => {
this.art.option.layers.forEach(item => {
@@ -18,21 +17,4 @@ export default class Layers {
const { $layers } = this.art.template;
return component(this.art, this, $layers, item, callback, 'layer');
}
set show(value) {
const { $player } = this.art.template;
if (value) {
this.state = true;
removeClass($player, 'art-layers-hide');
this.art.emit('layers:show');
} else {
this.state = false;
addClass($player, 'art-layers-hide');
this.art.emit('layers:hide');
}
}
toggle() {
this.show = !this.state;
}
}
+4 -20
View File
@@ -1,26 +1,10 @@
import { append, addClass, removeClass } from './utils';
import { append } from './utils';
import Component from './utils/components';
export default class Loading {
export default class Loading extends Component {
constructor(art) {
this.art = art;
super(art);
const { $loading } = art.template;
append($loading, art.icons.loading);
}
set show(value) {
const { $player } = this.art.template;
if (value) {
this.state = true;
addClass($player, 'art-loading-show');
this.art.emit('loading:show');
} else {
this.state = false;
removeClass($player, 'art-loading-show');
this.art.emit('loading:hide');
}
}
toggle() {
this.show = !this.state;
}
}
+4 -20
View File
@@ -1,26 +1,10 @@
import { append, addClass, removeClass } from './utils';
import { append } from './utils';
import Component from './utils/components';
export default class Mask {
export default class Mask extends Component {
constructor(art) {
this.art = art;
super(art);
const { $state } = art.template;
append($state, art.icons.state);
}
set show(value) {
const { $player } = this.art.template;
if (value) {
this.state = true;
addClass($player, 'art-mask-show');
this.art.emit('mask:show');
} else {
this.state = false;
removeClass($player, 'art-mask-show');
this.art.emit('mask:hide');
}
}
toggle() {
this.show = !this.state;
}
}
-2
View File
@@ -8,7 +8,6 @@ export default class Notice {
show(msg, autoHide = true, time = 1000) {
const { $player, $noticeInner } = this.art.template;
this.state = true;
addClass($player, 'art-notice-show');
$noticeInner.innerHTML = msg instanceof Error ? msg.message.trim() : msg;
clearTimeout(this.timer);
@@ -22,7 +21,6 @@ export default class Notice {
hide() {
const { $player } = this.art.template;
this.state = false;
removeClass($player, 'art-notice-show');
this.art.emit('notice:hide');
}
+36 -58
View File
@@ -1,17 +1,47 @@
import { addClass, removeClass } from '../utils';
import Component from '../utils/components';
import component from '../utils/component';
import flip from './flip';
import aspectRatio from './aspectRatio';
import playbackRate from './playbackRate';
export default class Setting {
export default class Setting extends Component {
constructor(art) {
this.id = 0;
this.art = art;
this.state = false;
super(art);
const {
option,
template: { $setting },
events: { proxy },
} = this.art;
if (art.option.setting) {
this.art.on('ready', () => {
this.init();
proxy($setting, 'click', e => {
if (e.target === $setting) {
this.show = false;
}
});
this.add(
flip({
disable: !option.flip,
name: 'flip',
}),
);
this.add(
aspectRatio({
disable: !option.aspectRatio,
name: 'aspectRatio',
}),
);
this.add(
playbackRate({
disable: !option.playbackRate,
name: 'playbackRate',
}),
);
});
this.art.on('blur', () => {
@@ -20,61 +50,9 @@ export default class Setting {
}
}
init() {
const {
option,
template: { $setting },
events: { proxy },
} = this.art;
proxy($setting, 'click', e => {
if (e.target === $setting) {
this.show = false;
}
});
this.add(
flip({
disable: !option.flip,
name: 'flip',
}),
);
this.add(
aspectRatio({
disable: !option.aspectRatio,
name: 'aspectRatio',
}),
);
this.add(
playbackRate({
disable: !option.playbackRate,
name: 'playbackRate',
}),
);
}
add(item, callback) {
this.id += 1;
const { $settingBody } = this.art.template;
return component(this.art, this, $settingBody, item, callback, 'setting');
}
set show(value) {
const { $player } = this.art.template;
if (value) {
this.state = true;
addClass($player, 'art-setting-show');
this.art.emit('setting:show');
} else {
this.state = false;
removeClass($player, 'art-setting-show');
this.art.emit('setting:hide');
}
}
toggle() {
this.show = !this.state;
}
}
+4 -3
View File
@@ -1,5 +1,6 @@
.art-video-player {
.art-subtitle {
display: none;
position: absolute;
z-index: 20;
bottom: 40px;
@@ -10,15 +11,15 @@
font-size: 20px;
pointer-events: none;
text-shadow: 0.5px 0.5px 0.5px rgba(0, 0, 0, 0.5);
p {
margin: 5px 0;
}
}
&.art-subtitle-hide {
&.art-subtitle-show {
.art-subtitle {
display: none;
display: block;
}
}
}
+4 -21
View File
@@ -1,9 +1,9 @@
import { setStyles, srtToVtt, vttToBlob, getExt, addClass, removeClass, assToVtt } from './utils';
import { setStyles, srtToVtt, vttToBlob, getExt, assToVtt } from './utils';
import Component from './utils/components';
export default class Subtitle {
export default class Subtitle extends Component {
constructor(art) {
this.art = art;
this.state = true;
super(art);
const {
events: { proxy },
@@ -88,21 +88,4 @@ export default class Subtitle {
throw err;
});
}
set show(value) {
const { $player } = this.art.template;
if (value) {
this.state = true;
removeClass($player, 'art-subtitle-hide');
this.art.emit('subtitle:show');
} else {
this.state = false;
addClass($player, 'art-subtitle-hide');
this.art.emit('subtitle:hide');
}
}
toggle() {
this.show = !this.state;
}
}
+5 -4
View File
@@ -3,6 +3,7 @@ import { errorHandle, query, addClass } from './utils';
export default class Template {
constructor(art) {
this.art = art;
if (art.option.container instanceof Element) {
this.$container = art.option.container;
} else {
@@ -33,7 +34,7 @@ export default class Template {
initDesktop() {
this.$container.innerHTML = `
<div class="art-video-player" style="--theme: ${this.art.option.theme}">
<div class="art-video-player art-subtitle-show" style="--theme: ${this.art.option.theme}">
<video class="art-video">
<track default kind="metadata"></track>
</video>
@@ -127,9 +128,9 @@ export default class Template {
initMobile() {
this.$container.innerHTML = `
<div class="art-video-player">
<video class="art-video"></video>
</div>
<div class="art-video-player">
<video class="art-video"></video>
</div>
`;
this.$player = this.query('.art-video-player');
this.$video = this.query('.art-video');
@@ -0,0 +1,30 @@
import { hasClass, addClass, removeClass } from './dom';
import { errorHandle } from './error';
export default class Component {
constructor(art) {
this.id = 0;
this.art = art;
this.$player = art.template.$player;
this.name = this.constructor.name.toLowerCase();
this.showClassName = `art-${this.name}-show`;
}
get show() {
return hasClass(this.$player, this.showClassName);
}
set show(value) {
errorHandle(value === false || value === true, 'The show attribute expects a boolean value');
if (value) {
addClass(this.$player, this.showClassName);
} else {
removeClass(this.$player, this.showClassName);
}
this.art.emit(`${this.name}:toggle`, value);
}
toggle() {
this.show = !this.show;
}
}
+27
View File
@@ -1,3 +1,5 @@
import { defineProperty } from './property';
export function query(selector, parent = document) {
return parent.querySelector(selector);
}
@@ -61,3 +63,28 @@ export function tooltip(target, msg, pos = 'up') {
target.setAttribute('aria-label', msg);
target.setAttribute('data-balloon-pos', pos);
}
export function addShowProperty(target, ref, name) {
const className = `art-${name}-show`;
defineProperty(target, 'show', {
get() {
return hasClass(ref, className);
},
set(value) {
if (value) {
addClass(ref, className);
} else {
removeClass(ref, className);
}
target.art.emit(`${name}:toggle`, value);
},
});
defineProperty(target, 'toggle', {
value: () => {
target.show = !target.show;
},
});
}
+4
View File
@@ -4,6 +4,10 @@ export function hasOwnProperty(obj, name) {
return Object.prototype.hasOwnProperty.call(obj, name);
}
export function defineProperty(obj, key, prop) {
return Object.defineProperty(obj, key, prop);
}
export function proxyPropertys(target, ...sources) {
return sources.reduce((result, source) => {
Object.getOwnPropertyNames(source).forEach(key => {