网站节日弹窗自动实现
171
2026-03-08
很久以前就想要这个功能了,今天借助豆包帮我实现了一下感谢豆包😄
网站全自动节日弹窗(纯前端实功能):
✅ 阳历节日+农历节日+二十四节气
✅ 仅当天首次访问弹出
✅ 淡入动画 + 关闭按钮
✅ 图片加载失败不弹窗
✅ 可直接放入 head 标签
实例截图

如果你是Halo用户 可以在Halo后台→设置→代码注入→全局Head标签 内插入下述代码
功能代码
此功能不依赖任何插件、不影响网站速度,弹窗优优先级:阳历 > 农历 > 节气
复制代码到网站 head 标签
替换图片地址为自己的海报
<style>
.festival-popup {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
z-index: 999999;
cursor: pointer;
opacity: 0;
animation: fadeIn 0.3s forwards;
}
.festival-popup img {
max-width: 90%;
max-height: 90vh;
object-fit: contain;
border-radius: 12px;
}
.close-btn {
position: absolute;
top: 20px;
right: 20px;
width: 44px;
height: 44px;
background: rgba(0,0,0,0.5);
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
cursor: pointer;
user-select: none;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function () {
// ======================
// 配置区(只改这里)
// ======================
const solarList = [
{ name: "元旦", date: "01-01", img: "https://picsum.photos/id/1/800/1000" },
{ name: "情人节", date: "02-14", img: "https://picsum.photos/id/2/800/1000" },
{ name: "妇女节", date: "03-08", img: "https://picsum.photos/id/3/800/1000" },
{ name: "愚人节", date: "04-01", img: "https://picsum.photos/id/4/800/1000" },
{ name: "劳动节", date: "05-01", img: "https://picsum.photos/id/5/800/1000" },
{ name: "儿童节", date: "06-01", img: "https://picsum.photos/id/6/800/1000" },
{ name: "教师节", date: "09-10", img: "https://picsum.photos/id/7/800/1000" },
{ name: "国庆节", date: "10-01", img: "https://picsum.photos/id/8/800/1000" },
{ name: "双十一", date: "11-11", img: "https://picsum.photos/id/9/800/1000" },
{ name: "圣诞节", date: "12-25", img: "https://picsum.photos/id/10/800/1000" }
];
const lunarList = [
{ name: "春节", date: "01-01", img: "https://picsum.photos/id/11/800/1000" },
{ name: "元宵节", date: "01-15", img: "https://picsum.photos/id/12/800/1000" },
{ name: "端午节", date: "05-05", img: "https://picsum.photos/id/13/800/1000" },
{ name: "七夕节", date: "07-07", img: "https://picsum.photos/id/14/800/1000" },
{ name: "中秋节", date: "08-15", img: "https://picsum.photos/id/15/800/1000" },
{ name: "重阳节", date: "09-09", img: "https://picsum.photos/id/16/800/1000" }
];
const solarTermsList = [
"立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑",
"立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至","小寒","大寒"
];
const termImgMap = {
"立春":"https://picsum.photos/id/17/800/1000", "雨水":"https://picsum.photos/id/18/800/1000",
"惊蛰":"https://picsum.photos/id/19/800/1000", "春分":"https://picsum.photos/id/20/800/1000",
"清明":"https://picsum.photos/id/21/800/1000", "谷雨":"https://picsum.photos/id/22/800/1000",
"立夏":"https://picsum.photos/id/23/800/1000", "小满":"https://picsum.photos/id/24/800/1000",
"芒种":"https://picsum.photos/id/25/800/1000", "夏至":"https://picsum.photos/id/26/800/1000",
"小暑":"https://picsum.photos/id/27/800/1000", "大暑":"https://picsum.photos/id/28/800/1000",
"立秋":"https://picsum.photos/id/29/800/1000", "处暑":"https://picsum.photos/id/30/800/1000",
"白露":"https://picsum.photos/id/31/800/1000", "秋分":"https://picsum.photos/id/32/800/1000",
"寒露":"https://picsum.photos/id/33/800/1000", "霜降":"https://picsum.photos/id/34/800/1000",
"立冬":"https://picsum.photos/id/35/800/1000", "小雪":"https://picsum.photos/id/36/800/1000",
"大雪":"https://picsum.photos/id/37/800/1000", "冬至":"https://picsum.photos/id/38/800/1000",
"小寒":"https://picsum.photos/id/39/800/1000", "大寒":"https://picsum.photos/id/40/800/1000"
};
// ======================
// 核心逻辑(不动)
// ======================
const today = new Date();
const todayStr = `${String(today.getMonth()+1).padStart(2,'0')}-${String(today.getDate()).padStart(2,'0')}`;
const storageKey = `popup_${todayStr}`;
// 1. 阳历匹配
let img = solarList.find(i => i.date === todayStr)?.img;
// 2. 农历匹配(不重复计算)
if (!img) {
const lunar = getLunarMMDD(today);
if (lunar) img = lunarList.find(i => i.date === lunar)?.img;
}
// 3. 节气匹配(只在节气列表内才生效)
if (!img) {
const term = getSolarTerm(today);
if (term && solarTermsList.includes(term)) img = termImgMap[term];
}
// ==============================================
// 关键:非节日/非节气 → 直接退出,完全不弹窗
// ==============================================
if (!img || localStorage.getItem(storageKey)) return;
// 弹出
const popup = document.createElement('div');
popup.className = 'festival-popup';
const image = new Image();
image.src = img;
image.onload = () => {
popup.innerHTML = `<img src="${img}"><div class="close-btn">×</div>`;
document.body.appendChild(popup);
popup.onclick = e => e.target === popup && close();
popup.querySelector('.close-btn').onclick = close;
function close() { popup.remove(); localStorage.setItem(storageKey, '1'); }
};
// 农历
function getLunarMMDD(d) {
try{
const l = new ChineseLunar(d);
return `${String(l.lunarMonth).padStart(2,'0')}-${String(l.lunarDay).padStart(2,'0')}`;
}catch(e){ return null; }
}
// 节气
function getSolarTerm(d) {
const terms = ["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"];
const base = Date.UTC(d.getFullYear(),0,6,2,5,0);
const ts = (Date.UTC(d.getFullYear(),d.getMonth(),d.getDate()) - base) / 86400000;
return terms[Math.floor((ts+21.16)/15.218425)%24];
}
// 农历类
class ChineseLunar{constructor(d){this.date=d||new Date();this.solarYear=this.date.getFullYear();this.solarMonth=this.date.getMonth()+1;this.solarDay=this.date.getDate();this.compute()}compute(){const n=[0x04bd8,0x04ae0,0x0a570,0x054d5,0x0d260,0x0d950,0x16554,0x056a0,0x09ad0,0x055d2,0x04ae0,0x0a5b6,0x0a4d0,0x0d250,0x1d255,0x0b540,0x0d6a0,0x0ada2,0x095b0,0x14977,0x04970,0x0a4b0,0x0b4b5,0x06a50,0x06d40,0x1ab54,0x02b60,0x09570,0x052f2,0x04970,0x06566,0x0d4a0,0x0ea50,0x06e95,0x05ad0,0x02b60,0x186e3,0x092e0,0x1c8d7,0x0c950,0x0d4a0,0x1d8a6,0x0b550,0x056a0,0x1a5b4,0x025d0,0x092d0,0x0d2b2,0x0a950,0x0b557,0x06ca0,0x0b550,0x15355,0x04da0,0x0a5d0,0x14573,0x052d0,0x0a9a8,0x0e950,0x06aa0,0x0aea6,0x0ab50,0x04b60,0x0aae4,0x0a570,0x05260,0x0f263,0x0d950,0x05b57,0x056a0,0x096d0,0x04dd5,0x04ad0,0x0a4d0,0x0d4d4,0x0d250,0x0d558,0x0b540,0x0b5a0,0x195a6,0x095b0,0x049b0,0x0a974,0x0a4b0,0x0b27a,0x06a50,0x06d40,0x0af46,0x0ab60,0x09570,0x04af5,0x04970,0x064b0,0x074a3,0x0ea50,0x06b58,0x055c0,0x0ab60,0x096d5,0x092e0,0x0c960,0x0d954,0x0d4a0,0x0da50,0x07552,0x056a0,0x0abb7,0x025d0,0x092d0,0x0cab5,0x0a950,0x0b4a0,0x0baa4,0x0ad50,0x055d9,0x04ba0,0x0a5b0,0x15176,0x052b0,0x0a930,0x07954,0x06aa0,0x0ad50,0x05b52,0x04b60,0x0a6e6,0x0a4e0,0x0d260,0x0ea65,0x0d530,0x05aa0,0x076a3,0x096d0,0x04bd7,0x04ad0,0x0a4d0,0x1d0b6,0x0d250,0x0d520,0x0dd45,0x0b5a0,0x056d0,0x055b2,0x049b0,0x0a577,0x0a4b0,0x0aa50,0x1b255,0x06d20,0x0ada0];const o=[31,28,31,30,31,30,31,31,30,31,30,31];const t=r=>(r%4===0&&r%100!==0)||r%400===0;const r=r=>{let e=348;for(let i=32768;i>8;i>>=1)e+=n[r-1900]&i?1:0;return e+a(r)}const a=r=>n[r-1900]&15;const e=r=>a(r)?n[r-1900]&65536?30:29:0;const i=(r,o)=>n[r-1900]&65536>>o?30:29;let s=this.solarYear,u=this.solarMonth-1,l=this.solarDay;if(s<1900||s>2100)return;let c=0;for(let r=1900;r<s;r++)c+=r(r);if(t(s)&&u>1)c++;for(let r=0;r<u;r++)c+=o[r];c+=l;let f=1900;while(c>r(f))c-=r(f),f++;this.lunarYear=f;let h=1,d=a(f),m=!1;while(!0){let g=i(f,h);if(d&&h===d&&!m){g=e(f);m=!0}else{if(c<=g)break;c-=g;h++;m=!1}}this.lunarMonth=h;this.lunarDay=c;this.isLeapMonth=m}}
});
</script>注:封面图片来源于摄图网‘老卢’已获取此图的VRF授权(图片ID:330153954)