Sfoglia il codice sorgente

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

DIYgod 9 anni fa
parent
commit
c6a7eaceec
9 ha cambiato i file con 275 aggiunte e 87 eliminazioni
  1. 71 5
      README.md
  2. 1 0
      demo/index.html
  3. 0 0
      dist/DPlayer.min.css
  4. 0 0
      dist/DPlayer.min.js
  5. 0 0
      dist/DPlayer.min.js.map
  6. 3 2
      nodejs/index.js
  7. 1 1
      package.json
  8. 151 79
      src/DPlayer.js
  9. 48 0
      src/DPlayer.scss

+ 71 - 5
README.md

@@ -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)

+ 1 - 0
demo/index.html

@@ -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 it is too large
+ 0 - 0
dist/DPlayer.min.css


File diff suppressed because it is too large
+ 0 - 0
dist/DPlayer.min.js


File diff suppressed because it is too large
+ 0 - 0
dist/DPlayer.min.js.map


+ 3 - 2
nodejs/index.js

@@ -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;

+ 1 - 1
package.json

@@ -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": {

+ 151 - 79
src/DPlayer.js

@@ -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');
+                });
+            });
         }
     
         /**

+ 48 - 0
src/DPlayer.scss

@@ -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;
+            }
+        }
+    }
 }

Some files were not shown because too many files changed in this diff