// ==UserScript==
// @name SilentlyJavaGuide
// @namespace http://tampermonkey.net/
// @version 1.4
// @description 在JavaGuide网站添加隐藏/显示侧边栏和导航栏的控制按钮,以及面试内容模糊控制
// @author NikoAoi
// @match https://javaguide.cn/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
// 创建样式
const style = document.createElement('style');
style.textContent = `
#floating-eye-container {
position: fixed;
bottom: 150px;
right: 15px;
width: 48px;
height: 48px;
z-index: 9999;
cursor: pointer;
background: white;
border: 1px solid #e0e0e0;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2));
transition: filter 0.3s ease;
}
#floating-eye-container:hover {
filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3));
}
#floating-eye {
width: 32px;
height: 32px;
filter: saturate(1.2);
}
#eye-iris {
transition: transform 0.3s ease;
fill: url(#iris-gradient);
}
@keyframes moveIris {
0%, 100% { transform: translate(0, 0); }
20% { transform: translate(2px, 0); }
40% { transform: translate(-2px, 0); }
60% { transform: translate(0, -2px); }
80% { transform: translate(0, 2px); }
}
@keyframes morphOutline {
0%, 100% { d: path('M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z'); }
20% { d: path('M1 12s4-8 11-8 11 8 11 8-4 7.8-11 7.8-11-7.8-11-7.8z'); }
40% { d: path('M1 12s4-8.2 11-8.2 11 8.2 11 8.2-4 7.8-11 7.8-11-7.8-11-7.8z'); }
60% { d: path('M1 12s4-7.8 11-7.8 11 7.8 11 7.8-4 8.2-11 8.2-11-8.2-11-8.2z'); }
80% { d: path('M1 12s4-8 11-8 11 8 11 8-4 8.2-11 8.2-11-8.2-11-8.2z'); }
}
@keyframes blink {
0%, 100% { d: path('M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z'); }
45%, 55% { d: path('M3 14 Q12 18 21 14 M2 12 L4 13 M20 12 L22 13'); }
}
.iris-moving {
animation: moveIris 4s ease-in-out infinite;
}
.outline-moving {
animation: morphOutline 4s ease-in-out infinite;
}
.eye-blinking {
animation: blink 0.2s ease-in-out;
}
.blur-text {
filter: blur(10px);
transition: filter 0.3s ease;
}
/* 提示框样式 */
#star-tooltip {
position: fixed;
bottom: 210px;
right: 15px;
background: white;
padding: 12px;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
font-size: 14px;
display: none;
width: 150px;
z-index: 9998;
animation: fadeIn 0.3s ease;
}
#star-tooltip::after {
content: '';
position: absolute;
bottom: -8px;
right: 20px;
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 8px solid white;
}
.star-icon {
display: inline-block;
width: 16px;
height: 16px;
stroke: #f1c40f;
stroke-width: 2;
fill: none;
cursor: pointer;
vertical-align: middle;
transition: fill 0.3s ease;
}
.star-icon:hover {
fill: #f1c40f;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.close-button {
position: absolute;
top: 4px;
right: 4px;
width: 10px;
height: 10px;
cursor: pointer;
opacity: 0.6;
transition: opacity 0.3s ease;
}
.close-button:hover {
opacity: 1;
}
.close-button::before,
.close-button::after {
content: '';
position: absolute;
top: 50%;
left: 0;
width: 100%;
height: 1px;
background-color: #666;
transform-origin: center;
}
.close-button::before {
transform: rotate(45deg);
}
.close-button::after {
transform: rotate(-45deg);
}
`;
document.head.appendChild(style);
// 创建容器
const container = document.createElement('div');
container.setAttribute('id', 'floating-eye-container');
// 创建SVG图标
const eyeIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
eyeIcon.setAttribute('id', 'floating-eye');
eyeIcon.setAttribute('viewBox', '0 0 24 24');
eyeIcon.setAttribute('fill', 'none');
eyeIcon.setAttribute('stroke', 'currentColor');
eyeIcon.setAttribute('stroke-width', '0.3');
// 添加渐变定义
const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
const gradient = document.createElementNS('http://www.w3.org/2000/svg', 'radialGradient');
gradient.setAttribute('id', 'iris-gradient');
gradient.setAttribute('cx', '50%');
gradient.setAttribute('cy', '50%');
gradient.setAttribute('r', '50%');
const stops = [
{ offset: '0%', color: '#644c95' }, // 深紫色中心
{ offset: '30%', color: '#4b6cb7' }, // 蓝紫色过渡
{ offset: '70%', color: '#354a77' }, // 深蓝色边缘
{ offset: '100%', color: '#1a1f3c' } // 深色外圈
];
stops.forEach(stop => {
const stopEl = document.createElementNS('http://www.w3.org/2000/svg', 'stop');
stopEl.setAttribute('offset', stop.offset);
stopEl.setAttribute('stop-color', stop.color);
gradient.appendChild(stopEl);
});
defs.appendChild(gradient);
eyeIcon.appendChild(defs);
// 创建眼睛部件
const eyeOutline = document.createElementNS('http://www.w3.org/2000/svg', 'path');
eyeOutline.setAttribute('d', 'M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z');
eyeOutline.classList.add('outline-moving');
const eyeIris = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
eyeIris.setAttribute('id', 'eye-iris');
eyeIris.setAttribute('cx', '12');
eyeIris.setAttribute('cy', '12');
eyeIris.setAttribute('r', '3');
eyeIris.classList.add('iris-moving');
eyeIcon.appendChild(eyeOutline);
eyeIcon.appendChild(eyeIris);
container.appendChild(eyeIcon);
// 创建提示框
const tooltip = document.createElement('div');
tooltip.setAttribute('id', 'star-tooltip');
tooltip.innerHTML = `
<div class="close-button"></div>
喜欢这个功能吗?欢迎去 GitHub 给个 Star
<svg class="star-icon" viewBox="0 0 24 24">
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
</svg>
`;
// 添加关闭按钮点击事件
const closeButton = tooltip.querySelector('.close-button');
closeButton.addEventListener('click', (e) => {
e.stopPropagation(); // 防止事件冒泡
tooltip.style.display = 'none';
});
// 添加 Star 图标的点击事件
const starIcon = tooltip.querySelector('.star-icon');
starIcon.addEventListener('click', () => {
window.open('https://github.com/NikoAoi/SilentlyJavaGuide', '_blank');
});
document.body.appendChild(tooltip);
// 一分钟后显示提示框
setTimeout(() => {
tooltip.style.display = 'block';
// 10秒后自动隐藏
setTimeout(() => {
tooltip.style.display = 'none';
}, 60000); // 60秒后隐藏提示框
}, 600000); // 10分钟后显示提示框
// 面试内容模糊功能
const blurWords = ['JavaGuide', '面试', '简历', '面经'];
function wrapTextWithSpan(textContent) {
let result = textContent;
for (const word of blurWords) {
const regex = new RegExp(`(${word})`, 'g');
result = result.replace(regex, '<span class="blur-text">$1</span>');
}
return result;
}
function processNode(node) {
if (node.nodeType === Node.TEXT_NODE) {
const parent = node.parentNode;
if (parent && parent.nodeName !== 'SCRIPT' && parent.nodeName !== 'STYLE') {
const newHtml = wrapTextWithSpan(node.textContent);
if (newHtml !== node.textContent) {
const span = document.createElement('span');
span.innerHTML = newHtml;
parent.replaceChild(span, node);
}
}
} else if (node.nodeType === Node.ELEMENT_NODE) {
if (node.childNodes.length === 0 && node.textContent) {
node.innerHTML = wrapTextWithSpan(node.textContent);
} else {
Array.from(node.childNodes).forEach(processNode);
}
}
}
function unwrapBlurredText(element) {
const blurredSpans = element.querySelectorAll('.blur-text');
blurredSpans.forEach(span => {
const textNode = document.createTextNode(span.textContent);
span.parentNode.replaceChild(textNode, span);
});
}
// 状态管理
let isEyeClosed = false;
// 添加一个变量来追踪眨眼动画的 timeout
let blinkTimeout = null;
// 修改眨眼控制函数
const blinkEye = () => {
if (!isEyeClosed) {
// 移除现有的动画类
eyeOutline.classList.remove('outline-moving');
eyeIris.classList.remove('iris-moving');
eyeIris.style.visibility = 'hidden';
// 添加眨眼动画
eyeOutline.classList.add('eye-blinking');
// 清除之前的 timeout
if (blinkTimeout) {
clearTimeout(blinkTimeout);
}
// 设置新的 timeout
blinkTimeout = setTimeout(() => {
eyeOutline.classList.remove('eye-blinking');
if (!isEyeClosed) { // 再次检查状态
eyeIris.style.visibility = 'visible';
// 如果不是悬停状态,恢复原来的动画
if (!container.matches(':hover')) {
eyeOutline.classList.add('outline-moving');
eyeIris.classList.add('iris-moving');
}
}
}, 100);
}
};
// 设置眨眼定时器
const startBlinkInterval = () => {
// 每5秒眨眼两次
setInterval(() => {
blinkEye();
// 100ms后进行第二次眨眼
setTimeout(blinkEye, 400);
}, 5000);
};
// 启动眨眼定时器
startBlinkInterval();
// 修改点击事件处理器
container.addEventListener('click', () => {
// 获得导航栏和侧边栏元素
const sidebar = document.getElementById('sidebar');
const navbar = document.getElementById('navbar');
// 清除正在进行的眨眼动画 timeout
if (blinkTimeout) {
clearTimeout(blinkTimeout);
blinkTimeout = null;
}
isEyeClosed = !isEyeClosed;
eyeOutline.classList.remove('eye-blinking');
if (isEyeClosed) {
// 闭眼状态:下弧线 + 睫毛
eyeOutline.setAttribute('d', 'M3 14 Q12 18 21 14 M2 12 L4 13 M20 12 L22 13');
eyeIris.style.visibility = 'hidden';
eyeOutline.classList.remove('outline-moving');
eyeIris.classList.remove('iris-moving');
if (sidebar) {
// 隐藏侧边栏
sidebar.style.display = 'none';
}
if (navbar) {
// 隐藏导航栏
navbar.style.display = 'none';
}
// 模糊面试内容
processNode(document.body);
} else {
// 睁眼状态
eyeOutline.setAttribute('d', 'M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z');
eyeIris.style.visibility = 'visible';
if (!container.matches(':hover')) {
eyeOutline.classList.add('outline-moving');
eyeIris.classList.add('iris-moving');
}
if (sidebar) {
// 显示侧边栏
sidebar.style.display = '';
}
if (navbar) {
// 显示导航栏
navbar.style.display = '';
}
// 取消模糊面试内容
unwrapBlurredText(document.body);
}
});
// 悬停控制动画
container.addEventListener('mouseenter', () => {
if (!isEyeClosed) {
eyeIris.classList.remove('iris-moving');
eyeOutline.classList.remove('outline-moving');
}
});
container.addEventListener('mouseleave', () => {
if (!isEyeClosed) {
eyeIris.classList.add('iris-moving');
eyeOutline.classList.add('outline-moving');
}
});
document.body.appendChild(container);
})();