新增嵌入式代码块;优化通知书推送时机
This commit is contained in:
@@ -0,0 +1,624 @@
|
||||
<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/8aea7800gy1i2vfbxoqhdj24w07c0b2p.jpg">
|
||||
</div>
|
||||
<div class="info">
|
||||
<p class="name">黄诗扶</p>
|
||||
<p class="bio">
|
||||
又甜又脆的大冬枣!好听!好看!好吃!黄黄的伟大无需多言!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="person">
|
||||
<div class="avatar">
|
||||
<img src="/upload/ec39c62fly1i3rextah4gj22dc35s1ky.jpg">
|
||||
</div>
|
||||
<div class="info">
|
||||
<p class="name">双笙</p>
|
||||
<p class="bio">
|
||||
什么?你问我为什么喜欢包包?可能因为是“青春”吧?
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="person">
|
||||
<div class="avatar">
|
||||
<img src="/upload/005Jyswjgy1hywx7z7sb7j30zk1betbd.jpg">
|
||||
</div>
|
||||
<div class="info">
|
||||
<p class="name">浅影阿</p>
|
||||
<p class="bio">
|
||||
谁能拒绝唱歌这么好看的老大呢?反正我是不行!下一位!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="person">
|
||||
<div class="avatar">
|
||||
<img src="/upload/ab0efe4ely1hnpfzqqw1oj23ls5eoqvg.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="/upload/logo.png">
|
||||
</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>
|
||||
Reference in New Issue
Block a user