以下是这份公告弹窗代码的完整功能介绍,涵盖设计特点、核心功能和交互逻辑:### 一、视觉设计与动画效果1. 现代玻璃态UI - 采用半透明背景(rgba(255, 255, 255, 0.9)
)搭配毛玻璃效果(backdrop-filter: blur(12px)
),与页面背景自然融合 - 圆角设计(24px)+ 柔和阴影(0 20px 50px rgba(0, 0, 0, 0.12)
),增强立体感 - 顶部渐变装饰条(#4361ee
到 #3a0ca3
),提升视觉焦点2. 分层入场动画 - 弹窗内元素按顺序依次出现(图标→标题→内容→倒计时→按钮→底部信息),每个元素延迟不同时间,形成流畅的层次感 - 动画类型包括:缩放弹出(popIn
)、淡入上移(fadeInUp
)、简单淡入(fadeIn
),增强页面活力3. 微交互效果 - 倒计时数字变化时触发脉冲动画(轻微放大后恢复),直观提示时间流逝 - 按钮悬停时显示流光效果(透明光带从左到右滑动),提升交互反馈 - 关闭按钮悬停时旋转+缩放,增强操作感知### 二、核心功能与逻辑1. 智能倒计时系统 - 双阶段倒计时:活动开始前显示“距离开始时间”,开始后自动切换为“距离结束时间”(活动时间固定为10月16日-10月26日) - 动态更新:每秒刷新倒计时数字(天/时/分/秒),并自动补零保持格式统一(如“03天”而非“3天”) - 结束处理:活动结束后自动显示“活动已结束”提示,并禁用“参加活动”按钮2. 显示频率控制 - 关闭弹窗后,通过localStorage
记录关闭时间,3小时内不再重复显示,平衡曝光率与用户体验 - 3小时后自动失效,重新打开页面会再次显示,确保用户不会错过活动关键信息3. 多场景适配 - 响应式设计:在手机、平板、电脑上自动调整布局(如移动端按钮纵向排列) - 内容自适应:根据活动阶段(未开始/进行中/已结束)自动切换公告文案,保持信息准确性### 三、交互与操作设计1. 多种关闭方式 - 点击右上角关闭按钮(×) - 点击“我知道了”按钮 - 点击弹窗外部背景区域 - 按下键盘ESC键 所有关闭方式均会触发淡出动画,提升操作流畅度2. 活动跳转功能 - “参加活动”按钮默认链接到指定地址(https://www.xkwo.com
),点击后在新窗口打开 - 活动未开始时按钮置灰不可点击,开始后自动激活,引导用户参与### 四、技术特点1. 纯前端实现:无需后端支持,通过HTML+CSS+JavaScript完成所有功能,可直接嵌入任何网站 2. 无依赖:不依赖jQuery等框架,代码轻量且独立,避免与现有网站代码冲突 3. 性能优化: - 关闭弹窗时自动清除倒计时定时器,避免内存泄漏 - 动画仅在必要时触发(如数字变化时才显示脉冲效果) - 通过localStorage
实现状态记录,无需频繁操作DOM### 总结这份代码是一个功能完整、体验优良的活动公告弹窗,既具备视觉吸引力(玻璃态设计+流畅动画),又通过智能倒计时和频率控制实现了高效的信息传递,同时兼顾了多设备适配和操作便捷性,适合各类网站的活动推广场景。
<script>
// 动态公告弹窗生成函数
function createAnnouncementPopup() {
// 检查是否在三小时内已关闭过弹窗
const lastClosedTime = localStorage.getItem('popupLastClosed');
const now = new Date().getTime();
const threeHours = 3 * 60 * 60 * 1000; // 三小时的毫秒数
// 如果三小时内关闭过,则不显示
if (lastClosedTime && (now - parseInt(lastClosedTime) < threeHours)) {
return;
}
// 创建样式
const style = document.createElement('style');
style.textContent = `
.announcement-popup {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.25);
backdrop-filter: blur(8px);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
opacity: 0;
visibility: hidden;
transition: opacity 0.6s cubic-bezier(0.34, 1.56, 0.64, 1), visibility 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
padding: 1rem;
}
.announcement-popup.active {
opacity: 1;
visibility: visible;
}
.popup-content {
background: rgba(255, 255, 255, 0.9);
border-radius: 24px;
width: 100%;
max-width: 550px;
padding: 2.5rem 2rem;
position: relative;
transform: translateY(40px) scale(0.95);
opacity: 0;
transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.12);
backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.7);
overflow: hidden;
}
.announcement-popup.active .popup-content {
transform: translateY(0) scale(1);
opacity: 1;
}
.popup-content::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 8px;
background: linear-gradient(90deg, #4361ee, #3a0ca3);
}
.popup-close {
position: absolute;
top: 1.5rem;
right: 1.5rem;
background: rgba(255, 255, 255, 0.7);
border: none;
font-size: 1.2rem;
cursor: pointer;
color: #4a4a4a;
transition: all 0.3s ease;
width: 38px;
height: 38px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
z-index: 10;
transform: translate(10px, -10px) scale(0);
animation: popIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) 0.5s forwards;
}
.popup-close:hover {
color: #2d2d2d;
background: white;
transform: rotate(90deg) scale(1.1);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
}
.popup-icon {
width: 80px;
height: 80px;
background: linear-gradient(135deg, #e0e7ff, #c7d2fe);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 2rem;
box-shadow: 0 6px 16px rgba(67, 97, 238, 0.15);
position: relative;
overflow: hidden;
transform: scale(0);
animation: popIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) 0.2s forwards;
}
.popup-icon::after {
content: '';
position: absolute;
top: -15px;
right: -15px;
width: 40px;
height: 40px;
background: rgba(255, 255, 255, 0.3);
border-radius: 50%;
}
.popup-icon svg {
width: 38px;
height: 38px;
color: #4361ee;
position: relative;
z-index: 1;
transform: scale(0.8);
animation: scaleIn 0.4s ease 0.4s forwards;
}
.popup-title {
font-size: 1.8rem;
font-weight: 700;
color: #1e293b;
text-align: center;
margin-bottom: 1.5rem;
line-height: 1.3;
text-shadow: 0 1px 2px rgba(255, 255, 255, 0.6);
letter-spacing: -0.02em;
opacity: 0;
transform: translateY(20px);
animation: fadeInUp 0.5s ease 0.6s forwards;
}
.popup-message {
color: #334155;
text-align: center;
line-height: 1.8;
margin-bottom: 1.8rem;
font-size: 1.1rem;
padding: 0 1rem;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);
opacity: 0;
transform: translateY(20px);
animation: fadeInUp 0.5s ease 0.7s forwards;
}
/* 倒计时样式 */
.countdown-container {
text-align: center;
margin-bottom: 2.2rem;
padding: 0 0.5rem;
opacity: 0;
animation: fadeIn 0.6s ease 0.8s forwards;
}
.countdown-title {
font-size: 1.05rem;
color: #d946ef;
font-weight: 600;
margin-bottom: 1rem;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.6);
letter-spacing: 0.03em;
}
.countdown-timer {
display: flex;
justify-content: center;
gap: 1rem;
}
.countdown-item {
background: rgba(255, 255, 255, 0.9);
padding: 1rem 0.8rem;
border-radius: 12px;
min-width: 68px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.07);
border: 1px solid rgba(255, 255, 255, 0.8);
position: relative;
overflow: hidden;
transform: scale(0.9);
opacity: 0;
}
.countdown-item:nth-child(1) { animation: popIn 0.4s ease 0.9s forwards; }
.countdown-item:nth-child(2) { animation: popIn 0.4s ease 1.0s forwards; }
.countdown-item:nth-child(3) { animation: popIn 0.4s ease 1.1s forwards; }
.countdown-item:nth-child(4) { animation: popIn 0.4s ease 1.2s forwards; }
.countdown-item::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 3px;
background: linear-gradient(90deg, #4361ee, #d946ef);
opacity: 0;
transition: opacity 0.3s ease;
}
.countdown-item:hover::before {
opacity: 1;
}
.countdown-number {
font-size: 1.6rem;
font-weight: 700;
color: #1e293b;
line-height: 1;
text-shadow: 0 1px 2px rgba(255, 255, 255, 0.7);
font-variant-numeric: tabular-nums;
transition: transform 0.3s ease;
}
.countdown-number.changed {
animation: pulse 0.3s ease;
}
.countdown-label {
font-size: 0.8rem;
color: #475569;
margin-top: 0.5rem;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.popup-buttons {
display: flex;
gap: 1.2rem;
margin-bottom: 1.2rem;
padding: 0 0.5rem;
opacity: 0;
animation: fadeIn 0.6s ease 1.3s forwards;
}
.popup-cta, .popup-action {
flex: 1;
border: none;
padding: 1.1rem 1rem;
border-radius: 14px;
font-size: 1.05rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
text-align: center;
white-space: nowrap;
opacity: 0.95;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
position: relative;
overflow: hidden;
transform: translateY(20px);
opacity: 0;
}
.popup-cta {
background: linear-gradient(135deg, #4361ee, #3a0ca3);
color: white;
box-shadow: 0 6px 18px rgba(67, 97, 238, 0.25);
animation: fadeInUp 0.5s ease 1.4s forwards;
}
.popup-action {
background: linear-gradient(135deg, #f72585, #d946ef);
color: white;
box-shadow: 0 6px 18px rgba(217, 70, 239, 0.25);
text-decoration: none;
display: flex;
align-items: center;
justify-content: center;
animation: fadeInUp 0.5s ease 1.5s forwards;
}
.popup-cta::after, .popup-action::after {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: all 0.6s ease;
}
.popup-cta:hover::after, .popup-action:hover::after {
left: 100%;
}
.popup-cta:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(67, 97, 238, 0.35);
opacity: 1;
}
.popup-action:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(217, 70, 239, 0.35);
opacity: 1;
}
.popup-cta:active, .popup-action:active {
transform: translateY(-2px);
}
.popup-footer {
text-align: center;
margin-top: 1.5rem;
font-size: 0.9rem;
color: #58667e;
line-height: 1.6;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);
padding: 0 1rem;
opacity: 0;
animation: fadeIn 0.6s ease 1.6s forwards;
}
/* 动画关键帧定义 */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes popIn {
0% {
opacity: 0;
transform: scale(0.8);
}
70% {
transform: scale(1.05);
}
100% {
opacity: 1;
transform: scale(1);
}
}
@keyframes scaleIn {
from { transform: scale(0.8); }
to { transform: scale(1); }
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.15); }
100% { transform: scale(1); }
}
@media (max-width: 480px) {
.popup-content {
padding: 2rem 1.5rem;
border-radius: 20px;
}
.popup-buttons {
flex-direction: column;
gap: 1rem;
}
.popup-title {
font-size: 1.6rem;
margin-bottom: 1.2rem;
}
.popup-message {
font-size: 1rem;
margin-bottom: 1.5rem;
}
.countdown-container {
margin-bottom: 1.8rem;
}
.countdown-item {
min-width: 60px;
padding: 0.8rem 0.6rem;
}
.countdown-number {
font-size: 1.4rem;
}
.popup-cta, .popup-action {
padding: 1.05rem;
font-size: 1rem;
}
.popup-icon {
width: 70px;
height: 70px;
margin-bottom: 1.8rem;
}
}
`;
document.head.appendChild(style);
// 创建弹窗HTML
const popup = document.createElement('div');
popup.className = 'announcement-popup';
popup.innerHTML = `
<div class="popup-content">
<button class="popup-close">×</button>
<div class="popup-icon">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<h3 class="popup-title">限时活动公告</h3>
<p class="popup-message" id="popupMessage">
年度回馈活动即将开始!设置提醒,第一时间参与活动,抢占丰厚奖励!
</p>
<!-- 倒计时区域 -->
<div class="countdown-container">
<div class="countdown-title" id="countdownTitle">活动开始倒计时</div>
<div class="countdown-timer">
<div class="countdown-item">
<div class="countdown-number" id="days">00</div>
<div class="countdown-label">天</div>
</div>
<div class="countdown-item">
<div class="countdown-number" id="hours">00</div>
<div class="countdown-label">时</div>
</div>
<div class="countdown-item">
<div class="countdown-number" id="minutes">00</div>
<div class="countdown-label">分</div>
</div>
<div class="countdown-item">
<div class="countdown-number" id="seconds">00</div>
<div class="countdown-label">秒</div>
</div>
</div>
</div>
<div class="popup-buttons">
<button class="popup-cta">我知道了</button>
<a href="https://www.xkwo.com/" target="_blank" class="popup-action" id="actionButton" style="opacity:0.7;pointer-events:none;">
活动未开始
</a>
</div>
<div class="popup-footer">活动时间:10月16日-10月26日 | 如有疑问,请联系在线客服</div>
</div>
`;
document.body.appendChild(popup);
// 显示弹窗(延迟1秒,让页面加载完成)
setTimeout(() => {
popup.classList.add('active');
}, 1000);
// 关闭弹窗的核心函数 - 记录关闭时间
function closePopup() {
// 记录当前关闭时间到localStorage
localStorage.setItem('popupLastClosed', now.toString());
popup.classList.remove('active');
// 清除倒计时定时器
if (window.countdownTimer) {
clearInterval(window.countdownTimer);
window.countdownTimer = null;
}
// 动画结束后移除弹窗
setTimeout(() => {
if (popup.parentNode) {
popup.remove();
}
}, 600);
}
// 倒计时功能实现 - 带数字变化动画
function startSmartCountdown() {
// 保存上次倒计时数值,用于检测变化
let lastValues = { days: '00', hours: '00', minutes: '00', seconds: '00' };
// 设置活动时间
const startTime = new Date();
startTime.setMonth(9);
startTime.setDate(16);
startTime.setHours(0, 0, 0, 0);
const endTime = new Date();
endTime.setMonth(9);
endTime.setDate(26);
endTime.setHours(23, 59, 59, 0);
const now = new Date();
let targetTime, isBeforeStart;
if (now < startTime) {
targetTime = startTime;
isBeforeStart = true;
} else if (now <= endTime) {
targetTime = endTime;
isBeforeStart = false;
updateForActiveEvent();
} else {
document.getElementById('countdownTitle').innerText = '活动已结束';
document.getElementById('countdown-timer').innerHTML = '<div style="color:#58667e;padding:1rem 0;">敬请期待下次活动</div>';
document.getElementById('popupMessage').textContent = '本次年度回馈活动已圆满结束,感谢您的参与!更多精彩活动敬请关注。';
document.getElementById('actionButton').textContent = '活动已结束';
return;
}
function updateForActiveEvent() {
document.getElementById('countdownTitle').innerText = '活动剩余时间';
document.getElementById('popupMessage').textContent = '年度回馈活动火热进行中!参与活动即可获得丰厚奖励,抓紧时间点击参与,错过再等一年!';
document.getElementById('actionButton').textContent = '参加活动';
document.getElementById('actionButton').style.opacity = '0.95';
document.getElementById('actionButton').style.pointerEvents = 'auto';
}
function updateCountdown() {
const now = new Date().getTime();
const distance = targetTime - now;
if (isBeforeStart && now >= startTime) {
targetTime = endTime;
isBeforeStart = false;
updateForActiveEvent();
}
// 计算时间差
const days = Math.floor(distance / (1000 * 60 * 60 * 24)).toString().padStart(2, '0');
const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)).toString().padStart(2, '0');
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)).toString().padStart(2, '0');
const seconds = Math.floor((distance % (1000 * 60)) / 1000).toString().padStart(2, '0');
// 更新DOM并添加变化动画
const elements = {
days: document.getElementById('days'),
hours: document.getElementById('hours'),
minutes: document.getElementById('minutes'),
seconds: document.getElementById('seconds')
};
// 检查每个数值是否变化,变化则添加动画
if (days !== lastValues.days) {
elements.days.innerText = days;
elements.days.classList.add('changed');
setTimeout(() => elements.days.classList.remove('changed'), 300);
lastValues.days = days;
}
if (hours !== lastValues.hours) {
elements.hours.innerText = hours;
elements.hours.classList.add('changed');
setTimeout(() => elements.hours.classList.remove('changed'), 300);
lastValues.hours = hours;
}
if (minutes !== lastValues.minutes) {
elements.minutes.innerText = minutes;
elements.minutes.classList.add('changed');
setTimeout(() => elements.minutes.classList.remove('changed'), 300);
lastValues.minutes = minutes;
}
if (seconds !== lastValues.seconds) {
elements.seconds.innerText = seconds;
elements.seconds.classList.add('changed');
setTimeout(() => elements.seconds.classList.remove('changed'), 300);
lastValues.seconds = seconds;
}
// 活动结束处理
if (distance < 0 && !isBeforeStart) {
clearInterval(window.countdownTimer);
window.countdownTimer = null;
document.getElementById('countdownTitle').innerText = '活动已结束';
document.getElementById('countdown-timer').innerHTML = '<div style="color:#58667e;padding:1rem 0;">敬请期待下次活动</div>';
document.getElementById('popupMessage').textContent = '本次年度回馈活动已圆满结束,感谢您的参与!更多精彩活动敬请关注。';
document.getElementById('actionButton').textContent = '活动已结束';
document.getElementById('actionButton').style.opacity = '0.7';
document.getElementById('actionButton').style.pointerEvents = 'none';
}
}
updateCountdown();
window.countdownTimer = setInterval(updateCountdown, 1000);
}
// 启动倒计时
startSmartCountdown();
// 绑定关闭事件
const closeBtn = popup.querySelector('.popup-close');
const ctaBtn = popup.querySelector('.popup-cta');
closeBtn.addEventListener('click', () => closePopup());
ctaBtn.addEventListener('click', () => closePopup());
popup.addEventListener('click', (e) => {
if (e.target === popup) closePopup();
});
const handleEscKey = (e) => {
if (e.key === 'Escape' && popup.classList.contains('active')) {
closePopup();
document.removeEventListener('keydown', handleEscKey);
}
};
document.addEventListener('keydown', handleEscKey);
}
// 页面加载完成后执行
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
setTimeout(createAnnouncementPopup, 500);
});
} else {
setTimeout(createAnnouncementPopup, 500);
}
</script>