BVHover

B站视频评论区BV信息显示

  1. // ==UserScript==
  2. // @name BVHover
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2
  5. // @description B站视频评论区BV信息显示
  6. // @author KeJun
  7. // @include *://www.bilibili.com/video/BV*
  8. // @require https://unpkg.com/@popperjs/core@2
  9. // @require https://unpkg.com/tippy.js@6
  10. // @require https://unpkg.com/animejs@2.2.0/anime.min.js
  11. // ==/UserScript==
  12. "use strict";
  13. const api = {
  14. view: "https://api.bilibili.com/x/web-interface/view?bvid=",
  15. videoshot: "https://api.bilibili.com/x/player/videoshot?bvid=",
  16. };
  17. const log = console.log.bind(console, "[BVHOVER]:");
  18.  
  19. function throttle(fun, delay) {
  20. let last, deferTimer;
  21. return function (args) {
  22. let that = this;
  23. let _args = arguments;
  24. let now = +new Date();
  25. if (last && now < last + delay) {
  26. clearTimeout(deferTimer);
  27. deferTimer = setTimeout(function () {
  28. last = now;
  29. fun.apply(that, _args);
  30. }, delay);
  31. } else {
  32. last = now;
  33. fun.apply(that, _args);
  34. }
  35. };
  36. }
  37.  
  38. // 过滤未添加事件的元素
  39. function getBVATag(comment) {
  40. return Array.from(comment.querySelectorAll("a")).filter((v) => !v.instance);
  41. }
  42.  
  43. // 获取数据设置事件等乱七八糟
  44. async function getDataAndSetEvent(ele) {
  45. const bvid = ele.innerText;
  46. if (!/^(bv|BV)[a-zA-Z0-9]{10}$/.test(bvid)) return;
  47. ele.instance = tippy(ele, {
  48. content: "加载中...",
  49. interactiveBorder: 5,
  50. interactive: true,
  51. });
  52. try {
  53. const { data: viewData } = await (await fetch(`${api.view}${bvid}`)).json();
  54. const { data: videoshotData } = await (
  55. await fetch(`${api.videoshot}${bvid}`)
  56. ).json();
  57. const { title, pic: image } = viewData;
  58. const [shotImage] = videoshotData.image;
  59. const boxEl = document.createElement("div");
  60. const titleEl = document.createElement("marquee");
  61. const imageEl = document.createElement("div");
  62. imageEl.style.backgroundImage = `url(${image})`;
  63. imageEl.style.backgroundSize = "cover";
  64. imageEl.style.width = boxEl.style.width = "160px";
  65. imageEl.style.height = "90px";
  66. titleEl.innerText = title;
  67. boxEl.style.margin = "5px 0 ";
  68. boxEl.append(titleEl, imageEl);
  69. ele.instance.setContent(boxEl);
  70. let PX = new Array(10)
  71. .fill(
  72. new Array(10).fill({}).map((v, i) => ({
  73. value: `-${i * 160}px`,
  74. delay: 100,
  75. }))
  76. )
  77. .flat();
  78. let PY = new Array(10).fill({}).map((v, i) => ({
  79. value: `-${i * 90}px`,
  80. delay: 1000,
  81. }));
  82. imageEl.addEventListener("mouseover", function () {
  83. if (!shotImage) return;
  84. imageEl.style.backgroundSize = "auto";
  85. imageEl.style.backgroundImage = `url(${shotImage})`;
  86. imageEl.animation = anime({
  87. targets: imageEl,
  88. backgroundPositionX: PX,
  89. backgroundPositionY: PY,
  90. loop: true,
  91. duration: 1,
  92. });
  93. });
  94. imageEl.addEventListener("mouseout", function () {
  95. if (!imageEl.animation) return;
  96. imageEl.style.backgroundImage = `url(${image})`;
  97. imageEl.style.backgroundSize = "cover";
  98. imageEl.animation.restart();
  99. imageEl.animation.pause();
  100. });
  101. } catch (e) {
  102. log(e);
  103. ele.instance.setContent("加载失败!");
  104. }
  105. }
  106.  
  107. // 悬停事件
  108. // 本来打算使用事件委托,最后想想感觉还不如这样实现好
  109. const hoverHandle = throttle(function (target) {
  110. getBVATag(target).forEach((v) => getDataAndSetEvent(v));
  111. }, 1000);
  112.  
  113. (function () {
  114. log("脚本启动");
  115. document
  116. .querySelector("#comment")
  117. .addEventListener("mouseover", async ({ target }) => {
  118. hoverHandle(target);
  119. });
  120. document
  121. .querySelector("#v_desc")
  122. .addEventListener("mouseover", async ({ target }) => {
  123. hoverHandle(target);
  124. });
  125. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址