206 lines
7.6 KiB
JavaScript
Raw Normal View History

// index.js (改进版)
// 功能:
// 1. 解决用户列表竖向轮播在无缝跳转时出现的卡顿/闪动问题
// 2. 为每条拼单信息增加实时倒计时,秒级更新
document.addEventListener('DOMContentLoaded', function () {
/* ---------- 顶部横向轮播 ---------- */
const wrapper = document.querySelector('.swiper-wrapper');
const slides = [...wrapper.children];
const pagination = document.querySelector('.swiper-pagination');
const count = slides.length;
let current = 0; // 当前索引
let startX = 0; // 手势起点
let dragging = false; // 拖动状态
let timer = null; // 自动轮播计时器
/* --- 1. 生成分页小圆点 --- */
for(let i=0;i<count;i++){
const dot = document.createElement('div');
dot.className = 'swiper-dot' + (i===0?' active':'');
dot.addEventListener('click',()=>goTo(i));
pagination.appendChild(dot);
}
const dots = pagination.children;
/* --- 2. 切换核心 --- */
function goTo(index){
current = (index + count) % count; // 防越界
wrapper.style.transition = 'transform .3s ease';
wrapper.style.transform = `translateX(-${current*100}%)`;
[...dots].forEach((d,i)=>d.classList.toggle('active',i===current));
}
/* --- 3. 自动轮播 --- */
function startAuto(){
timer = setInterval(()=>goTo(current+1),3000);
}
function stopAuto(){
clearInterval(timer);
}
startAuto();
/* --- 4. 手势/鼠标拖动 --- */
const getX = e => e.touches ? e.touches[0].clientX : e.clientX;
wrapper.addEventListener('pointerdown',e=>{
stopAuto();
dragging = true;
startX = getX(e);
wrapper.style.transition = 'none'; // 跟手拖动
});
wrapper.addEventListener('pointermove',e=>{
if(!dragging) return;
const diff = getX(e) - startX; // 像素位移
wrapper.style.transform =
`translateX(calc(${-current*100}% + ${diff}px))`;
});
const endSwipe = e=>{
if(!dragging) return;
dragging = false;
const diff = getX(e) - startX;
const limit = wrapper.offsetWidth * 0.15; // 15% 宽度阈值
if(diff > limit) goTo(current-1);
else if(diff < -limit) goTo(current+1);
else goTo(current); // 回弹
startAuto();
};
wrapper.addEventListener('pointerup', endSwipe);
wrapper.addEventListener('pointercancel', endSwipe);
wrapper.addEventListener('pointerleave', endSwipe);
/* --------- 动态生成“xx人在抢参与可立即拼成” --------- */
2025-07-06 18:11:46 +08:00
const leftNum = Math.floor(Math.random() * 101) + 100; // 100 ~ 200
const groupTitle = document.getElementById('groupTitle');
if (groupTitle) groupTitle.textContent = `${leftNum}人在抢,参与可立即拼成`;
/* ---------- 拼单用户纵向轮播 ---------- */
const userList = document.getElementById('userList');
const userItems = userList.querySelectorAll('.user-item');
const itemHeight = userItems[0].offsetHeight;
let userIndex = 0;
const originalCount = userItems.length;
// 克隆第一条放到末尾,实现无缝衔接
userList.appendChild(userItems[0].cloneNode(true));
userList.addEventListener('transitionend', () => {
if (userIndex >= originalCount) {
// 闪电跳回首条,关闭过渡以避免闪屏
userList.style.transition = 'none';
userList.style.transform = 'translateY(0)';
userIndex = 0;
// 强制回流,保证下次 transition 生效
void userList.offsetWidth;
}
});
function rotateUsers() {
userIndex++;
userList.style.transition = 'transform 0.5s ease';
userList.style.transform = `translateY(${-userIndex * itemHeight}px)`;
}
// 每 3 秒滚动一次
setInterval(rotateUsers, 3000);
// 页面加载后立即滚动一次,保证视觉一致
rotateUsers();
/* ---------- 拼单倒计时 ---------- */
const countdownEls = document.querySelectorAll('.countdown');
// 预处理:把初始文本转为秒数
const countdownData = Array.from(countdownEls).map((el) => ({
el,
remain: parseTime(el.textContent.trim()),
}));
function parseTime(t) {
const [h = '00', m = '00', s = '00'] = t.split(':');
return Number(h) * 3600 + Number(m) * 60 + Number(s);
}
function formatTime(sec) {
const h = String(Math.floor(sec / 3600)).padStart(2, '0');
const m = String(Math.floor((sec % 3600) / 60)).padStart(2, '0');
const s = String(sec % 60).padStart(2, '0');
return `${h}:${m}:${s}`;
}
function updateCountdown() {
countdownData.forEach((c) => {
if (c.remain > 0) {
c.remain -= 1;
c.el.textContent = formatTime(c.remain);
} else {
c.el.textContent = '00:00:00';
// 可选:到点后给整条加灰色样式,并禁用按钮
const item = c.el.closest('.user-item');
item?.classList.add('expired');
item?.querySelector('.buy-btn')?.setAttribute('disabled', 'disabled');
}
});
}
2025-07-06 18:11:46 +08:00
/* --------- 动态填充“已抢 xxx 件” --------- */
const soldBox = document.getElementById('soldBox');
if (soldBox){
const soldNum = Math.floor(Math.random()*101)+200; // 200~300
soldBox.textContent = `已抢 ${soldNum}`;
}
/* --------- 给每条拼单状态前加 “仅剩 x 人成团” --------- */
document.querySelectorAll('.user-status').forEach(statusEl=>{
const x = Math.floor(Math.random()*3)+1; // 1 ~ 3 随机整数
const span = document.createElement('span');
span.className = 'left-num';
span.textContent = `仅剩${x}人成团,`; // 注意带逗号或空格
statusEl.prepend(span);
});
/* ============= 支付 & 登录判断 ============= */
const modal = document.getElementById('paymentModal');
const paymentAmount = document.getElementById('paymentAmount');
const cancelPayment = document.getElementById('cancelPayment');
const completePayment= document.getElementById('completePayment');
/* 把 3 类按钮统一选出来 */
[...document.querySelectorAll('.buy-btn, .btn-single, .btn-group')].forEach(btn=>{
btn.addEventListener('click',()=>{
/* 简单读取 cookie 判断是否登录 */
if(!getCookie('username')){
window.location.href='login.html'; // 跳转到登录页
return;
}
/* 已登录:弹出支付弹窗 */
const price = btn.dataset.price || '0';
paymentAmount.textContent = `支付金额 ¥${price}`;
modal.style.display='flex';
});
});
/* 取消/完成支付 */
cancelPayment.addEventListener('click', ()=>modal.style.display='none');
completePayment.addEventListener('click', ()=>{
alert('支付成功!');
modal.style.display='none';
});
/* 读取 cookie 工具函数 */
function getCookie(name){
return document.cookie.split(';').map(c=>c.trim())
.find(c=>c.startsWith(name+'='))?.split('=')[1] || null;
}
/* 点击遮罩空白关闭弹窗 */
modal.addEventListener('click', e=>{
if(e.target===modal) modal.style.display='none';
});
// 每秒刷新一次倒计时
setInterval(updateCountdown, 1000);
});