Easy Picture-in-Picture

動画上部にポップアウト ボタンを表示して、ピクチャー・イン・ピクチャーを簡単に実行できるようにします。

当前为 2019-01-01 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Easy Picture-in-Picture
  3. // @namespace easy-picture-in-picture.user.js
  4. // @version 1.1
  5. // @description 動画上部にポップアウト ボタンを表示して、ピクチャー・イン・ピクチャーを簡単に実行できるようにします。
  6. // @author nafumofu
  7. // @match *://*/*
  8. // @grant GM_addStyle
  9. // @license MIT License
  10. // ==/UserScript==
  11.  
  12. class EasyPictureInPicture {
  13. constructor() {
  14. this.epipButton = this.createButton();
  15. document.body.addEventListener('mousemove', (event) => this.event(event), {passive: true});
  16. document.body.addEventListener('touchstart', (event) => this.event(event), {passive: true});
  17. }
  18. event(event) {
  19. if (!this.eventLocked) {
  20. this.eventLocked = !!setTimeout(() => {
  21. this.eventLocked = false;
  22. }, 50);
  23. var posX = event.screenX || event.changedTouches[0].screenX;
  24. var posY = event.screenY || event.changedTouches[0].screenY;
  25. var nodes = document.elementsFromPoint(posX, posY);
  26. for (let node of nodes) {
  27. if (node.tagName === 'VIDEO') {
  28. this.showButton(node);
  29. break;
  30. }
  31. }
  32. }
  33. }
  34. popout() {
  35. document.pictureInPictureElement ? document.exitPictureInPicture() : this.epipTarget.requestPictureInPicture();
  36. }
  37. showButton(target) {
  38. if (!target.disablePictureInPicture) {
  39. this.epipTarget = target;
  40. var style = this.epipButton.style;
  41. var compStyle = getComputedStyle(this.epipButton);
  42. var rect =this.epipTarget.getBoundingClientRect();
  43. var posY = window.scrollY + rect.top;
  44. var posX = window.scrollX + rect.left + (rect.width / 2 - parseInt(compStyle.width) / 2);
  45. style.setProperty('top', `${posY}px`, 'important');
  46. style.setProperty('left', `${posX}px`, 'important');
  47. style.setProperty('opacity', '1', 'important');
  48. clearTimeout(this.epipTimer);
  49. this.epipTimer = setTimeout(() => {
  50. style.setProperty('opacity', '0', 'important');
  51. }, 3000);
  52. }
  53. }
  54. createButton() {
  55. GM_addStyle(`
  56. #epip-button {
  57. all: unset !important;
  58. opacity: 0 !important;
  59. position: absolute !important;
  60. background: rgba(0,0,0,0.4) !important;
  61. color: #ffffff !important;
  62. font-size: 13px !important;
  63. text-align: center !important;
  64. width: 80px !important;
  65. height: 30px !important;
  66. border: solid 1px rgba(255,255,255,0.4) !important;
  67. border-top: none !important;
  68. border-radius: 0 0 8px 8px !important;
  69. z-index: 2147483647 !important;
  70. transition: opacity 0.3s !important;
  71. }
  72. `);
  73. var button = document.createElement('button');
  74. button.id = 'epip-button';
  75. button.textContent = 'Pop-out';
  76. button.addEventListener('click', () => this.popout());
  77. document.documentElement.append(button);
  78. return button;
  79. }
  80. }
  81.  
  82. setTimeout(() => {
  83. if (document.pictureInPictureEnabled) {
  84. new EasyPictureInPicture();
  85. }
  86. }, 1000);