瀏覽代碼

fix: 销毁播放器时移除绑定的事件,避免内存泄漏;video标签contextmenu原生事件阻止有漏掉的情况;

xiaozhengdong 4 年之前
父節點
當前提交
5be01ad19f
共有 9 個文件被更改,包括 132 次插入112 次删除
  1. 6 6
      docs/guide.md
  2. 7 7
      docs/zh/guide.md
  3. 10 5
      src/js/contextmenu.js
  4. 9 12
      src/js/controller.js
  5. 1 7
      src/js/danmaku.js
  6. 20 8
      src/js/fullscreen.js
  7. 65 51
      src/js/hotkey.js
  8. 13 15
      src/js/player.js
  9. 1 1
      src/js/timer.js

+ 6 - 6
docs/guide.md

@@ -709,7 +709,7 @@ DPlayer can work with any MSE library via `customType` option.
 
 ```js
 var type = 'normal';
-if(Hls.isSupported() && Hls.WEBRTC_SUPPORT) {
+if (Hls.isSupported() && Hls.WEBRTC_SUPPORT) {
     type = 'customHls';
 }
 const dp = new DPlayer({
@@ -718,19 +718,19 @@ const dp = new DPlayer({
         url: 'demo.m3u8',
         type: type,
         customType: {
-            'customHls': function (video, player) {
+            customHls: function (video, player) {
                 const hls = new Hls({
                     debug: false,
                     // Other hlsjsConfig options provided by hls.js
                     p2pConfig: {
-                        live: false,        
+                        live: false,
                         // Other p2pConfig options provided by CDNBye http://www.cdnbye.com/en/
-                    }
+                    },
                 });
                 hls.loadSource(video.src);
                 hls.attachMedia(video);
-            }
-        }
+            },
+        },
     },
 });
 ```

+ 7 - 7
docs/zh/guide.md

@@ -683,7 +683,7 @@ const dp = new DPlayer({
 
 ### 配合其他 MSE 库使用
 
-DPlayer 可以通过 `customType` 参数与任何 MSE 库一起使用,例如支持P2P插件:
+DPlayer 可以通过 `customType` 参数与任何 MSE 库一起使用,例如支持 P2P 插件:
 
 ```html
 <div id="dplayer"></div>
@@ -693,7 +693,7 @@ DPlayer 可以通过 `customType` 参数与任何 MSE 库一起使用,例如
 
 ```js
 var type = 'normal';
-if(Hls.isSupported() && Hls.WEBRTC_SUPPORT) {
+if (Hls.isSupported() && Hls.WEBRTC_SUPPORT) {
     type = 'customHls';
 }
 const dp = new DPlayer({
@@ -702,19 +702,19 @@ const dp = new DPlayer({
         url: 'demo.m3u8',
         type: type,
         customType: {
-            'customHls': function (video, player) {
+            customHls: function (video, player) {
                 const hls = new Hls({
                     debug: false,
                     // Other hlsjsConfig options provided by hls.js
                     p2pConfig: {
-                        live: false,        // 如果是直播设为true
+                        live: false, // 如果是直播设为true
                         // Other p2pConfig options provided by CDNBye http://www.cdnbye.com/cn/
-                    }
+                    },
                 });
                 hls.loadSource(video.src);
                 hls.attachMedia(video);
-            }
-        }
+            },
+        },
     },
 });
 ```

+ 10 - 5
src/js/contextmenu.js

@@ -12,22 +12,23 @@ class ContextMenu {
             }
         });
 
-        this.player.container.addEventListener('contextmenu', (e) => {
+        this.contextmenuHandler = (e) => {
+            const event = e || window.event;
+            event.preventDefault();
+
             if (this.shown) {
                 this.hide();
                 return;
             }
 
-            const event = e || window.event;
-            event.preventDefault();
-
             const clientRect = this.player.container.getBoundingClientRect();
             this.show(event.clientX - clientRect.left, event.clientY - clientRect.top);
 
             this.player.template.mask.addEventListener('click', () => {
                 this.hide();
             });
-        });
+        };
+        this.player.container.addEventListener('contextmenu', this.contextmenuHandler);
     }
 
     show(x, y) {
@@ -62,6 +63,10 @@ class ContextMenu {
         this.shown = false;
         this.player.events.trigger('contextmenu_hide');
     }
+
+    destroy() {
+        this.player.container.removeEventListener('contextmenu', this.contextmenuHandler);
+    }
 }
 
 export default ContextMenu;

+ 9 - 12
src/js/controller.js

@@ -8,18 +8,11 @@ class Controller {
 
         this.autoHideTimer = 0;
         if (!utils.isMobile) {
-            this.player.container.addEventListener('mousemove', () => {
-                this.setAutoHide();
-            });
-            this.player.container.addEventListener('click', () => {
-                this.setAutoHide();
-            });
-            this.player.on('play', () => {
-                this.setAutoHide();
-            });
-            this.player.on('pause', () => {
-                this.setAutoHide();
-            });
+            this.setAutoHideHandler = this.setAutoHide.bind(this);
+            this.player.container.addEventListener('mousemove', this.setAutoHideHandler);
+            this.player.container.addEventListener('click', this.setAutoHideHandler);
+            this.player.on('play', this.setAutoHideHandler);
+            this.player.on('pause', this.setAutoHideHandler);
         }
 
         this.initPlayButton();
@@ -331,6 +324,10 @@ class Controller {
     }
 
     destroy() {
+        if (!utils.isMobile) {
+            this.player.container.removeEventListener('mousemove', this.setAutoHideHandler);
+            this.player.container.removeEventListener('click', this.setAutoHideHandler);
+        }
         clearTimeout(this.autoHideTimer);
     }
 }

+ 1 - 7
src/js/danmaku.js

@@ -307,13 +307,7 @@ class Danmaku {
     }
 
     htmlEncode(str) {
-        return str
-            .replace(/&/g, '&amp;')
-            .replace(/</g, '&lt;')
-            .replace(/>/g, '&gt;')
-            .replace(/"/g, '&quot;')
-            .replace(/'/g, '&#x27;')
-            .replace(/\//g, '&#x2f;');
+        return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g, '&#x2f;');
     }
 
     resize() {

+ 20 - 8
src/js/fullscreen.js

@@ -12,7 +12,7 @@ class FullScreen {
             utils.setScrollPosition(this.lastScrollPosition);
         });
 
-        const fullscreenchange = () => {
+        this.fullscreenchange = () => {
             this.player.resize();
             if (this.isFullScreen('browser')) {
                 this.player.events.trigger('fullscreen');
@@ -21,7 +21,7 @@ class FullScreen {
                 this.player.events.trigger('fullscreen_cancel');
             }
         };
-        const docfullscreenchange = () => {
+        this.docfullscreenchange = () => {
             const fullEle = document.fullscreenElement || document.mozFullScreenElement || document.msFullscreenElement;
             if (fullEle && fullEle !== this.player.container) {
                 return;
@@ -35,13 +35,13 @@ class FullScreen {
             }
         };
         if (/Firefox/.test(navigator.userAgent)) {
-            document.addEventListener('mozfullscreenchange', docfullscreenchange);
-            document.addEventListener('fullscreenchange', docfullscreenchange);
+            document.addEventListener('mozfullscreenchange', this.docfullscreenchange);
+            document.addEventListener('fullscreenchange', this.docfullscreenchange);
         } else {
-            this.player.container.addEventListener('fullscreenchange', fullscreenchange);
-            this.player.container.addEventListener('webkitfullscreenchange', fullscreenchange);
-            document.addEventListener('msfullscreenchange', docfullscreenchange);
-            document.addEventListener('MSFullscreenChange', docfullscreenchange);
+            this.player.container.addEventListener('fullscreenchange', this.fullscreenchange);
+            this.player.container.addEventListener('webkitfullscreenchange', this.fullscreenchange);
+            document.addEventListener('msfullscreenchange', this.docfullscreenchange);
+            document.addEventListener('MSFullscreenChange', this.docfullscreenchange);
         }
     }
 
@@ -122,6 +122,18 @@ class FullScreen {
             this.request(type);
         }
     }
+
+    destroy() {
+        if (/Firefox/.test(navigator.userAgent)) {
+            document.removeEventListener('mozfullscreenchange', this.docfullscreenchange);
+            document.removeEventListener('fullscreenchange', this.docfullscreenchange);
+        } else {
+            this.player.container.removeEventListener('fullscreenchange', this.fullscreenchange);
+            this.player.container.removeEventListener('webkitfullscreenchange', this.fullscreenchange);
+            document.removeEventListener('msfullscreenchange', this.docfullscreenchange);
+            document.removeEventListener('MSFullscreenChange', this.docfullscreenchange);
+        }
+    }
 }
 
 export default FullScreen;

+ 65 - 51
src/js/hotkey.js

@@ -1,60 +1,74 @@
 class HotKey {
     constructor(player) {
-        if (player.options.hotkey) {
-            document.addEventListener('keydown', (e) => {
-                if (player.focus) {
-                    const tag = document.activeElement.tagName.toUpperCase();
-                    const editable = document.activeElement.getAttribute('contenteditable');
-                    if (tag !== 'INPUT' && tag !== 'TEXTAREA' && editable !== '' && editable !== 'true') {
-                        const event = e || window.event;
-                        let percentage;
-                        switch (event.keyCode) {
-                            case 32:
-                                event.preventDefault();
-                                player.toggle();
-                                break;
-                            case 37:
-                                event.preventDefault();
-                                if (player.options.live) {
-                                    break;
-                                }
-                                player.seek(player.video.currentTime - 5);
-                                player.controller.setAutoHide();
-                                break;
-                            case 39:
-                                event.preventDefault();
-                                if (player.options.live) {
-                                    break;
-                                }
-                                player.seek(player.video.currentTime + 5);
-                                player.controller.setAutoHide();
-                                break;
-                            case 38:
-                                event.preventDefault();
-                                percentage = player.volume() + 0.1;
-                                player.volume(percentage);
-                                break;
-                            case 40:
-                                event.preventDefault();
-                                percentage = player.volume() - 0.1;
-                                player.volume(percentage);
-                                break;
+        this.player = player;
+        this.doHotKeyHandler = this.doHotKey.bind(this);
+        this.cancelFullScreenHandler = this.cancelFullScreen.bind(this);
+        if (this.player.options.hotkey) {
+            document.addEventListener('keydown', this.doHotKeyHandler);
+        }
+
+        document.addEventListener('keydown', this.cancelFullScreenHandler);
+    }
+
+    doHotKey(e) {
+        if (this.player.focus) {
+            const tag = document.activeElement.tagName.toUpperCase();
+            const editable = document.activeElement.getAttribute('contenteditable');
+            if (tag !== 'INPUT' && tag !== 'TEXTAREA' && editable !== '' && editable !== 'true') {
+                const event = e || window.event;
+                let percentage;
+                switch (event.keyCode) {
+                    case 32:
+                        event.preventDefault();
+                        this.player.toggle();
+                        break;
+                    case 37:
+                        event.preventDefault();
+                        if (this.player.options.live) {
+                            break;
                         }
-                    }
+                        this.player.seek(this.player.video.currentTime - 5);
+                        this.player.controller.setAutoHide();
+                        break;
+                    case 39:
+                        event.preventDefault();
+                        if (this.player.options.live) {
+                            break;
+                        }
+                        this.player.seek(this.player.video.currentTime + 5);
+                        this.player.controller.setAutoHide();
+                        break;
+                    case 38:
+                        event.preventDefault();
+                        percentage = this.player.volume() + 0.1;
+                        this.player.volume(percentage);
+                        break;
+                    case 40:
+                        event.preventDefault();
+                        percentage = this.player.volume() - 0.1;
+                        this.player.volume(percentage);
+                        break;
                 }
-            });
+            }
         }
+    }
 
-        document.addEventListener('keydown', (e) => {
-            const event = e || window.event;
-            switch (event.keyCode) {
-                case 27:
-                    if (player.fullScreen.isFullScreen('web')) {
-                        player.fullScreen.cancel('web');
-                    }
-                    break;
-            }
-        });
+    cancelFullScreen(e) {
+        const event = e || window.event;
+        switch (event.keyCode) {
+            case 27:
+                if (this.player.fullScreen.isFullScreen('web')) {
+                    this.player.fullScreen.cancel('web');
+                }
+                break;
+        }
+    }
+
+    destroy() {
+        if (this.player.options.hotkey) {
+            document.removeEventListener('keydown', this.doHotKeyHandler);
+        }
+        document.removeEventListener('keydown', this.cancelFullScreenHandler);
     }
 }
 

+ 13 - 15
src/js/player.js

@@ -116,21 +116,14 @@ class DPlayer {
 
         this.setting = new Setting(this);
         this.plugins = {};
-
-        document.addEventListener(
-            'click',
-            () => {
-                this.focus = false;
-            },
-            true
-        );
-        this.container.addEventListener(
-            'click',
-            () => {
-                this.focus = true;
-            },
-            true
-        );
+        this.docClickFun = () => {
+            this.focus = false;
+        };
+        this.containerClickFun = () => {
+            this.focus = true;
+        };
+        document.addEventListener('click', this.docClickFun, true);
+        this.container.addEventListener('click', this.containerClickFun, true);
 
         this.paused = true;
 
@@ -608,6 +601,11 @@ class DPlayer {
     destroy() {
         instances.splice(instances.indexOf(this), 1);
         this.pause();
+        document.removeEventListener('click', this.docClickFun, true);
+        this.container.removeEventListener('click', this.containerClickFun, true);
+        this.fullScreen.destroy();
+        this.hotkey.destroy();
+        this.contextmenu.destroy();
         this.controller.destroy();
         this.timer.destroy();
         this.video.src = '';

+ 1 - 1
src/js/timer.js

@@ -8,7 +8,7 @@ class Timer {
             window.mozRequestAnimationFrame ||
             window.oRequestAnimationFrame ||
             window.msRequestAnimationFrame ||
-            function(callback) {
+            function (callback) {
                 window.setTimeout(callback, 1000 / 60);
             })();