style without danmaku; limit danmaku max length; right button menu; hot key

This commit is contained in:
DIYgod 2016-05-19 22:49:13 +08:00
parent 6b6b965ba1
commit c6a7eaceec
No known key found for this signature in database
GPG Key ID: F8797DD1088C6506
9 changed files with 279 additions and 91 deletions

View File

@ -13,12 +13,80 @@
**Notice:** This player is still under development.
[Demo](http://diygod.github.io/DPlayer/demo/)
## Install
```
$ npm install dplayer --save
```
## Usage
### HTML
```HTML
<link rel="stylesheet" href="DPlayer.min.css">
<!-- ... -->
<div id="player1" class="dplayer"></div>
<!-- ... -->
<script src="DPlayer.min.js"></script>
```
### JS
```JS
var dp = new DPlayer(option);
dp.init();
```
#### Options
```JS
var option = {
element: document.getElementById('player1'), // Optional, player element
autoplay: false, // Optional, autoplay video, not supported by mobile browsers
theme: '#FADFA3', // Optional, theme color, default: #b7daff
loop: true, // Optional, loop play music, default: true
video: { // Required, video info
url: '若能绽放光芒.mp4', // Required, video url
pic: '若能绽放光芒.png' // Optional, music picture
},
danmaku: { // Optional, showing danmaku
id: '9E2E3368B56CDBB4', // Required, danmaku id, MUST BE UNIQUE
api: 'https://dplayer.daoapp.io/', // Required, danmaku api
token: 'tokendemo' // Optional, danmaku token for api
}
}
```
#### API
+ `dp.init()`
+ `dp.play()`
+ `dp.pause()`
#### Event binding
`dp.on(event, handler)`
`event`:
+ `play`: Triggered when DPlayer start play
+ `pause`: Triggered when DPlayer paused
+ `canplay`: Triggered when enough data is available that DPlayer can be played
+ `playing`: Triggered periodically when DPlayer is playing
+ `ended`: Triggered when DPlayer ended
+ `error`: Triggered when an error occurs
#### Work with module bundler
```js
var DPlayer = require('DPlayer');
var dp = new DPlayer({
// ...
});
```
## Run in development
```
@ -39,16 +107,14 @@ $ npm run build
## Todo
- [ ] 无弹幕
- [ ] 中英文切换
- [ ] 保存刚发的弹幕
- [ ] 微博登录
- [ ] 快捷键: 空格暂停,双击全屏, 方向键控制进度
- [ ] 锁定IP规则
- [ ] icon 动画
- [ ] 右键
## LICENSE
MIT © [DIYgod](http://github.com/DIYgod)

View File

@ -60,6 +60,7 @@
element: document.getElementsByClassName('dplayer')[0],
autoplay: false,
theme: '#FADFA3',
loop: true,
video: {
url: 'http://devtest.qiniudn.com/若能绽放光芒.mp4',
pic: 'http://devtest.qiniudn.com/若能绽放光芒.png'

File diff suppressed because one or more lines are too long

4
dist/DPlayer.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -111,7 +111,7 @@ app.post('/', function (req, res) {
// check black ip
var blanklist = fs.readFileSync('blacklist').toString().split('\n');
if (blanklist.indexOf(ip) !== -1) {
if (blanklist.indexOf(ip.split(',')[0]) !== -1) {
logger.info(`Reject POST form ${ip} for black ip.`);
res.send(`{"code": -1, "msg": "Rejected for black ip."}`);
return;
@ -150,7 +150,8 @@ app.post('/', function (req, res) {
|| jsonStr.time === undefined
|| jsonStr.text === undefined
|| jsonStr.color === undefined
|| jsonStr.type === undefined) {
|| jsonStr.type === undefined
|| jsonStr.text.length >= 30) {
logger.info(`Reject POST form ${ip} for illegal data: ${JSON.stringify(jsonStr)}`);
res.send(`{"code": -3, "msg": "Rejected for illegal data"}`);
return;

View File

@ -1,6 +1,6 @@
{
"name": "dplayer",
"version": "0.0.2",
"version": "0.1.0",
"description": "Wow, such a lovely HTML5 danmaku video player",
"main": "dist/DPlayer.min.js",
"scripts": {

View File

@ -99,11 +99,14 @@
*/
init() {
this.element = this.option.element;
if (!this.option.danmaku) {
this.element.classList.add('dplayer-no-danmaku');
}
this.element.innerHTML = `
<div class="dplayer-mask"></div>
<div class="dplayer-video-wrap">
<video class="dplayer-video" poster="${this.option.video.pic}">
<video class="dplayer-video" ${this.option.video.pic ? `poster="${this.option.video.pic}"` : ``}>
<source src="${this.option.video.url}" type="video/mp4">
</video>
<div class="dplayer-danmaku"></div>
@ -216,7 +219,7 @@
</label>
</div>
</div>
<input class="dplayer-comment-input" type="text" placeholder="输入弹幕,回车发送">
<input class="dplayer-comment-input" type="text" placeholder="输入弹幕,回车发送" maxlength="30">
<button class="dplayer-icon dplayer-send-icon">`
+ this.getSVG('send')
+ ` </button>
@ -241,11 +244,14 @@
</div>
</div>
</div>
<div class="dplayer-menu">
<div class="dplayer-menu-item"><span class="dplayer-menu-label"><a target="_blank" href="http://diygod.me/">关于作者</a></span></div>
<div class="dplayer-menu-item"><span class="dplayer-menu-label"><a target="_blank" href="https://github.com/DIYgod/DPlayer">关于 DPlayer 播放器</a></span></div>
</div>
`;
// get this audio object
this.audio = this.element.getElementsByClassName('dplayer-video')[0];
window.audio = this.audio // Todo
this.bezel = this.element.getElementsByClassName('dplayer-bezel-icon')[0];
this.bezel.addEventListener('animationend', () => {
@ -318,17 +324,17 @@
this.loadedBar = this.element.getElementsByClassName('dplayer-loaded')[0];
this.bar = this.element.getElementsByClassName('dplayer-bar-wrap')[0];
let barWidth;
this.audio.addEventListener('seeked', () => {
if (this.option.danmaku) {
if (this.option.danmaku) {
this.audio.addEventListener('seeked', () => {
for (let i = 0; i < this.dan.length; i++) {
if (this.dan[i].time >= this.audio.currentTime) {
this.danIndex = i;
return;
}
}
}
});
});
}
let lastPlayPos = 0;
let currentPlayPos = 0;
@ -342,13 +348,13 @@
&& currentPlayPos < (lastPlayPos + 0.01)
&& !this.audio.paused) {
this.element.classList.add('dplayer-loading');
bufferingDetected = true
bufferingDetected = true;
}
if (bufferingDetected
&& currentPlayPos > (lastPlayPos + 0.01)
&& !this.audio.paused) {
this.element.classList.remove('dplayer-loading');
bufferingDetected = false
bufferingDetected = false;
}
lastPlayPos = currentPlayPos;
@ -395,7 +401,6 @@
percentage = percentage > 0 ? percentage : 0;
percentage = percentage < 1 ? percentage : 1;
this.updateBar('played', percentage, 'width');
this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = this.secondToTime(percentage * this.audio.duration);
this.audio.currentTime = parseFloat(this.playedBar.style.width) / 100 * this.audio.duration;
});
@ -490,8 +495,8 @@
this.updateBar('volume', 0, 'width');
}
});
/**
* auto hide controller
*/
@ -635,50 +640,52 @@
});
}
});
// danmaku opacity
this.danmakuBar = this.element.getElementsByClassName('dplayer-danmaku-bar-inner')[0];
const danmakuBarWrapWrap = this.element.getElementsByClassName('dplayer-danmaku-bar-wrap')[0];
const danmakuBarWrap = this.element.getElementsByClassName('dplayer-danmaku-bar')[0];
const danmakuSettingBox = this.element.getElementsByClassName('dplayer-setting-danmaku')[0];
const dWidth = 130;
this.updateBar('danmaku', this.danOpacity, 'width');
const danmakuMove = (event) => {
const e = event || window.event;
let percentage = (e.clientX - getElementViewLeft(danmakuBarWrap)) / dWidth;
percentage = percentage > 0 ? percentage : 0;
percentage = percentage < 1 ? percentage : 1;
this.updateBar('danmaku', percentage, 'width');
const items = this.element.getElementsByClassName('dplayer-danmaku-item');
for (let i = 0; i < items.length; i++) {
items[i].style.opacity = percentage;
}
this.danOpacity = percentage;
};
const danmakuUp = () => {
document.removeEventListener('mouseup', danmakuUp);
document.removeEventListener('mousemove', danmakuMove);
danmakuSettingBox.classList.remove('dplayer-setting-danmaku-active');
};
danmakuBarWrapWrap.addEventListener('click', (event) => {
const e = event || window.event;
let percentage = (e.clientX - getElementViewLeft(danmakuBarWrap)) / dWidth;
percentage = percentage > 0 ? percentage : 0;
percentage = percentage < 1 ? percentage : 1;
this.updateBar('danmaku', percentage, 'width');
const items = this.element.getElementsByClassName('dplayer-danmaku-item');
for (let i = 0; i < items.length; i++) {
items[i].style.opacity = percentage;
}
this.danOpacity = percentage;
});
danmakuBarWrapWrap.addEventListener('mousedown', () => {
document.addEventListener('mousemove', danmakuMove);
document.addEventListener('mouseup', danmakuUp);
danmakuSettingBox.classList.add('dplayer-setting-danmaku-active');
});
if (this.option.danmaku) {
// danmaku opacity
this.danmakuBar = this.element.getElementsByClassName('dplayer-danmaku-bar-inner')[0];
const danmakuBarWrapWrap = this.element.getElementsByClassName('dplayer-danmaku-bar-wrap')[0];
const danmakuBarWrap = this.element.getElementsByClassName('dplayer-danmaku-bar')[0];
const danmakuSettingBox = this.element.getElementsByClassName('dplayer-setting-danmaku')[0];
const dWidth = 130;
this.updateBar('danmaku', this.danOpacity, 'width');
const danmakuMove = (event) => {
const e = event || window.event;
let percentage = (e.clientX - getElementViewLeft(danmakuBarWrap)) / dWidth;
percentage = percentage > 0 ? percentage : 0;
percentage = percentage < 1 ? percentage : 1;
this.updateBar('danmaku', percentage, 'width');
const items = this.element.getElementsByClassName('dplayer-danmaku-item');
for (let i = 0; i < items.length; i++) {
items[i].style.opacity = percentage;
}
this.danOpacity = percentage;
};
const danmakuUp = () => {
document.removeEventListener('mouseup', danmakuUp);
document.removeEventListener('mousemove', danmakuMove);
danmakuSettingBox.classList.remove('dplayer-setting-danmaku-active');
};
danmakuBarWrapWrap.addEventListener('click', (event) => {
const e = event || window.event;
let percentage = (e.clientX - getElementViewLeft(danmakuBarWrap)) / dWidth;
percentage = percentage > 0 ? percentage : 0;
percentage = percentage < 1 ? percentage : 1;
this.updateBar('danmaku', percentage, 'width');
const items = this.element.getElementsByClassName('dplayer-danmaku-item');
for (let i = 0; i < items.length; i++) {
items[i].style.opacity = percentage;
}
this.danOpacity = percentage;
});
danmakuBarWrapWrap.addEventListener('mousedown', () => {
document.addEventListener('mousemove', danmakuMove);
document.addEventListener('mouseup', danmakuUp);
danmakuSettingBox.classList.add('dplayer-setting-danmaku-active');
});
}
};
settingEvent();
@ -731,8 +738,8 @@
if (this.audio.duration !== 1) { // compatibility: Android browsers will output 1 at first
this.element.getElementsByClassName('dplayer-dtime')[0].innerHTML = this.audio.duration ? this.secondToTime(this.audio.duration) : '00:00';
}
/**
* danmaku display
*/
@ -746,18 +753,18 @@
top: {},
bottom: {}
};
const danItemRight = (ele) => {
return danContainer.getBoundingClientRect().right - ele.getBoundingClientRect().right;
};
const danSpeed = (ele) => {
return (danWidth + ele.offsetWidth) / 5;
};
const getTunnel = (ele, type) => {
const tmp = danWidth / danSpeed(ele);
for (let i = 0; ; i++) {
let item = danTunnel[type][i + ''];
if (item && item.length) {
@ -784,7 +791,7 @@
}
}
};
this.danmakuIn = (text, color, type) => {
danWidth = danContainer.offsetWidth;
danHeight = danContainer.offsetHeight;
@ -795,10 +802,10 @@
item.classList.add(`dplayer-danmaku-${type}`);
item.appendChild(content);
item.style.opacity = this.danOpacity;
// insert
danContainer.appendChild(item);
// adjust
item.style.color = color;
switch (type) {
@ -825,11 +832,11 @@
default:
console.error(`Can't handled danmaku type: ${type}`);
}
// move
item.classList.add(`dplayer-danmaku-move`);
};
// danmaku
if (this.option.danmaku) {
this.danIndex = 0;
@ -838,8 +845,7 @@
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
this.dan = JSON.parse(xhr.responseText).danmaku.sort((a, b) => a.time - b.time);
console.log(this.dan);
// autoplay
if (this.option.autoplay && !this.isMobile) {
this.play();
@ -865,8 +871,8 @@
this.pause();
}
}
/**
* comment
*/
@ -897,7 +903,7 @@
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
console.log(JSON.parse(xhr.responseText));
console.log('Post danmaku: ', JSON.parse(xhr.responseText));
}
else {
console.log('Request was unsuccessful: ' + xhr.status);
@ -909,9 +915,11 @@
commentInput.value = '';
closeComment();
this.dan.splice(this.danIndex, 0, danmakuData);
this.danIndex++;
this.danmakuIn(danmakuData.text, danmakuData.color, danmakuData.type);
};
const closeCommentSetting = () => {
if (commentSettingBox.classList.contains('dplayer-comment-setting-open')) {
commentSettingBox.classList.remove('dplayer-comment-setting-open');
@ -925,7 +933,7 @@
commentSettingBox.classList.add('dplayer-comment-setting-open');
}
};
let disableHide = 0;
const closeComment = () => {
if (commentBox.classList.contains('dplayer-comment-box-open')) {
@ -944,7 +952,7 @@
}, 1000);
this.element.classList.add('dplayer-show-controller');
};
mask.addEventListener('click', () => {
closeComment();
});
@ -957,7 +965,7 @@
commentSettingIcon.addEventListener('click', () => {
toggleCommentSetting();
});
// comment setting box
this.element.getElementsByClassName('dplayer-comment-setting-color')[0].addEventListener('click', () => {
const sele = this.element.querySelector('input[name="dplayer-danmaku-color"]:checked+span');
@ -965,7 +973,7 @@
commentSettingIcon.setAttribute('style', sele.getAttribute('style'));
}
});
commentInput.addEventListener('click', () => {
closeCommentSetting();
});
@ -975,7 +983,7 @@
sendComment();
}
});
commentSendIcon.addEventListener('click', sendComment);
@ -1024,6 +1032,70 @@
}
resetAnimation();
});
/**
* hot key
*/
document.addEventListener('keydown', (e) => {
const event = e || window.event;
let percentage;
switch (event.keyCode) {
case 32:
event.preventDefault();
this.toggle();
break;
case 37:
event.preventDefault();
this.audio.currentTime = this.audio.currentTime -5;
break;
case 39:
event.preventDefault();
this.audio.currentTime = this.audio.currentTime + 5;
break;
case 38:
event.preventDefault();
percentage = this.audio.volume + 0.1;
percentage = percentage > 0 ? percentage : 0;
percentage = percentage < 1 ? percentage : 1;
this.updateBar('volume', percentage, 'width');
this.audio.volume = percentage;
if (this.audio.muted) {
this.audio.muted = false;
}
switchVolumeIcon();
break;
case 40:
event.preventDefault();
percentage = this.audio.volume - 0.1;
percentage = percentage > 0 ? percentage : 0;
percentage = percentage < 1 ? percentage : 1;
this.updateBar('volume', percentage, 'width');
this.audio.volume = percentage;
if (this.audio.muted) {
this.audio.muted = false;
}
switchVolumeIcon();
break;
}
});
/**
* right key
*/
this.menu = this.element.getElementsByClassName('dplayer-menu')[0];
this.element.addEventListener('contextmenu', (e) => {
const event = e || window.event;
event.preventDefault();
this.menu.style.left = event.clientX - this.element.getBoundingClientRect().left + 'px';
this.menu.style.top = event.clientY - this.element.getBoundingClientRect().top + 'px';
this.menu.classList.add('dplayer-menu-show');
mask.classList.add('dplayer-mask-show');
mask.addEventListener('click', () => {
mask.classList.remove('dplayer-mask-show');
this.menu.classList.remove('dplayer-menu-show');
});
});
}
/**

View File

@ -2,6 +2,7 @@
position: relative;
overflow: hidden;
user-select: none;
line-height: 1;
&:-webkit-full-screen {
width: 100%;
@ -9,6 +10,20 @@
background: #000;
}
&.dplayer-no-danmaku {
.dplayer-controller .dplayer-icons .dplayer-setting .dplayer-setting-box {
height: 60px;
}
.dplayer-controller .dplayer-icons .dplayer-comment {
display: none;
}
.dplayer-danmaku {
display: none;
}
}
&.dplayer-playing {
.dplayer-controller-mask {
opacity: 0;
@ -778,4 +793,37 @@
}
}
}
.dplayer-menu {
position: absolute;
width: 150px;
border-radius: 2px;
background: rgba(28, 28, 28, 0.9);
padding: 7px 0;
overflow: hidden;
z-index: 3;
display: none;
&.dplayer-menu-show {
display: block;
}
.dplayer-menu-item {
height: 30px;
padding: 5px 10px;
box-sizing: border-box;
cursor: pointer;
&:hover {
background-color: rgba(255,255,255,.1);
}
.dplayer-menu-label a {
color: #eee;
font-size: 13px;
display: inline-block;
vertical-align: middle;
}
}
}
}