199 lines
7.4 KiB
JavaScript
199 lines
7.4 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 leftSpan = document.querySelector('.group-left');
|
|||
|
if (leftSpan) leftSpan.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');
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
/* --------- 给每条拼单状态前加 “仅剩 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);
|
|||
|
});
|