624 lines
18 KiB
HTML
624 lines
18 KiB
HTML
<section class="people-list">
|
|
<style>
|
|
/* CSS 变量定义 (绿色主题) */
|
|
:root {
|
|
--green-text: #0f5132;
|
|
--green-light: #e9f7f1;
|
|
--green-border: #c3e6d3;
|
|
--green-link: #197d53;
|
|
--green-link-hover: #146343;
|
|
--green-glow: rgba(15, 81, 50, 0.2);
|
|
--avatar-w: 80px;
|
|
--avatar-h: 100px;
|
|
--radius: 12px;
|
|
--font: "Helvetica Neue", Arial, sans-serif;
|
|
--scratch-cursor-size: 100px;
|
|
/* 刮奖圆圈大小 */
|
|
--scratch-mask-color: #d1efe3;
|
|
/* 刮奖遮罩颜色 */
|
|
}
|
|
|
|
/* 整体容器样式 */
|
|
.people-list {
|
|
font-family: var(--font);
|
|
max-width: 860px;
|
|
margin: 0 auto;
|
|
padding: 20px 10px;
|
|
}
|
|
|
|
/* 标题样式 */
|
|
.category-title {
|
|
font-size: 1.4rem;
|
|
font-weight: 600;
|
|
color: var(--green-text);
|
|
margin: 0 0 16px 0;
|
|
padding-bottom: 6px;
|
|
border-bottom: 2px solid var(--green-border);
|
|
letter-spacing: 0.5px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
/* 朋友圈链接样式 */
|
|
.people-friends-link {
|
|
font-size: 0.9rem !important;
|
|
font-weight: normal !important;
|
|
color: var(--green-link) !important;
|
|
text-decoration: none !important;
|
|
display: flex !important;
|
|
align-items: center !important;
|
|
gap: 4px !important;
|
|
transition: color 0.2s ease !important;
|
|
}
|
|
|
|
.people-friends-link:hover {
|
|
color: var(--green-link-hover) !important;
|
|
}
|
|
|
|
/* 分类区段 */
|
|
.category-section {
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.category-section:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
/* 卡片网格布局 */
|
|
.people-grid {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 12px;
|
|
}
|
|
|
|
/* 单个卡片 (person) 基础样式 */
|
|
.person {
|
|
display: flex;
|
|
gap: 12px;
|
|
padding: 14px;
|
|
border-radius: 14px;
|
|
background: var(--green-light);
|
|
align-items: center;
|
|
flex: 1 1 calc(50% - 6px);
|
|
min-width: 260px;
|
|
transition: background-color 0.2s ease;
|
|
position: relative;
|
|
overflow: hidden;
|
|
cursor: default;
|
|
/* 默认光标 */
|
|
}
|
|
|
|
/* 普通卡片:鼠标光晕效果 */
|
|
.person:not(.mystery-person)::before {
|
|
content: "";
|
|
position: absolute;
|
|
width: 100px;
|
|
height: 100px;
|
|
border-radius: 50%;
|
|
background: var(--green-glow);
|
|
transform: translate(-50%, -50%) scale(0);
|
|
transition: transform 0.3s ease;
|
|
pointer-events: none;
|
|
z-index: 0;
|
|
left: 50%;
|
|
/* 默认居中 */
|
|
top: 50%;
|
|
/* 默认居中 */
|
|
}
|
|
|
|
/* 悬停时光晕放大 */
|
|
.person:not(.mystery-person):hover::before {
|
|
transform: translate(-50%, -50%) scale(1);
|
|
}
|
|
|
|
/* 悬停时头像缩放 */
|
|
.person:not(.mystery-person):hover .avatar img {
|
|
transform: scale(1.15);
|
|
}
|
|
|
|
/* 悬停时背景色变化 */
|
|
.person:not(.mystery-person):hover {
|
|
background-color: #dff4ec;
|
|
}
|
|
|
|
/* 头像区域 */
|
|
.avatar {
|
|
width: var(--avatar-w);
|
|
height: var(--avatar-h);
|
|
border-radius: var(--radius);
|
|
overflow: hidden;
|
|
background: #d1efe3;
|
|
flex-shrink: 0;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.avatar img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
transition: transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
transform-origin: center center;
|
|
}
|
|
|
|
/* 信息区域 */
|
|
.info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
height: 100%;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.name,
|
|
.bio {
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
.name {
|
|
font-size: 1.15rem;
|
|
font-weight: 700;
|
|
color: var(--green-text);
|
|
}
|
|
|
|
.bio {
|
|
margin-top: 1px;
|
|
font-size: 0.92rem;
|
|
color: #234437;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.bio a {
|
|
color: var(--green-text);
|
|
text-decoration: none !important;
|
|
border-bottom: none !important;
|
|
padding-bottom: 0;
|
|
}
|
|
|
|
/* 神秘嘉宾卡片 (刮奖效果) */
|
|
.mystery-person {
|
|
cursor: none;
|
|
/* 隐藏默认光标 */
|
|
background-color: var(--scratch-mask-color);
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* 刮奖遮罩 Canvas */
|
|
.mystery-mask {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: 2;
|
|
pointer-events: auto;
|
|
/* 允许鼠标事件交互 */
|
|
border-radius: 14px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* 刮奖提示文字 */
|
|
.mystery-hint {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: 2;
|
|
pointer-events: none;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: var(--green-text);
|
|
font-size: 1.2rem;
|
|
text-align: center;
|
|
padding: 20px;
|
|
transition: opacity 0.2s ease;
|
|
}
|
|
|
|
.mystery-hint i {
|
|
font-size: 1.5rem;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
/* 刮奖自定义光标 */
|
|
.mystery-cursor {
|
|
position: absolute;
|
|
width: var(--scratch-cursor-size);
|
|
height: var(--scratch-cursor-size);
|
|
border-radius: 50%;
|
|
border: 2px solid rgba(15, 81, 50, 0.6);
|
|
background-color: transparent;
|
|
pointer-events: none;
|
|
z-index: 3;
|
|
display: none;
|
|
/* 默认隐藏 */
|
|
}
|
|
|
|
/* 补充 CSS:普通卡片光晕位置 (用于JS鼠标追踪) */
|
|
.person:not(.mystery-person)::before {
|
|
left: var(--mouse-x, 50%);
|
|
top: var(--mouse-y, 50%);
|
|
}
|
|
|
|
/* 响应式调整 (max-width: 768px) */
|
|
@media (max-width: 768px) {
|
|
.category-title {
|
|
font-size: 1.3rem;
|
|
margin-bottom: 14px;
|
|
}
|
|
|
|
.people-friends-link {
|
|
font-size: 0.85rem !important;
|
|
}
|
|
|
|
.person {
|
|
flex: 1 1 100%;
|
|
/* 单列显示 */
|
|
padding: 18px;
|
|
gap: 18px;
|
|
overflow: visible;
|
|
}
|
|
|
|
.people-grid {
|
|
gap: 18px;
|
|
}
|
|
|
|
.avatar {
|
|
--avatar-w: 90px;
|
|
--avatar-h: 110px;
|
|
}
|
|
|
|
.name {
|
|
font-size: 1.25rem;
|
|
}
|
|
|
|
.bio {
|
|
font-size: 0.96rem;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
/* 移动端隐藏普通卡片光晕 */
|
|
.person:not(.mystery-person)::before {
|
|
display: none !important;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<div class="category-section">
|
|
<h3 class="category-title">
|
|
近期联系
|
|
<a href="/friends" class="people-friends-link">
|
|
<i class="ri-group-line"></i> 前往朋友圈
|
|
</a>
|
|
</h3>
|
|
<div class="people-grid">
|
|
<div class="person">
|
|
<div class="avatar">
|
|
<img src="/upload/20260416214530_191_129.jpg">
|
|
</div>
|
|
<div class="info">
|
|
<p class="name">黄诗扶</p>
|
|
<p class="bio">
|
|
又甜又脆的大冬枣!好听!好看!好吃!黄黄的伟大无需多言!
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div class="person">
|
|
<div class="avatar">
|
|
<img src="/upload/20260416213611_190_129.jpg">
|
|
</div>
|
|
<div class="info">
|
|
<p class="name">双笙</p>
|
|
<p class="bio">
|
|
什么?你问我为什么喜欢包包?可能因为是青春吧?
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div class="person">
|
|
<div class="avatar">
|
|
<img src="/upload/005Jyswjgy1ic7xfxtlu8j33402c0npe.jpg">
|
|
</div>
|
|
<div class="info">
|
|
<p class="name">浅影阿</p>
|
|
<p class="bio">
|
|
谁能拒绝唱歌这么好看的老大呢?反正我是不行!下一位!
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div class="person">
|
|
<div class="avatar">
|
|
<img src="/upload/67f2efd2gy1i8iv8nkuqjj23344mokjp.jpg">
|
|
</div>
|
|
<div class="info">
|
|
<p class="name">玄觞</p>
|
|
<p class="bio">
|
|
你们古风圈,怎么都喊女生王子,喊男生二婶呢?
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="category-section">
|
|
<h3 class="category-title">神秘嘉宾</h3>
|
|
<div class="people-grid">
|
|
<div class="person mystery-person">
|
|
<div class="avatar">
|
|
<img src="/upload/69e273f8ly8fnvmu255jyj20e80e8wev.jpg">
|
|
</div>
|
|
<div class="info">
|
|
<p class="name">蔡徐坤</p>
|
|
<p class="bio">
|
|
不要问为什么坤坤会在这里!因为!黑粉也是粉!
|
|
</p>
|
|
</div>
|
|
<canvas class="mystery-mask"></canvas>
|
|
<div class="mystery-hint">
|
|
<i class="ri-hand-scratch-line"></i>
|
|
点击或悬浮,解锁神秘嘉宾
|
|
</div>
|
|
<div class="mystery-cursor"></div>
|
|
</div>
|
|
|
|
<div class="person mystery-person">
|
|
<div class="avatar">
|
|
<img src="/themes/theme-pix/assets/img/logo.webp">
|
|
</div>
|
|
<div class="info">
|
|
<p class="name">敬请期待</p>
|
|
<p class="bio">
|
|
会是谁呢?我也不知道
|
|
</p>
|
|
</div>
|
|
<canvas class="mystery-mask"></canvas>
|
|
<div class="mystery-hint">
|
|
<i class="ri-hand-scratch-line"></i>
|
|
点击或悬浮,解锁神秘嘉宾
|
|
</div>
|
|
<div class="mystery-cursor"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="category-section">
|
|
<h3 class="category-title">全部名单</h3>
|
|
<div class="people-grid">
|
|
<div class="person">
|
|
<div class="avatar">
|
|
<img src="/upload/62a32171jw8f2kxo5x4j1j20ai0bvt9l.jpg">
|
|
</div>
|
|
<div class="info">
|
|
<p class="name">明月庄主</p>
|
|
<p class="bio">
|
|
眼睁睁的看着庄主结婚生娃,也算是从小看到大了
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div class="person">
|
|
<div class="avatar">
|
|
<img src="/upload/80c44916gy1i6s8l132bhj20u01n14cu.jpg">
|
|
</div>
|
|
<div class="info">
|
|
<p class="name">杨馥羽</p>
|
|
<p class="bio">
|
|
剧是没看的,人是要夸可爱的,抖音是第一条推的
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div class="person">
|
|
<div class="avatar">
|
|
<img src="/upload/mugu.jpg">
|
|
</div>
|
|
<div class="info">
|
|
<p class="name">木骨</p>
|
|
<p class="bio">
|
|
事情的起因要从那节最优化理论课说起...
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div class="person">
|
|
<div class="avatar">
|
|
<img src="/upload/001U2r7Jgy1i20tzkph8gj61vk2tcx6p02.jpg">
|
|
</div>
|
|
<div class="info">
|
|
<p class="name">音频怪物</p>
|
|
<p class="bio">
|
|
你知道的,我很少粉同性的,但...老妖他不一样!
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// 存储事件监听引用,用于PJAX返回时清理
|
|
const eventListeners = {
|
|
personMouseMove: [],
|
|
mysteryMouseMove: [],
|
|
mysteryMouseLeave: [],
|
|
mysteryClick: [],
|
|
resize: []
|
|
};
|
|
|
|
// 普通卡片:鼠标跟踪脚本
|
|
function initPersonCards() {
|
|
// 清理之前的事件监听
|
|
eventListeners.personMouseMove.forEach(({ el, handler }) => {
|
|
el.removeEventListener('mousemove', handler);
|
|
});
|
|
eventListeners.personMouseMove = [];
|
|
|
|
const personCards = document.querySelectorAll('.person:not(.mystery-person)');
|
|
personCards.forEach(card => {
|
|
let lastMouseX = 50;
|
|
let lastMouseY = 50;
|
|
|
|
const handleMouseMove = (e) => {
|
|
const rect = card.getBoundingClientRect();
|
|
lastMouseX = ((e.clientX - rect.left) / rect.width) * 100;
|
|
lastMouseY = ((e.clientY - rect.top) / rect.height) * 100;
|
|
card.style.setProperty('--mouse-x', `${lastMouseX}%`);
|
|
card.style.setProperty('--mouse-y', `${lastMouseY}%`);
|
|
};
|
|
|
|
card.addEventListener('mousemove', handleMouseMove);
|
|
eventListeners.personMouseMove.push({ el: card, handler: handleMouseMove });
|
|
|
|
card.addEventListener('mouseleave', () => { });
|
|
});
|
|
}
|
|
|
|
// 神秘嘉宾:刮奖效果脚本
|
|
function initMysteryCards() {
|
|
// 清理之前的事件监听
|
|
eventListeners.mysteryMouseMove.forEach(({ el, handler }) => {
|
|
el.removeEventListener('mousemove', handler);
|
|
});
|
|
eventListeners.mysteryMouseLeave.forEach(({ el, handler }) => {
|
|
el.removeEventListener('mouseleave', handler);
|
|
});
|
|
eventListeners.mysteryClick.forEach(({ el, handler }) => {
|
|
el.removeEventListener('click', handler);
|
|
});
|
|
eventListeners.resize.forEach(handler => {
|
|
window.removeEventListener('resize', handler);
|
|
});
|
|
eventListeners.mysteryMouseMove = [];
|
|
eventListeners.mysteryMouseLeave = [];
|
|
eventListeners.mysteryClick = [];
|
|
eventListeners.resize = [];
|
|
|
|
const mysteryCards = document.querySelectorAll('.mystery-person');
|
|
mysteryCards.forEach(card => {
|
|
const canvas = card.querySelector('.mystery-mask');
|
|
const ctx = canvas.getContext('2d');
|
|
const cursor = card.querySelector('.mystery-cursor');
|
|
const hintText = card.querySelector('.mystery-hint');
|
|
|
|
// 从:root获取 CSS 变量值
|
|
const computedStyle = getComputedStyle(document.documentElement);
|
|
const cursorSize = parseInt(computedStyle.getPropertyValue('--scratch-cursor-size'));
|
|
const maskColor = computedStyle.getPropertyValue('--scratch-mask-color');
|
|
|
|
// 重置/初始化 Canvas 状态
|
|
canvas.width = card.offsetWidth;
|
|
canvas.height = card.offsetHeight;
|
|
ctx.fillStyle = maskColor;
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
hintText.style.opacity = '1';
|
|
cursor.style.display = 'none';
|
|
canvas.style.display = 'block';
|
|
hintText.style.display = 'flex';
|
|
card.style.cursor = 'none';
|
|
|
|
function resizeMysteryCanvas() {
|
|
const rect = card.getBoundingClientRect();
|
|
canvas.width = rect.width;
|
|
canvas.height = rect.height;
|
|
fillMysteryMask();
|
|
}
|
|
|
|
function fillMysteryMask() {
|
|
ctx.fillStyle = maskColor;
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
}
|
|
|
|
function updateMysteryCursor(x, y) {
|
|
// 光标位置计算
|
|
cursor.style.left = `${x - cursorSize / 2}px`;
|
|
cursor.style.top = `${y - cursorSize / 2}px`;
|
|
}
|
|
|
|
function scratchMystery(x, y) {
|
|
fillMysteryMask();
|
|
ctx.globalCompositeOperation = 'destination-out';
|
|
ctx.beginPath();
|
|
// 刮奖半径同步光标大小
|
|
ctx.arc(x, y, cursorSize / 2, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
ctx.globalCompositeOperation = 'source-over';
|
|
}
|
|
|
|
const handleMouseMove = (e) => {
|
|
const rect = canvas.getBoundingClientRect();
|
|
const x = e.clientX - rect.left;
|
|
const y = e.clientY - rect.top;
|
|
|
|
cursor.style.display = 'block';
|
|
hintText.style.opacity = '0';
|
|
updateMysteryCursor(x, y);
|
|
scratchMystery(x, y);
|
|
};
|
|
|
|
const handleMouseLeave = () => {
|
|
cursor.style.display = 'none';
|
|
hintText.style.opacity = '1';
|
|
fillMysteryMask(); // 鼠标移开重新遮挡
|
|
};
|
|
|
|
const handleClick = () => {
|
|
// 清除遮罩,显示内容
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
setTimeout(() => {
|
|
canvas.style.display = 'none';
|
|
hintText.style.display = 'none';
|
|
card.style.cursor = 'default';
|
|
cursor.style.display = 'none';
|
|
}, 300);
|
|
};
|
|
|
|
// 绑定事件
|
|
canvas.addEventListener('mousemove', handleMouseMove);
|
|
canvas.addEventListener('mouseleave', handleMouseLeave);
|
|
card.addEventListener('click', handleClick);
|
|
window.addEventListener('resize', resizeMysteryCanvas);
|
|
|
|
// 存储事件监听引用
|
|
eventListeners.mysteryMouseMove.push({ el: canvas, handler: handleMouseMove });
|
|
eventListeners.mysteryMouseLeave.push({ el: canvas, handler: handleMouseLeave });
|
|
eventListeners.mysteryClick.push({ el: card, handler: handleClick });
|
|
eventListeners.resize.push(resizeMysteryCanvas);
|
|
|
|
resizeMysteryCanvas();
|
|
});
|
|
}
|
|
|
|
// 初始化所有组件
|
|
function initPeopleList() {
|
|
initPersonCards();
|
|
initMysteryCards();
|
|
}
|
|
|
|
// 页面首次加载初始化
|
|
document.addEventListener('DOMContentLoaded', initPeopleList);
|
|
|
|
// PJAX跳转成功后重新初始化
|
|
document.addEventListener('pjax:success', function () {
|
|
// 仅当页面存在people-list组件时才初始化
|
|
if (document.querySelector('.people-list')) {
|
|
initPeopleList();
|
|
}
|
|
});
|
|
|
|
// 页面隐藏时清理事件监听(避免内存泄漏)
|
|
document.addEventListener('visibilitychange', function () {
|
|
if (document.hidden) {
|
|
// 清理所有事件监听 (简化逻辑:仅清除存储的)
|
|
[...eventListeners.personMouseMove, ...eventListeners.mysteryMouseMove].forEach(({ el, handler }) => el.removeEventListener('mousemove', handler));
|
|
eventListeners.mysteryMouseLeave.forEach(({ el, handler }) => el.removeEventListener('mouseleave', handler));
|
|
eventListeners.mysteryClick.forEach(({ el, handler }) => el.removeEventListener('click', handler));
|
|
eventListeners.resize.forEach(handler => window.removeEventListener('resize', handler));
|
|
} else {
|
|
// 页面重新显示时重新初始化
|
|
if (document.querySelector('.people-list')) {
|
|
initPeopleList();
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
</section> |