Files
halo-theme-pix/embed-code/page/address.html
T
2026-07-20 01:13:14 +08:00

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>