瀏覽代碼

extend controller module and some fixes

DIYgod 7 年之前
父節點
當前提交
e3cfb21fdf
共有 6 個文件被更改,包括 437 次插入448 次删除
  1. 195 1
      demo/demo.js
  2. 9 195
      demo/index.html
  3. 14 245
      src/DPlayer.js
  4. 199 1
      src/controller.js
  5. 6 6
      src/thumbnails.js
  6. 14 0
      src/utils.js

+ 195 - 1
demo/demo.js

@@ -9,4 +9,198 @@ function animate () {
 
     requestAnimationFrame(animate);
 }
-requestAnimationFrame(animate);
+requestAnimationFrame(animate);
+
+initPlayers();
+
+function initPlayers () {
+    // dp1
+    window.dp1 = new DPlayer({
+        container: document.getElementById('dplayer1'),
+        preload: 'none',
+        video: {
+            url: 'https://moeplayer.b0.upaiyun.com/dplayer/hikarunara.mp4',
+            pic: 'https://moeplayer.b0.upaiyun.com/dplayer/hikarunara.png',
+            thumbnails: 'https://moeplayer.b0.upaiyun.com/dplayer/hikarunara_thumbnails.jpg'
+        },
+        subtitle: {
+            url: 'https://moeplayer.b0.upaiyun.com/dplayer/hikarunara.vtt'
+        },
+        danmaku: {
+            id: '9E2E3368B56CDBB4',
+            api: 'https://api.prprpr.me/dplayer/'
+        }
+    });
+
+    // dp2
+    window.dp2 = new DPlayer({
+        container: document.getElementById('dplayer2'),
+        preload: 'none',
+        autoplay: false,
+        theme: '#FADFA3',
+        loop: true,
+        screenshot: true,
+        hotkey: true,
+        logo: 'https://moeplayer.b0.upaiyun.com/dplayer/DPlayer.png',
+        volume: 0.2,
+        mutex: true,
+        video: {
+            url: 'https://moeplayer.b0.upaiyun.com/dplayer/hikarunara.mp4',
+            pic: 'https://moeplayer.b0.upaiyun.com/dplayer/hikarunara.png',
+            thumbnails: 'https://moeplayer.b0.upaiyun.com/dplayer/hikarunara_thumbnails.jpg',
+            type: 'auto'
+        },
+        subtitle: {
+            url: 'https://moeplayer.b0.upaiyun.com/dplayer/hikarunara.vtt',
+            type: 'webvtt',
+            fontSize: '25px',
+            bottom: '10%',
+            color: '#b7daff'
+        },
+        danmaku: {
+            id: '9E2E3368B56CDBB4',
+            api: 'https://api.prprpr.me/dplayer/',
+            token: 'tokendemo',
+            maximum: 3000,
+            user: 'DIYgod',
+            margin: {
+                bottom: '15%'
+            },
+            unlimited: true
+        },
+        contextmenu: [
+            {
+                text: 'custom contextmenu',
+                link: 'https://github.com/MoePlayer/DPlayer'
+            }
+        ]
+    });
+
+    const events = [
+        'abort', 'canplay', 'canplaythrough', 'durationchange', 'emptied', 'ended', 'error',
+        'loadeddata', 'loadedmetadata', 'loadstart', 'mozaudioavailable', 'pause', 'play',
+        'playing', 'ratechange', 'seeked', 'seeking', 'stalled',
+        'volumechange', 'waiting',
+        'screenshot',
+        'thumbnails_show', 'thumbnails_hide',
+        'danmaku_show', 'danmaku_hide', 'danmaku_clear',
+        'danmaku_loaded', 'danmaku_send', 'danmaku_opacity',
+        'contextmenu_show', 'contextmenu_hide',
+        'notice_show', 'notice_hide',
+        'quality_start', 'quality_end',
+        'destroy',
+        'resize',
+        'fullscreen', 'fullscreen_cancel', 'webfullscreen', 'webfullscreen_cancel',
+        'subtitle_show', 'subtitle_hide', 'subtitle_change'
+    ];
+    const eventsEle = document.getElementById('events');
+    for (let i = 0; i < events.length; i++) {
+        dp2.on(events[i], (info) => {
+            eventsEle.innerHTML += '<p>Event: ' + events[i] + '</p>';
+            eventsEle.scrollTop = eventsEle.scrollHeight;
+        });
+    }
+
+    // dp3
+    // window.dp3 = new DPlayer({
+    //     container: document.getElementById('dplayer3'),
+    //     preload: 'none',
+    //     video: {
+    //         quality: [{
+    //             name: 'HD',
+    //             url: 'https://moeplayer.b0.upaiyun.com/dplayer/hls/hikarunara.m3u8',
+    //             type: 'hls'
+    //         }, {
+    //             name: 'SD',
+    //             url: 'https://moeplayer.b0.upaiyun.com/dplayer/hikarunara.mp4',
+    //             type: 'normal'
+    //         }],
+    //         defaultQuality: 0,
+    //         pic: 'https://moeplayer.b0.upaiyun.com/dplayer/hikarunara.png'
+    //     }
+    // });
+
+    // // dp4
+    // window.dp4 = new DPlayer({
+    //     container: document.getElementById('dplayer4'),
+    //     preload: 'none',
+    //     video: {
+    //         url: 'https://moeplayer.b0.upaiyun.com/dplayer/hls/hikarunara.m3u8',
+    //         type: 'hls'
+    //     }
+    // });
+
+    // // dp5
+    // window.dp5 = new DPlayer({
+    //     container: document.getElementById('dplayer5'),
+    //     preload: 'none',
+    //     video: {
+    //         url: 'https://moeplayer.b0.upaiyun.com/dplayer/hikarunara.flv',
+    //         type: 'flv'
+    //     }
+    // });
+
+    // window.dp8 = new DPlayer({
+    //     container: document.getElementById('dplayer8'),
+    //     preload: 'none',
+    //     video: {
+    //         url: 'https://moeplayer.b0.upaiyun.com/dplayer/dash/hikarunara.mpd',
+    //         type: 'dash'
+    //     }
+    // });
+
+    // window.dp6 = new DPlayer({
+    //     container: document.getElementById('dplayer6'),
+    //     preload: 'none',
+    //     live: true,
+    //     danmaku: true,
+    //     apiBackend: {
+    //         read: function (endpoint, callback) {
+    //             console.log('假装 WebSocket 连接成功');
+    //             callback();
+    //         },
+    //         send: function (endpoint, danmakuData, callback) {
+    //             console.log('假装通过 WebSocket 发送数据', danmakuData);
+    //             callback();
+    //         }
+    //     },
+    //     video: {
+    //         url: 'https://moeplayer.b0.upaiyun.com/dplayer/hls/hikarunara.m3u8',
+    //         type: 'hls'
+    //     }
+    // });
+}
+
+function clearPlayers () {
+    for (let i = 0; i < 6; i++) {
+        window['dp' + (i + 1)].pause();
+        document.getElementById('dplayer' + (i + 1)).innerHTML = '';
+    }
+}
+
+function switchDPlayer () {
+    if (dp2.option.danmaku.id !== '5rGf5Y2X55qu6Z2p') {
+        dp2.switchVideo({
+            url: 'https://moeplayer.b0.upaiyun.com/dplayer/flowerdance.mp4',
+            pic: 'https://moeplayer.b0.upaiyun.com/dplayer/flowerdance.jpg',
+            type: 'auto',
+        }, {
+            id: '5rGf5Y2X55qu6Z2p',
+            api: 'https://api.prprpr.me/dplayer/',
+            maximum: 3000,
+            user: 'DIYgod'
+        });
+    } else {
+        dp2.switchVideo({
+            url: 'https://moeplayer.b0.upaiyun.com/dplayer/hikarunara.mp4',
+            pic: 'https://moeplayer.b0.upaiyun.com/dplayer/hikarunara.png',
+            thumbnails: 'https://moeplayer.b0.upaiyun.com/dplayer/hikarunara_thumbnails.jpg',
+            type: 'auto'
+        }, {
+            id: '9E2E3368B56CDBB42',
+            api: 'https://api.prprpr.me/dplayer/',
+            maximum: 3000,
+            user: 'DIYgod'
+        });
+    }
+}

+ 9 - 195
demo/index.html

@@ -5,10 +5,10 @@
     <meta charset="UTF-8">
     <title>DPlayer Demo</title>
     <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-    <link rel="stylesheet" href="https://unpkg.com/github-markdown-css">
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css">
     <link rel="stylesheet" href="demo.css">
-    <script src="https://unpkg.com/flv.js/dist/flv.min.js"></script>
-    <script src="https://unpkg.com/hls.js/dist/hls.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/flv.js/dist/flv.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/hls.js/dist/hls.min.js"></script>
     <script src="https://cdn.jsdelivr.net/npm/dashjs/dist/dash.all.min.js"></script>
     <script src="DPlayer.js"></script>
 </head>
@@ -17,23 +17,6 @@
     <h2 id="quick-start">Quick Start</h2>
     <div class="example">
         <div id="dplayer1"></div>
-        <script>
-            var dp1 = new DPlayer({
-                container: document.getElementById('dplayer1'),
-                video: {
-                    url: 'http://devtest.qiniudn.com/若能绽放光芒.mp4',
-                    pic: 'http://devtest.qiniudn.com/若能绽放光芒.png',
-                    thumbnails: 'http://devtest.qiniudn.com/thumbnails.jpg',
-                },
-                subtitle: {
-                    url: 'http://devtest.qiniudn.com/若能绽放光芒2.vtt'
-                },
-                danmaku: {
-                    id: '9E2E3368B56CDBB4',
-                    api: 'https://api.prprpr.me/dplayer/'
-                }
-            });
-        </script>
     </div>
 
     <h2 id="options">Options</h2>
@@ -48,205 +31,36 @@
         <button class="btn" onclick="dp2.danmaku.show()">danmaku.show</button>
         <div id="dplayer2"></div>
         <div id="events"></div>
-        <script>
-            var dp2 = new DPlayer({
-                container: document.getElementById('dplayer2'),
-                autoplay: false,
-                theme: '#FADFA3',
-                loop: true,
-                screenshot: true,
-                hotkey: true,
-                logo: 'http://devtest.qiniudn.com/DPlayer.png',
-                volume: 0.2,
-                mutex: false,
-                video: {
-                    url: 'http://devtest.qiniudn.com/若能绽放光芒.mp4',
-                    pic: 'http://devtest.qiniudn.com/若能绽放光芒.png',
-                    type: 'auto',
-                    thumbnails: 'http://devtest.qiniudn.com/thumbnails.jpg'
-                },
-                subtitle: {
-                    url: 'http://devtest.qiniudn.com/若能绽放光芒2.vtt',
-                    type: 'webvtt',
-                    fontSize: '25px',
-                    bottom: '10%',
-                    color: '#b7daff'
-                },
-                danmaku: {
-                    id: '9E2E3368B56CDBB4',
-                    api: 'https://api.prprpr.me/dplayer/',
-                    token: 'tokendemo',
-                    maximum: 3000,
-                    user: 'DIYgod',
-                    margin: {
-                        bottom: '15%'
-                    },
-                    unlimited: true
-                },
-                contextmenu: [
-                    {
-                        text: 'custom contextmenu',
-                        link: 'https://github.com/DIYgod/DPlayer'
-                    }
-                ]
-            });
-            function switchDPlayer() {
-                if (dp2.danmaku.options.api.id !== '5rGf5Y2X55qu6Z2p') {
-                    dp2.switchVideo({
-                        url: 'http://devtest.qiniudn.com/若能绽放光芒5.m3u8',
-                        type: 'hls'
-                    },
-                        {
-                            id: '5rGf5Y2X55qu6Z2p',
-                            api: 'https://api.prprpr.me/dplayer/',
-                            maximum: 3000,
-                            user: 'DIYgod'
-                        });
-                }
-                else {
-                    dp2.switchVideo({
-                        url: 'http://devtest.qiniudn.com/若能绽放光芒.mp4',
-                        pic: 'http://devtest.qiniudn.com/若能绽放光芒.png',
-                        type: 'auto',
-                    },
-                        {
-                            id: '9E2E3368B56CDBB4',
-                            api: 'https://api.prprpr.me/dplayer/',
-                            maximum: 3000,
-                            user: 'DIYgod'
-                        });
-                }
-            }
-            var events = [
-                'abort', 'canplay', 'canplaythrough', 'durationchange', 'emptied', 'ended', 'error',
-                'loadeddata', 'loadedmetadata', 'loadstart', 'mozaudioavailable', 'pause', 'play',
-                'playing', 'ratechange', 'seeked', 'seeking', 'stalled',
-                'volumechange', 'waiting',
-                'screenshot',
-                'thumbnails_show', 'thumbnails_hide',
-                'danmaku_show', 'danmaku_hide', 'danmaku_clear',
-                'danmaku_loaded', 'danmaku_send', 'danmaku_opacity',
-                'contextmenu_show', 'contextmenu_hide',
-                'notice_show', 'notice_hide',
-                'quality_start', 'quality_end',
-                'destroy',
-                'resize',
-                'fullscreen', 'fullscreen_cancel', 'webfullscreen', 'webfullscreen_cancel',
-                'subtitle_show', 'subtitle_hide', 'subtitle_change'
-            ];
-            var eventsEle = document.getElementById('events');
-            for (let i = 0; i < events.length; i++) {
-                dp2.on(events[i], (info) => {
-                    eventsEle.innerHTML += '<p>Event: ' + events[i] + '</p>';
-                    eventsEle.scrollTop = eventsEle.scrollHeight;
-                })
-            }
-        </script>
     </div>
 
     <h2 id="quality-switching">Quality switching</h2>
     <div class="example">
         <button class="btn" onclick="dp3.switchQuality(1)">Switch quality</button>
         <div id="dplayer3"></div>
-        <script>
-            var dp3 = new DPlayer({
-                container: document.getElementById('dplayer3'),
-                video: {
-                    quality: [{
-                        name: 'HD',
-                        url: 'http://devtest.qiniudn.com/若能绽放光芒5.m3u8',
-                        type: 'hls'
-                    }, {
-                        name: 'SD',
-                        url: 'http://devtest.qiniudn.com/若能绽放光芒.mp4',
-                        type: 'normal'
-                    }],
-                    defaultQuality: 0,
-                    pic: 'http://devtest.qiniudn.com/若能绽放光芒.png'
-                }
-            });
-        </script>
     </div>
 
     <h2 id="hls-support">HLS support</h2>
     <div class="example">
         <div id="dplayer4"></div>
-        <script>
-            var dp4 = new DPlayer({
-                container: document.getElementById('dplayer4'),
-                video: {
-                    url: 'http://devtest.qiniudn.com/若能绽放光芒5.m3u8',
-                    type: 'hls'
-                }
-            });
-        </script>
     </div>
 
-    <h2 id="flv-support">FLV support</h2>
+    <h2 id="dash-support">MPEG DASH support</h2>
     <div class="example">
-        <div id="dplayer5"></div>
-        <script>
-            var dp5 = new DPlayer({
-                container: document.getElementById('dplayer5'),
-                video: {
-                    url: 'http://devtest.qiniudn.com/【微小微】玖月奇迹-踩踩踩.flv',
-                    type: 'flv'
-                }
-            });
-        </script>
+        <div id="dplayer8"></div>
     </div>
 
-    <h2 id="dash-support">MPEG DASH support</h2>
+    <h2 id="flv-support">FLV support</h2>
     <div class="example">
-        <div id="dplayer10"></div>
-        <script>
-            var dp10 = new DPlayer({
-                container: document.getElementById('dplayer10'),
-                video: {
-                    url: 'http://devtest.qiniudn.com/若能绽放光芒.mpd',
-                    type: 'dash'
-                }
-            });
-        </script>
+        <div id="dplayer5"></div>
     </div>
 
     <h2 id="live">Live</h2>
     <div class="example">
         <button class="btn" onclick="drawDanmaku()">假装收到 WebSocket 弹幕</button>
-        <div id="dplayer11"></div>
-        <script>
-            var dp10 = new DPlayer({
-                container: document.getElementById('dplayer11'),
-                live: true,
-                danmaku: true,
-                apiBackend: {
-                    read: function (endpoint, callback) {
-                        setTimeout(function () {}, 1000);
-                        console.log('假装 WebSocket 连接成功');
-                        callback();
-                    },
-                    send: function (endpoint, danmakuData, callback) {
-                        console.log('假装通过 WebSocket 发送数据', danmakuData);
-                        setTimeout(function () {}, 1000);
-                        callback();
-                    }
-                },
-                video: {
-                    url: 'http://devtest.qiniudn.com/若能绽放光芒5.m3u8',
-                    type: 'hls'
-                }
-            });
-            function drawDanmaku() {
-                dp10.danmaku.draw({
-                    text: '假装收到 WebSocket 弹幕',
-                    color: '#fff',
-                    type: 'right'
-                })
-            }
-        </script>
+        <div id="dplayer6"></div>
     </div>
 
-    <script src="https://unpkg.com/stats.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/stats.js"></script>
     <script src="demo.js"></script>
 </body>
 

+ 14 - 245
src/DPlayer.js

@@ -6,7 +6,6 @@ import i18n from './i18n';
 import Template from './template';
 import SvgCollection from './svg';
 import Danmaku from './danmaku';
-import Thumbnails from './thumbnails';
 import Events from './events';
 import FullScreen from './fullscreen';
 import User from './user';
@@ -71,6 +70,8 @@ class DPlayer {
 
         this.bezel = new Bezel(this.template.bezel);
 
+        this.fullScreen = new FullScreen(this);
+
         this.controller = new Controller(this);
 
         if (this.options.danmaku) {
@@ -121,123 +122,10 @@ class DPlayer {
             this.focus = true;
         }, true);
 
-        // play and pause button
         this.paused = true;
-        this.template.playButton.addEventListener('click', () => {
-            this.toggle();
-        });
-
-        if (!isMobile) {
-            this.template.videoWrap.addEventListener('click', () => {
-                this.toggle();
-            });
-            this.template.controllerMask.addEventListener('click', () => {
-                this.toggle();
-            });
-        }
-        else {
-            this.template.videoWrap.addEventListener('click', () => {
-                this.controller.toggle();
-            });
-            this.template.controllerMask.addEventListener('click', () => {
-                this.controller.toggle();
-            });
-        }
 
         this.time = new Time(this);
 
-        if (this.options.video.thumbnails) {
-            this.initThumbnails();
-        }
-        this.isTimeTipsShow = true;
-        this.mouseHandler = this.mouseHandler(this.template.playedBarWrap, this.template.playedBarTime).bind(this);
-        this.template.playedBarWrap.addEventListener('mousemove', this.mouseHandler);
-        this.template.playedBarWrap.addEventListener('mouseenter', this.mouseHandler);
-        this.template.playedBarWrap.addEventListener('mouseleave', this.mouseHandler);
-
-        let barWidth;
-        const thumbMove = (e) => {
-            let percentage = (e.clientX - utils.getElementViewLeft(this.template.playedBarWrap)) / barWidth;
-            percentage = Math.max(percentage, 0);
-            percentage = Math.min(percentage, 1);
-            this.bar.set('played', percentage, 'width');
-            this.template.ptime.innerHTML = utils.secondToTime(percentage * this.video.duration);
-        };
-
-        const thumbUp = (e) => {
-            document.removeEventListener('mouseup', thumbUp);
-            document.removeEventListener('mousemove', thumbMove);
-            let percentage = (e.clientX - utils.getElementViewLeft(this.template.playedBarWrap)) / barWidth;
-            percentage = Math.max(percentage, 0);
-            percentage = Math.min(percentage, 1);
-            this.bar.set('played', percentage, 'width');
-            this.seek(this.bar.get('played') * this.video.duration);
-            this.time.enable('progress');
-        };
-
-        this.template.playedBarWrap.addEventListener('mousedown', () => {
-            barWidth = this.template.playedBarWrap.clientWidth;
-            this.time.disable('progress');
-            document.addEventListener('mousemove', thumbMove);
-            document.addEventListener('mouseup', thumbUp);
-        });
-
-
-        /**
-         * control volume
-         */
-        const vWidth = 35;
-
-        this.switchVolumeIcon = () => {
-            if (this.volume() >= 0.95) {
-                this.template.volumeIcon.innerHTML = this.icons.get('volume-up');
-            }
-            else if (this.volume() > 0) {
-                this.template.volumeIcon.innerHTML = this.icons.get('volume-down');
-            }
-            else {
-                this.template.volumeIcon.innerHTML = this.icons.get('volume-off');
-            }
-        };
-        const volumeMove = (event) => {
-            const e = event || window.event;
-            const percentage = (e.clientX - utils.getElementViewLeft(this.template.volumeBarWrap) - 5.5) / vWidth;
-            this.volume(percentage);
-        };
-        const volumeUp = () => {
-            document.removeEventListener('mouseup', volumeUp);
-            document.removeEventListener('mousemove', volumeMove);
-            this.template.volumeButton.classList.remove('dplayer-volume-active');
-        };
-
-        this.template.volumeBarWrapWrap.addEventListener('click', (event) => {
-            const e = event || window.event;
-            const percentage = (e.clientX - utils.getElementViewLeft(this.template.volumeBarWrap) - 5.5) / vWidth;
-            this.volume(percentage);
-        });
-        this.template.volumeBarWrapWrap.addEventListener('mousedown', () => {
-            document.addEventListener('mousemove', volumeMove);
-            document.addEventListener('mouseup', volumeUp);
-            this.template.volumeButton.classList.add('dplayer-volume-active');
-        });
-        this.template.volumeIcon.addEventListener('click', () => {
-            if (this.video.muted) {
-                this.video.muted = false;
-                this.switchVolumeIcon();
-                this.bar.set('volume', this.volume(), 'width');
-            }
-            else {
-                this.video.muted = true;
-                this.template.volumeIcon.innerHTML = this.icons.get('volume-off');
-                this.bar.set('volume', 0, 'width');
-            }
-        });
-
-        // set duration time
-        if (this.video.duration !== 1) { // compatibility: Android browsers will output 1 at first
-            this.template.dtime.innerHTML = this.video.duration ? utils.secondToTime(this.video.duration) : '00:00';
-        }
-
         if (!this.danmaku) {
             // autoplay
             if (this.options.autoplay && !isMobile) {
@@ -248,18 +136,6 @@ class DPlayer {
             }
         }
 
-        this.fullScreen = new FullScreen(this);
-
-        // browser full screen
-        this.template.browserFullButton.addEventListener('click', () => {
-            this.fullScreen.toggle('browser');
-        });
-
-        // web full screen
-        this.template.webFullButton.addEventListener('click', () => {
-            this.fullScreen.toggle('web');
-        });
-
         /**
          * hot key
          */
@@ -354,55 +230,6 @@ class DPlayer {
             });
         });
 
-        /**
-         * Switch quality
-         */
-        if (this.options.video.quality) {
-            this.template.qualityList.addEventListener('click', (e) => {
-                if (e.target.classList.contains('dplayer-quality-item')) {
-                    this.switchQuality(e.target.dataset.index);
-                }
-            });
-        }
-
-        /**
-         * Screenshot
-         */
-        if (this.options.screenshot) {
-            this.template.camareButton.addEventListener('click', () => {
-                const canvas = document.createElement("canvas");
-                canvas.width = this.video.videoWidth;
-                canvas.height = this.video.videoHeight;
-                canvas.getContext('2d').drawImage(this.video, 0, 0, canvas.width, canvas.height);
-
-                const dataURL = canvas.toDataURL();
-                this.template.camareButton.href = dataURL;
-                this.template.camareButton.download = "DPlayer.png";
-
-                this.events.trigger('screenshot', dataURL);
-            });
-        }
-
-        /**
-         * Toggle subtitle
-         */
-        if (this.options.subtitle) {
-            this.events.on('subtitle_show', () => {
-                this.template.subtitleButton.dataset.balloon = this.tran('Hide subtitle');
-                this.template.subtitleButtonInner.style.opacity = '';
-                this.user.set('subtitle', 1);
-            });
-            this.events.on('subtitle_hide', () => {
-                this.template.subtitleButton.dataset.balloon = this.tran('Show subtitle');
-                this.template.subtitleButtonInner.style.opacity = '0.4';
-                this.user.set('subtitle', 0);
-            });
-
-            this.template.subtitleButton.addEventListener('click', () => {
-                this.subtitle.toggle();
-            });
-        }
-
         this.initVideo(this.video, this.quality && this.quality.type || this.options.video.type);
 
         index++;
@@ -480,6 +307,18 @@ class DPlayer {
         }
     }
 
+    switchVolumeIcon () {
+        if (this.volume() >= 0.95) {
+            this.template.volumeIcon.innerHTML = this.icons.get('volume-up');
+        }
+        else if (this.volume() > 0) {
+            this.template.volumeIcon.innerHTML = this.icons.get('volume-down');
+        }
+        else {
+            this.template.volumeIcon.innerHTML = this.icons.get('volume-off');
+        }
+    }
+
     /**
      * Set volume
      */
@@ -709,76 +548,6 @@ class DPlayer {
         });
     }
 
-    mouseHandler (pbar, timeTips) {
-        // http://stackoverflow.com/questions/1480133/how-can-i-get-an-objects-absolute-position-on-the-page-in-javascript
-        const cumulativeOffset = (element) => {
-            let top = 0, left = 0;
-            do {
-                top += element.offsetTop || 0;
-                left += element.offsetLeft || 0;
-                element = element.offsetParent;
-            } while (element);
-
-            return {
-                top: top,
-                left: left
-            };
-        };
-
-        return (e) => {
-            if (!this.video.duration) {
-                return;
-            }
-            const { clientX } = e;
-            const px = cumulativeOffset(pbar).left;
-            const tx = clientX - px;
-            if (tx < 0 || tx > pbar.offsetWidth) {
-                return;
-            }
-            const time = this.video.duration * (tx / pbar.offsetWidth);
-            timeTips.style.left = `${(tx - 20)}px`;
-
-            switch (e.type) {
-            case 'mouseenter':
-                this.thumbnails && this.thumbnails.show();
-                break;
-            case 'mousemove':
-                this.thumbnails && this.thumbnails.move(tx);
-                timeTips.innerText = utils.secondToTime(time);
-                this.timeTipsDisplay(true, timeTips);
-                break;
-            case 'mouseleave':
-                this.thumbnails && this.thumbnails.hide();
-                this.timeTipsDisplay(false, timeTips);
-                break;
-            }
-        };
-    }
-
-    timeTipsDisplay (show, timeTips) {
-        if (show) {
-            if (this.isTimeTipsShow) {
-                return;
-            }
-            timeTips.classList.remove('hidden');
-            this.isTimeTipsShow = true;
-        } else {
-            if (!this.isTimeTipsShow) {
-                return;
-            }
-            timeTips.classList.add('hidden');
-            this.isTimeTipsShow = false;
-        }
-    }
-
-    initThumbnails () {
-        this.thumbnails = new Thumbnails(this.template.barPreview, this.template.barWrap.offsetWidth, this.options.video.thumbnails, this.events);
-
-        this.on('loadedmetadata', () => {
-            this.thumbnails.resize(160, 90);
-        });
-    }
-
     notice (text, time = 2000, opacity = 0.8) {
         this.template.notice.innerHTML = text;
         this.template.notice.style.opacity = opacity;

+ 199 - 1
src/controller.js

@@ -1,4 +1,5 @@
-import { isMobile } from './utils';
+import utils, { isMobile } from './utils';
+import Thumbnails from './thumbnails';
 
 class Controller {
     constructor (player) {
@@ -13,6 +14,203 @@ class Controller {
                 this.setAutoHide();
             });
         }
+
+        this.initPlayButton();
+        this.initThumbnails();
+        this.initPlayedBar();
+        this.initFullButton();
+        this.initVolumeButton();
+        this.initQualityButton();
+        this.initScreenshotButton();
+        this.initSubtitleButton();
+    }
+
+    initPlayButton () {
+        this.player.template.playButton.addEventListener('click', () => {
+            this.player.toggle();
+        });
+
+        if (!isMobile) {
+            this.player.template.videoWrap.addEventListener('click', () => {
+                this.player.toggle();
+            });
+            this.player.template.controllerMask.addEventListener('click', () => {
+                this.player.toggle();
+            });
+        }
+        else {
+            this.player.template.videoWrap.addEventListener('click', () => {
+                this.toggle();
+            });
+            this.player.template.controllerMask.addEventListener('click', () => {
+                this.toggle();
+            });
+        }
+    }
+
+    initThumbnails () {
+        if (this.player.options.video.thumbnails) {
+            this.thumbnails = new Thumbnails({
+                container: this.player.template.barPreview,
+                barWidth: this.player.template.barWrap.offsetWidth,
+                url: this.player.options.video.thumbnails,
+                events: this.player.events
+            });
+
+            this.player.on('loadedmetadata', () => {
+                this.thumbnails.resize(160, this.player.video.videoHeight / this.player.video.videoWidth * 160);
+            });
+        }
+    }
+
+    initPlayedBar () {
+        const thumbMove = (e) => {
+            let percentage = (e.clientX - utils.getElementViewLeft(this.player.template.playedBarWrap)) / this.player.template.playedBarWrap.clientWidth;
+            percentage = Math.max(percentage, 0);
+            percentage = Math.min(percentage, 1);
+            this.player.bar.set('played', percentage, 'width');
+            this.player.template.ptime.innerHTML = utils.secondToTime(percentage * this.player.video.duration);
+        };
+
+        const thumbUp = (e) => {
+            document.removeEventListener('mouseup', thumbUp);
+            document.removeEventListener('mousemove', thumbMove);
+            let percentage = (e.clientX - utils.getElementViewLeft(this.player.template.playedBarWrap)) / this.player.template.playedBarWrap.clientWidth;
+            percentage = Math.max(percentage, 0);
+            percentage = Math.min(percentage, 1);
+            this.player.bar.set('played', percentage, 'width');
+            this.player.seek(this.player.bar.get('played') * this.player.video.duration);
+            this.player.time.enable('progress');
+        };
+
+        this.player.template.playedBarWrap.addEventListener('mousedown', () => {
+            this.player.time.disable('progress');
+            document.addEventListener('mousemove', thumbMove);
+            document.addEventListener('mouseup', thumbUp);
+        });
+
+        this.player.template.playedBarWrap.addEventListener('mousemove', (e) => {
+            if (this.player.video.duration) {
+                const px = utils.cumulativeOffset(this.player.template.playedBarWrap).left;
+                const tx = e.clientX - px;
+                if (tx < 0 || tx > this.player.template.playedBarWrap.offsetWidth) {
+                    return;
+                }
+                const time = this.player.video.duration * (tx / this.player.template.playedBarWrap.offsetWidth);
+                this.thumbnails && this.thumbnails.move(tx);
+                this.player.template.playedBarTime.style.left = `${(tx - 20)}px`;
+                this.player.template.playedBarTime.innerText = utils.secondToTime(time);
+                this.player.template.playedBarTime.classList.remove('hidden');
+            }
+        });
+
+        this.player.template.playedBarWrap.addEventListener('mouseenter', () => {
+            if (this.player.video.duration) {
+                this.thumbnails && this.thumbnails.show();
+                this.player.template.playedBarTime.classList.remove('hidden');
+            }
+        });
+
+        this.player.template.playedBarWrap.addEventListener('mouseleave', () => {
+            if (this.player.video.duration) {
+                this.thumbnails && this.thumbnails.hide();
+                this.player.template.playedBarTime.classList.add('hidden');
+            }
+        });
+    }
+
+    initFullButton () {
+        this.player.template.browserFullButton.addEventListener('click', () => {
+            this.player.fullScreen.toggle('browser');
+        });
+
+        this.player.template.webFullButton.addEventListener('click', () => {
+            this.player.fullScreen.toggle('web');
+        });
+    }
+
+    initVolumeButton () {
+        const vWidth = 35;
+
+        const volumeMove = (event) => {
+            const e = event || window.event;
+            const percentage = (e.clientX - utils.getElementViewLeft(this.player.template.volumeBarWrap) - 5.5) / vWidth;
+            this.player.volume(percentage);
+        };
+        const volumeUp = () => {
+            document.removeEventListener('mouseup', volumeUp);
+            document.removeEventListener('mousemove', volumeMove);
+            this.player.template.volumeButton.classList.remove('dplayer-volume-active');
+        };
+
+        this.player.template.volumeBarWrapWrap.addEventListener('click', (event) => {
+            const e = event || window.event;
+            const percentage = (e.clientX - utils.getElementViewLeft(this.player.template.volumeBarWrap) - 5.5) / vWidth;
+            this.player.volume(percentage);
+        });
+        this.player.template.volumeBarWrapWrap.addEventListener('mousedown', () => {
+            document.addEventListener('mousemove', volumeMove);
+            document.addEventListener('mouseup', volumeUp);
+            this.player.template.volumeButton.classList.add('dplayer-volume-active');
+        });
+        this.player.template.volumeIcon.addEventListener('click', () => {
+            if (this.player.video.muted) {
+                this.player.video.muted = false;
+                this.player.switchVolumeIcon();
+                this.player.bar.set('volume', this.player.volume(), 'width');
+            }
+            else {
+                this.player.video.muted = true;
+                this.player.template.volumeIcon.innerHTML = this.player.icons.get('volume-off');
+                this.player.bar.set('volume', 0, 'width');
+            }
+        });
+    }
+
+    initQualityButton () {
+        if (this.player.options.video.quality) {
+            this.player.template.qualityList.addEventListener('click', (e) => {
+                if (e.target.classList.contains('dplayer-quality-item')) {
+                    this.player.switchQuality(e.target.dataset.index);
+                }
+            });
+        }
+    }
+
+    initScreenshotButton () {
+        if (this.player.options.screenshot) {
+            this.player.template.camareButton.addEventListener('click', () => {
+                const canvas = document.createElement("canvas");
+                canvas.width = this.player.video.videoWidth;
+                canvas.height = this.player.video.videoHeight;
+                canvas.getContext('2d').drawImage(this.player.video, 0, 0, canvas.width, canvas.height);
+
+                const dataURL = canvas.toDataURL();
+                this.player.template.camareButton.href = dataURL;
+                this.player.template.camareButton.download = "DPlayer.png";
+
+                this.player.events.trigger('screenshot', dataURL);
+            });
+        }
+    }
+
+    initSubtitleButton () {
+        if (this.player.options.subtitle) {
+            this.player.events.on('subtitle_show', () => {
+                this.player.template.subtitleButton.dataset.balloon = this.player.tran('Hide subtitle');
+                this.player.template.subtitleButtonInner.style.opacity = '';
+                this.player.user.set('subtitle', 1);
+            });
+            this.player.events.on('subtitle_hide', () => {
+                this.player.template.subtitleButton.dataset.balloon = this.player.tran('Show subtitle');
+                this.player.template.subtitleButtonInner.style.opacity = '0.4';
+                this.player.user.set('subtitle', 0);
+            });
+
+            this.player.template.subtitleButton.addEventListener('click', () => {
+                this.player.subtitle.toggle();
+            });
+        }
     }
 
     setAutoHide () {

+ 6 - 6
src/thumbnails.js

@@ -1,9 +1,9 @@
 class Thumbnails {
-    constructor (container, width, url, events) {
-        this.container = container;
-        this.width = width;
-        this.container.style.backgroundImage = `url('${url}')`;
-        this.events = events;
+    constructor (options) {
+        this.container = options.container;
+        this.barWidth = options.barWidth;
+        this.container.style.backgroundImage = `url('${options.url}')`;
+        this.events = options.events;
     }
 
     resize (width, height) {
@@ -18,7 +18,7 @@ class Thumbnails {
     }
 
     move (position) {
-        this.container.style.backgroundPosition = `-${(Math.ceil(position / this.width * 100) - 1) * 160}px 0`;
+        this.container.style.backgroundPosition = `-${(Math.ceil(position / this.barWidth * 100) - 1) * 160}px 0`;
         this.container.style.left = `${(position - this.container.offsetWidth / 2)}px`;
     }
 

+ 14 - 0
src/utils.js

@@ -68,6 +68,20 @@ module.exports = {
         },
 
         get: (key) => localStorage.getItem(key)
+    },
+
+    cumulativeOffset: (element) => {
+        let top = 0, left = 0;
+        do {
+            top += element.offsetTop || 0;
+            left += element.offsetLeft || 0;
+            element = element.offsetParent;
+        } while (element);
+
+        return {
+            top: top,
+            left: left
+        };
     }
 
 };