BVHover

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

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==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);
    });
})();