BVHover

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

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         BVHover
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  B站视频评论区BV信息显示
// @author       KeJun
// @include      *://www.bilibili.com/video/BV*
// @require      https://unpkg.com/@popperjs/core@2
// @require      https://unpkg.com/tippy.js@6
// @require      https://unpkg.com/[email protected]/anime.min.js
// ==/UserScript==
"use strict";
const api = {
  view: "https://api.bilibili.com/x/web-interface/view?bvid=",
  videoshot: "https://api.bilibili.com/x/player/videoshot?bvid=",
};
const log = console.log.bind(console, "[BVHOVER]:");

function throttle(fun, delay) {
  let last, deferTimer;
  return function (args) {
    let that = this;
    let _args = arguments;
    let now = +new Date();
    if (last && now < last + delay) {
      clearTimeout(deferTimer);
      deferTimer = setTimeout(function () {
        last = now;
        fun.apply(that, _args);
      }, delay);
    } else {
      last = now;
      fun.apply(that, _args);
    }
  };
}

// 过滤未添加事件的元素
function getBVATag(comment) {
  return Array.from(comment.querySelectorAll("a")).filter((v) => !v.instance);
}

// 获取数据设置事件等乱七八糟
async function getDataAndSetEvent(ele) {
  const bvid = ele.innerText;
  if (!/^(bv|BV)[a-zA-Z0-9]{10}$/.test(bvid)) return;
  ele.instance = tippy(ele, {
    content: "加载中...",
    interactiveBorder: 5,
    interactive: true,
  });
  try {
    const { data: viewData } = await (await fetch(`${api.view}${bvid}`)).json();
    const { data: videoshotData } = await (
      await fetch(`${api.videoshot}${bvid}`)
    ).json();
    const { title, pic: image } = viewData;
    const [shotImage] = videoshotData.image;
    const boxEl = document.createElement("div");
    const titleEl = document.createElement("marquee");
    const imageEl = document.createElement("div");
    imageEl.style.backgroundImage = `url(${image})`;
    imageEl.style.backgroundSize = "cover";
    imageEl.style.width = boxEl.style.width = "160px";
    imageEl.style.height = "90px";
    titleEl.innerText = title;
    boxEl.style.margin = "5px 0 ";
    boxEl.append(titleEl, imageEl);
    ele.instance.setContent(boxEl);
    let PX = new Array(10)
      .fill(
        new Array(10).fill({}).map((v, i) => ({
          value: `-${i * 160}px`,
          delay: 100,
        }))
      )
      .flat();
    let PY = new Array(10).fill({}).map((v, i) => ({
      value: `-${i * 90}px`,
      delay: 1000,
    }));
    imageEl.addEventListener("mouseover", function () {
      if (!shotImage) return;
      imageEl.style.backgroundSize = "auto";
      imageEl.style.backgroundImage = `url(${shotImage})`;
      imageEl.animation = anime({
        targets: imageEl,
        backgroundPositionX: PX,
        backgroundPositionY: PY,
        loop: true,
        duration: 1,
      });
    });
    imageEl.addEventListener("mouseout", function () {
      if (!imageEl.animation) return;
      imageEl.style.backgroundImage = `url(${image})`;
      imageEl.style.backgroundSize = "cover";
      imageEl.animation.restart();
      imageEl.animation.pause();
    });
  } catch (e) {
    log(e);
    ele.instance.setContent("加载失败!");
  }
}

// 悬停事件
// 本来打算使用事件委托,最后想想感觉还不如这样实现好
const hoverHandle = throttle(function (target) {
  getBVATag(target).forEach((v) => getDataAndSetEvent(v));
}, 1000);

(function () {
  log("脚本启动");
  document
    .querySelector("#comment")
    .addEventListener("mouseover", async ({ target }) => {
      hoverHandle(target);
    });
  document
    .querySelector("#v_desc")
    .addEventListener("mouseover", async ({ target }) => {
      hoverHandle(target);
    });
})();