// ==UserScript==
// @name #相亲相爱一嘉人#
// @description 在哔站右下角添加嘉然小姐的live2d模型
// @version 1.0.0
// @namespace https://github.com/journey-ad
// @author journey-ad
// @include /^https:\/\/(www|live|space|t)\.bilibili\.com\/.*$/
// @icon https://www.google.com/s2/favicons?domain=bilibili.com
// @license GPL v2
// @run-at document-end
// @grant none
// ==/UserScript==
(async function () {
'use strict';
if (inIframe()) {
console.log('iframe中不加载');
return false;
}
const 引流 = [
"https://space.bilibili.com/672328094",
"https://www.bilibili.com/video/BV1FZ4y1F7HH",
"https://www.bilibili.com/video/BV1FX4y1g7u8",
"https://www.bilibili.com/video/BV1aK4y1P7Cg",
"https://www.bilibili.com/video/BV17A411V7Uh",
"https://www.bilibili.com/video/BV1JV411b7Pc",
"https://www.bilibili.com/video/BV1AV411v7er",
"https://www.bilibili.com/video/BV1564y1173Q",
"https://www.bilibili.com/video/BV1MX4y1N75X",
"https://www.bilibili.com/video/BV17h411U71w",
"https://www.bilibili.com/video/BV1ry4y1Y71t",
"https://www.bilibili.com/video/BV1Sy4y1n7c4",
"https://www.bilibili.com/video/BV15y4y177uk",
"https://www.bilibili.com/video/BV1PN411X7QW",
"https://www.bilibili.com/video/BV1Dp4y1H7iB",
"https://www.bilibili.com/video/BV1bi4y1P7Eh",
"https://www.bilibili.com/video/BV1vQ4y1Z7C2",
"https://www.bilibili.com/video/BV1oU4y1h7Sc",
]
const CUSTOM_CSS = `#pio-container {
display: block !important;
bottom: -0.3rem;
z-index: 22637261;
transition: transform 0.3s;
cursor: grab;
}
#pio-container:hover {
transform: translateY(-0.3rem);
}
#pio-container:active {
cursor: grabbing;
}
#pio-container .pio-dialog {
right: 10%;
line-height: 1.5;
background: rgba(255, 255, 255, 0.9);
}
#pio {
height: 240px;
}
.pio-action .pio-home {
display: none;
}
.pio-action span {
background: none;
background-size: 100%;
border: 1px solid #fdcf7b;
border: 0;
width: 2em;
height: 2em;
margin-bottom: 0.6em;
}
.pio-action .pio-skin {
background: url("data:image/svg+xml,%3Csvg class='icon' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 512c0 282.774 229.226 512 512 512s512-229.226 512-512S794.774 0 512 0 0 229.226 0 512z' fill='%23FEC43C'/%3E%3Cpath d='M1013.76 408.576C965.632 175.104 759.808 0 512 0 229.376 0 0 229.376 0 512c0 123.904 44.032 236.544 116.736 324.608 87.04 48.128 186.368 74.752 292.864 74.752 301.056 0 550.912-217.088 604.16-502.784z' fill='%23FFD73A'/%3E%3Cpath d='M233.456 460.383a93.759 93.759 0 1 0 187.526 0c0-51.783-41.984-93.76-93.767-93.76s-93.759 41.977-93.759 93.76zm458.39 0c0 51.782 41.976 93.759 93.759 93.759s93.759-41.984 93.759-93.76c0-51.782-41.984-93.758-93.76-93.758-51.782 0-93.758 41.976-93.758 93.759z' fill='%23873A18'/%3E%3Cpath d='M556.41 689.577H410.561c-17.707 0-31.256-13.548-31.256-31.255 0-17.715 13.549-31.256 31.256-31.256h145.85c17.714 0 31.255 13.548 31.255 31.256s-13.549 31.255-31.256 31.255zM320.97 429.127H156.357c-14.588 0-27.089-13.548-27.089-31.256s12.5-31.247 27.097-31.247H320.96c14.58 0 27.089 13.54 27.089 31.247 0 17.715-12.509 31.256-27.097 31.256zm454.215 0H618.92c-17.715 0-31.255-13.548-31.255-31.256s13.548-31.247 31.255-31.247h156.263c17.715 0 31.255 13.54 31.255 31.247 0 17.715-13.548 31.256-31.255 31.256z' fill='%23873A18'/%3E%3Cpath d='M102.4 327.68C46.08 327.68 0 281.6 0 225.28 0 133.12 102.4 0 102.4 0s102.4 133.12 102.4 225.28c0 56.32-46.08 102.4-102.4 102.4z' fill='%2361A3E0'/%3E%3C/svg%3E");
}
.pio-action .pio-info {
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 500 500' xmlns='http://www.w3.org/2000/svg'%3E%3Crect transform='rotate(45.001 238.211 363.575)' x='29.285' y='22.411' width='273.903' height='505.038' rx='70' ry='70' fill='%23dcdcdc'/%3E%3Cpath d='M218.543 249.999l-47.186 47.186c-8.987 8.988-8.987 22.47 0 31.457 8.988 8.988 22.47 8.988 31.457 0L250 281.456l15.728 15.729c17.976 17.976 17.976 46.063 0 64.038l-64.037 64.038c-17.976 17.975-46.063 17.975-64.038 0l-64.038-64.038c-17.975-17.975-17.975-46.062 0-64.038l64.038-64.037c17.975-17.976 46.062-17.976 64.038 0l16.852 16.851z' fill='%23fff'/%3E%3Cpath d='M281.457 249.999l47.186-47.186c8.988-8.987 8.988-22.469 0-31.457-8.987-8.987-22.469-8.987-31.457 0L250 218.542l-15.729-15.729c-17.975-17.975-17.975-46.062 0-64.037l64.038-64.038c17.975-17.975 46.062-17.975 64.038 0l64.037 64.038c17.977 17.975 17.977 46.062 0 64.037l-64.037 64.038c-17.976 17.976-46.063 17.976-64.038 0l-16.852-16.852z' fill='%2361a3e0'/%3E%3C/svg%3E");
}
.pio-action .pio-close {
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 500 500' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M249.999 198.668L352.665 96c14.667-14.666 36.668-14.666 51.335 0 14.666 14.667 14.666 36.668 0 51.334L301.333 250 404 352.668c14.666 14.667 14.666 36.666 0 51.332-14.667 14.667-36.667 14.667-51.334 0L249.999 301.334 147.333 404c-14.668 14.667-36.666 14.667-51.334 0-14.666-14.666-14.666-36.665 0-51.332L198.666 250 95.999 147.334c-14.666-14.666-14.666-36.667 0-51.334 14.668-14.666 36.665-14.666 51.333 0l102.667 102.668z' fill='%23873a18'/%3E%3C/svg%3E");
}
`
// 用到的库
const LIBS = [
'https://cdn.jsdelivr.net/gh/journey-ad/blog-img@94eb7e2/live2d/lib/pio.css',
'https://cdn.jsdelivr.net/npm/greensock@1.20.2/dist/TweenLite.js',
'https://cubism.live2d.com/sdk-web/cubismcore/live2dcubismcore.min.js',
'https://cdn.jsdelivr.net/npm/pixi.js@5.3.6/dist/pixi.min.js',
'https://cdn.jsdelivr.net/npm/pixi-live2d-display@0.3.1/dist/cubism4.min.js',
'https://cdn.jsdelivr.net/gh/journey-ad/blog-img@94eb7e2/live2d/lib/pio_sdk4.js',
'https://cdn.jsdelivr.net/gh/journey-ad/blog-img@94eb7e2/live2d/lib/pio.js'
]
const reqArr = LIBS.map(src => loadSource(src))
// 创建顺序加载队列
const doTask = reqArr.reduce((prev, next) => prev.then(() => next()), Promise.resolve());
// 队列执行完毕后
doTask.then(() => {
// 移除自带看板娘
const haruna = document.getElementById('my-dear-haruna-vm')
haruna && haruna.remove()
// 初始化pio
_pio_initialize_pixi()
// 添加自定义样式
addStyle(CUSTOM_CSS)
加载圣·嘉然()
console.log("all done.")
});
// 初始化设定
const initConfig = {
mode: "fixed",
hidden: true,
content: {
link: 引流[Math.floor(Math.random() * 引流.length)], // 引流链接
referer: "Hi!", // 存在访问来源时的欢迎文本
welcome: ["Hi!"], // 未开启时间问好时的欢迎文本
skin: ["诶,想看看其他团员吗?", "替换后入场文本"], // 0更换模型提示文案 1更换完毕入场文案
custom: [
// 鼠标移上去提示元素
{ "selector": ".most-viewed-panel .most-viewed-item, .live-up-list .live-detail, .card .user-name, .user .name, .post-content .content-full a, .tag-list .content, .title, h2 a[title]", "type": "link" }
],
},
model: [
// 待加载的模型列表
"https://cdn.jsdelivr.net/gh/journey-ad/blog-img/live2d/Diana/Diana.model3.json",
"https://cdn.jsdelivr.net/gh/journey-ad/blog-img/live2d/Ava/Ava.model3.json",
],
tips: true, // 时间问好
onModelLoad: onModelLoad // 模型加载完成回调
}
let pio_reference // pio实例
function 加载圣·嘉然() {
pio_reference = new Paul_Pio(initConfig)
pio_alignment = "right" // 右下角
// Then apply style
pio_refresh_style()
}
// 模型加载完成回调
function onModelLoad(model) {
const canvas = document.getElementById("pio")
const modelNmae = model.internalModel.settings.name
const coreModel = model.internalModel.coreModel
const motionManager = model.internalModel.motionManager
let touchList = [
{
text: "点击展示文本1",
motion: "Idle"
},
{
text: "点击展示文本2",
motion: "Idle"
}
]
// 播放动作
function playAction(action) {
action.text && pio_reference.modules.render(action.text) // 展示文案
action.motion && pio_reference.model.motion(action.motion) // 播放动作
if (action.from && action.to) {
// 指定部件渐入渐出
Object.keys(action.from).forEach(id => {
const hidePartIndex = coreModel._partIds.indexOf(id)
TweenLite.to(coreModel._partOpacities, 0.6, { [hidePartIndex]: action.from[id] });
// coreModel._partOpacities[hidePartIndex] = action.from[id]
})
motionManager.once("motionFinish", (data) => {
Object.keys(action.to).forEach(id => {
const hidePartIndex = coreModel._partIds.indexOf(id)
TweenLite.to(coreModel._partOpacities, 0.6, { [hidePartIndex]: action.to[id] });
// coreModel._partOpacities[hidePartIndex] = action.to[id]
})
})
}
}
canvas.onclick = function () {
// 除闲置动作外不打断
if (motionManager.state.currentGroup !== "Idle") return
// 随机选择并播放动作
const action = pio_reference.modules.rand(touchList)
playAction(action)
}
if (modelNmae === "Diana") {
// 嘉然小姐
// 入场动作及文案
initConfig.content.skin[1] = ["我是吃货担当 嘉然 Diana~", "嘉心糖们 想然然了没有呀~", "有人在吗?"]
playAction({ motion: "Tap抱阿草-左手" })
// 点击动作及文案,不区分区域
touchList = [
{
text: "嘉心糖屁用没有",
motion: "Tap生气 -领结"
},
{
text: "有人急了,但我不说是谁~",
motion: "Tap= = 左蝴蝶结"
},
{
text: "呜呜...呜呜呜....",
motion: "Tap哭 -眼角"
},
{
text: "想然然了没有呀~",
motion: "Tap害羞-中间刘海"
},
{
text: "阿草好软呀~",
motion: "Tap抱阿草-左手"
},
{
text: "不要再戳啦!好痒!",
motion: "Tap摇头- 身体"
},
{
text: "嗷呜~~~",
motion: "Tap耳朵-发卡"
},
{
text: "zzZ。。。",
motion: "Leave"
},
{
text: "哇!好吃的!",
motion: "Tap右头发"
},
]
} else if (modelNmae === "Ava") {
initConfig.content.skin[1] = ["我是<s>拉胯</s>Gamer担当 向晚 AvA~", "怎么推流辣!", "AAAAAAAAAAvvvvAAA 向晚!"]
playAction({
motion: "Tap左眼",
from: {
"Part15": 1
},
to: {
"Part15": 0
}
})
touchList = [
{
text: "水母 水母~ 只是普通的生物",
motion: "Tap右手"
},
{
text: "可爱的鸽子鸽子~我喜欢你~",
motion: "Tap胸口项链",
from: {
"Part12": 1
},
to: {
"Part12": 0
}
},
{
text: "好...好兄弟之间喜欢很正常啦",
motion: "Tap中间刘海",
from: {
"Part12": 1
},
to: {
"Part12": 0
}
},
{
text: "啊啊啊!怎么推流辣",
motion: "Tap右眼",
from: {
"Part16": 1
},
to: {
"Part16": 0
}
},
{
text: "你怎么老摸我,我的身体是不是可有魅力",
motion: "Tap嘴"
},
{
text: "AAAAAAAAAAvvvvAAA 向晚!",
motion: "Tap左眼",
from: {
"Part15": 1
},
to: {
"Part15": 0
}
}
]
// 钻头比较大,宽度*1.2倍,模型位移也要重新计算
canvas.width = model.width * 1.2
model.x = canvas.width - model.width
// 模型问题,手动隐藏指定部件
const hideParts = [
"Part5", // 晕
"neko", // 喵喵拳
"game", // 左手游戏手柄
"Part15", // 墨镜
"Part21", // 右手小臂
"Part22", // 左手垂下
"Part", // 双手抱拳
"Part16", // 惊讶特效
"Part12" // 小心心
]
const hidePartsIndex = hideParts.map(id => coreModel._partIds.indexOf(id))
hidePartsIndex.forEach(idx => {
coreModel._partOpacities[idx] = 0
})
}
}
// 检测是否处于iframe内嵌环境
function inIframe() {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
// 加载js或css,返回函数包裹的promise实例,用于顺序加载队列
function loadSource(src) {
return () => {
return new Promise(function (resolve, reject) {
const TYPE = src.split('.').pop()
let s = null;
let r = false;
if (TYPE === 'js') {
s = document.createElement('script');
s.type = 'text/javascript';
s.src = src;
s.async = true;
} else if (TYPE === 'css') {
s = document.createElement('link');
s.rel = 'stylesheet';
s.type = 'text/css';
s.href = src;
}
s.onerror = function (err) {
reject(err, s);
};
s.onload = s.onreadystatechange = function () {
// console.log(this.readyState); // uncomment this line to see which ready states are called.
if (!r && (!this.readyState || this.readyState == 'complete')) {
r = true;
console.log(src)
resolve();
}
};
const t = document.getElementsByTagName('script')[0];
t.parentElement.insertBefore(s, t);
});
}
}
// 添加css
function addStyle(css) {
if (typeof GM_addStyle != "undefined") {
GM_addStyle(css);
} else if (typeof PRO_addStyle != "undefined") {
PRO_addStyle(css);
} else {
const node = document.createElement("style");
node.type = "text/css";
node.appendChild(document.createTextNode(css));
const heads = document.getElementsByTagName("head");
if (heads.length > 0) {
heads[0].appendChild(node);
} else {
// no head yet, stick it whereever
document.documentElement.appendChild(node);
}
}
}
})();