206 lines
7.6 KiB
JavaScript
206 lines
7.6 KiB
JavaScript
// 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人在抢,参与可立即拼成” --------- */
|
||
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');
|
||
}
|
||
});
|
||
}
|
||
|
||
/* --------- 动态填充“已抢 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);
|
||
});
|