// ==UserScript== // @name 文学社在看着你👀 // @namespace https://world.xiaomawang.com/w/person/project/all/3267489 // @version 1.9 // @description 在你的浏览器上添加文学社所有女生的Q版形象 // @author 茶铭 // @match *://*/* // @icon https://ddlc.moe/images/favicon.ico // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_getValue // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; const imageUrls = [ "https://ddlc.moe/images/sticker_s.png", "https://ddlc.moe/images/sticker_y.png", "https://ddlc.moe/images/sticker_m.png", "https://ddlc.moe/images/sticker_n.png" ]; const alternativeImageUrls = [ "https://img.qovv.cn/2024/04/20/66230b7098aac.png", "https://img.qovv.cn/2024/04/20/66230b748295a.png", "https://img.qovv.cn/2024/04/20/66230b6924394.png", "https://img.qovv.cn/2024/04/20/66230b6c47129.png" ]; const links = [ "https://chat.monika.love/", "https://wiki.monika.love/index.php?title=%E9%A6%96%E9%A1%B5", "https://disk.monika.love/", "https://forum.monika.love/" ]; const descriptions = [ "DCC chat", "DCC wiki", "莫盘", "心跳文学部中文论坛" ]; const name = [ "纱世里", "优里", "莫妮卡", "夏树" ]; const images = []; const imagePositions = []; let isJumpPaused = false; function toggleImageVisibility(index) { return function() { const img = images[index]; img.style.display = img.style.display === 'none' ? 'block' : 'none'; }; } function createImage(url, link, description, x) { const a = document.createElement('a'); a.href = link; a.title = `前往 ${description}`; const img = document.createElement('img'); img.src = url; img.style.position = 'fixed'; img.style.bottom = '0'; img.style.left = `${x}px`; img.style.zIndex = '9999'; a.appendChild(img); document.body.appendChild(a); return img; } function registerMenuCommands() { for (let i = 0; i < name.length; i++) { GM_registerMenuCommand(`隐藏/显示 ${name[i]}`, toggleImageVisibility(i)); } } registerMenuCommands(); function startJumpAnimation() { if (isJumpPaused) return; const visibleImages = images.filter(img => img.style.display !== 'none'); if (visibleImages.length === 0) return; // 如果没有可见图片,则不执行跳跃动画 const randomIndex = Math.floor(Math.random() * visibleImages.length); jumpAnimation(visibleImages[randomIndex], true); // 只执行大跳事件 const randomInterval = Math.floor(Math.random() * 3000) + 3000; setTimeout(startJumpAnimation, randomInterval); } function jumpAnimation(img, isBigJump) { const jumpHeight = isBigJump ? 100 : 50; // 调整第一次触底反弹的高度 const jumpDuration = isBigJump ? 1000 : 0; // 增加跳跃速度 img.animate([ { transform: 'translateY(0)', }, { transform: `translateY(-${jumpHeight}px)`, offset: 0.3 }, // 触底反弹 { transform: 'translateY(0)', offset: 0.6 }, // 再次落地 { transform: `translateY(-${jumpHeight / 2}px)`, offset: 0.8 }, // 落地前稍微反弹一次 { transform: 'translateY(0)', offset: 1 } ], { duration: jumpDuration, easing: 'cubic-bezier(0.4, 0, 0.2, 1)', iterations: 1 }); } function checkOverlap(x) { for (let i = 0; i < imagePositions.length; i++) { const position = imagePositions[i]; if (Math.abs(x - position) <= 100) { return true; } } return false; } function generateRandomX() { let x = Math.floor(Math.random() * (window.innerWidth - 100)); while (checkOverlap(x)) { x = Math.floor(Math.random() * (window.innerWidth - 100)); } return x; } function startJumpAnimation() { if (isJumpPaused) return; const randomIndex = Math.floor(Math.random() * images.length); jumpAnimation(images[randomIndex], true); // 只执行大跳事件 const randomInterval = Math.floor(Math.random() * 3000) + 3000; setTimeout(startJumpAnimation, randomInterval); } window.addEventListener('load', () => { imageUrls.forEach((url, index) => { const x = generateRandomX(); const img = createImage(url, links[index], descriptions[index], x); images.push(img); imagePositions.push(x); }); startJumpAnimation(); document.addEventListener('keydown', event => { if (event.key === '1' && !isJumpPaused) { images[0].src = alternativeImageUrls[0]; jumpAnimation(images[0], true); isJumpPaused = true; setTimeout(() => { images[0].src = imageUrls[0]; isJumpPaused = false; }, 1000); } if (event.key === '2' && !isJumpPaused) { images[1].src = alternativeImageUrls[1]; jumpAnimation(images[1], true); isJumpPaused = true; setTimeout(() => { images[1].src = imageUrls[1]; isJumpPaused = false; }, 1000); } if (event.key === '3' && !isJumpPaused) { images[2].src = alternativeImageUrls[2]; jumpAnimation(images[2], true); isJumpPaused = true; setTimeout(() => { images[2].src = imageUrls[2]; isJumpPaused = false; }, 1000); } if (event.key === '4' && !isJumpPaused) { images[3].src = alternativeImageUrls[3]; jumpAnimation(images[3], true); isJumpPaused = true; setTimeout(() => { images[3].src = imageUrls[3]; isJumpPaused = false; }, 1000); } if (event.key === 'n' || event.key === 'N') { toggleImagesVisibility(); } }); }); })();