AlpineJS倒计时异常:显示负值而非剩余时长问题求助
AlpineJS电商发货倒计时负数值问题修复
问题描述
基于AlpineJS开发的电商发货倒计时功能,需求为:
- 读取后台设置的发货时间,展示距离该时间的剩余时长
- 当后台设置发货时间为16:00、当前时间为15:00时,显示
59m 59s - 时间到达16:00后,自动切换为下一个发货日的倒计时(如
23h 59m 59s)继续运行
功能初期可用,但运行约1小时后,会显示自16:00起流逝的秒数负值,而非正常剩余时长。原代码如下:
window.shippingAlert = ( days, time, offdays, ) => ({ days: [], offdays: [], offDates: [], hour: '', minute: '', nextShipmentUnix: null, timeLeftString: "", init() { this.days = days.replace(7, 0).split(',').map((x) => parseInt(x)).sort(); // replace 7 (sunday) with 0 to match JS dates. let offDaysArray = offdays.split(','); this.offDates = []; for (let i = 0; i < offDaysArray.length; i++) { let dateParts = offDaysArray[i].split("-"); let year = parseInt(dateParts[0]); let month = parseInt(dateParts[1]) - 1; let day = parseInt(dateParts[2]); this.offDates.push(new Date(year, month, day)); } console.log(this.offDates) let timeParts = time.split(':'); this.hour = parseInt(timeParts[0]); this.minute = parseInt(timeParts[1] || 0); this.calculateNextShipment(); this.calculateTimeLeft(); window.setInterval(() => this.calculateTimeLeft(), 1000); }, calculateNextShipment() { let currentDate = new Date(); let currentDay = currentDate.getDay(); let currentHour = currentDate.getHours(); let currentMinute = currentDate.getMinutes(); let nextDay = currentDay; // If time is passed, move to next days if (nextDay > this.hour || (currentHour === this.hour && currentMinute >= this.minute)) { nextDay = nextDay === 6 ? 0 : (nextDay + 1); } // If no shipments occur on the day, move on until the next available day if (!this.days.includes(nextDay)) { nextDay = this.days.filter((day) => day > nextDay)?.[0] || this.days[0]; } // If there are specific dates where shipments do not occur let nextDate = new Date(); let isOffDate = true; while (isOffDate) { nextDate.setDate(nextDate.getDate() + (nextDay >= currentDay ? (nextDay - currentDay) : (7 - currentDay + nextDay))); nextDate.setHours(this.hour); nextDate.setMinutes(this.minute); nextDate.setSeconds(0); isOffDate = this.offDates.some((offDate) => offDate.getDay() === nextDate.getDay()); if (isOffDate) { nextDay = this.days.filter((day) => day > nextDay)?.[0] || this.days[0]; } } console.log(nextDate); this.nextShipmentUnix = Math.floor(nextDate.getTime() / 1000); }, calculateTimeLeft() { let currentTimeUnix = Math.floor(new Date().getTime() / 1000); let secondsLeft = this.nextShipmentUnix - currentTimeUnix; if (secondsLeft <= 0) { this.calculateNextShipment(); currentTimeUnix = Math.floor(new Date().getTime() / 1000); // Update the current time secondsLeft = this.nextShipmentUnix - currentTimeUnix; } let parts = []; let days = Math.floor(secondsLeft / 86400); if (days > 0) { secondsLeft = secondsLeft - (days * 86400); parts.push(days + 'd'); } let hours = Math.floor(secondsLeft / 3600); if (hours > 0) { secondsLeft = secondsLeft - (hours * 3600); parts.push(hours + 't'); } let minutes = Math.floor(secondsLeft / 60); if (minutes > 0) { secondsLeft = secondsLeft - (minutes * 60); parts.push(minutes + 'm'); } parts.push(secondsLeft + 's'); this.timeLeftString = parts.join(' '); }, })
问题根源
- 时间判断逻辑完全错误:原代码用
nextDay(星期几,0-6)> this.hour(小时数,如16)判断时间是否已过,类型不匹配导致逻辑失效,计算出的nextShipmentUnix指向错误时间点。 - 节假日判断逻辑错误:用
offDate.getDay() === nextDate.getDay()判断节假日,仅比较星期几而非完整日期,错误跳过所有与节假日星期几相同的正常发货日。 - 剩余秒数检查不严谨:仅用
if判断一次剩余秒数,极端情况下重新计算发货时间后仍可能出现负值。
修复后的完整代码
window.shippingAlert = ( days, time, offdays, ) => ({ days: [], offDates: [], hour: '', minute: '', nextShipmentUnix: null, timeLeftString: "", init() { // 替换周日标识7为0,匹配JS星期格式并排序 this.days = days.replace(7, 0).split(',').map(x => parseInt(x)).sort(); // 解析节假日为Date对象(过滤空值) this.offDates = offdays.split(',').filter(dateStr => dateStr.trim()).map(dateStr => { const [year, month, day] = dateStr.split("-").map(Number); return new Date(year, month - 1, day); }); // 解析发货时间 const timeParts = time.split(':'); this.hour = parseInt(timeParts[0]); this.minute = parseInt(timeParts[1] || 0); this.calculateNextShipment(); this.calculateTimeLeft(); window.setInterval(() => this.calculateTimeLeft(), 1000); }, calculateNextShipment() { const currentDate = new Date(); const currentDay = currentDate.getDay(); const currentHour = currentDate.getHours(); const currentMinute = currentDate.getMinutes(); let nextDay = currentDay; let deltaDays = 0; // 判断当日发货时间是否已过,若是则切换到下一天 if (currentHour > this.hour || (currentHour === this.hour && currentMinute >= this.minute)) { nextDay = nextDay === 6 ? 0 : nextDay + 1; deltaDays = 1; } // 找到下一个可用的发货星期几 if (!this.days.includes(nextDay)) { const nextAvailableDay = this.days.find(day => day > nextDay); nextDay = nextAvailableDay !== undefined ? nextAvailableDay : this.days[0]; // 计算到目标星期几的天数(含跨周情况) deltaDays = nextDay >= currentDay ? (nextDay - currentDay) : (7 - currentDay + nextDay); // 若当日已过发货时间,天数需叠加 if (currentHour > this.hour || (currentHour === this.hour && currentMinute >= this.minute)) { deltaDays = nextDay > currentDay ? (nextDay - currentDay) : (7 - currentDay + nextDay); } } // 初始化下一个发货日期 let nextDate = new Date(currentDate); nextDate.setDate(currentDate.getDate() + deltaDays); nextDate.setHours(this.hour); nextDate.setMinutes(this.minute); nextDate.setSeconds(0); // 检查是否为节假日(精确匹配年/月/日) const isOffDate = (date) => { return this.offDates.some(offDate => { return offDate.getFullYear() === date.getFullYear() && offDate.getMonth() === date.getMonth() && offDate.getDate() === date.getDate(); }); }; // 循环跳过节假日,找到下一个有效发货日 while (isOffDate(nextDate)) { const currentNextDayIndex = this.days.indexOf(nextDate.getDay()); nextDay = this.days[(currentNextDayIndex + 1) % this.days.length]; const daysToAdd = nextDay > nextDate.getDay() ? (nextDay - nextDate.getDay()) : (7 - nextDate.getDay() + nextDay); nextDate.setDate(nextDate.getDate() + daysToAdd); nextDate.setHours(this.hour); nextDate.setMinutes(this.minute); nextDate.setSeconds(0); } this.nextShipmentUnix = Math.floor(nextDate.getTime() / 1000); }, calculateTimeLeft() { let currentTimeUnix = Math.floor(new Date().getTime() / 1000); let secondsLeft = this.nextShipmentUnix - currentTimeUnix; // 循环检查,确保剩余秒数为正 while (secondsLeft <= 0) { this.calculateNextShipment(); currentTimeUnix = Math.floor(new Date().getTime() / 1000); secondsLeft = this.nextShipmentUnix - currentTimeUnix; } const parts = []; const days = Math.floor(secondsLeft / 86400); if (days > 0) { secondsLeft -= days * 86400; parts.push(`${days}d`); } const hours = Math.floor(secondsLeft / 3600); if (hours > 0) { secondsLeft -= hours * 3600; parts.push(`${hours}h`); // 原代码用't',改为通用的'h',需保留可自行改回 } const minutes = Math.floor(secondsLeft / 60); if (minutes > 0) { secondsLeft -= minutes * 60; parts.push(`${minutes}m`); } parts.push(`${secondsLeft}s`); this.timeLeftString = parts.join(' '); }, })
核心修复要点
- 修正时间判断逻辑:用
currentHour > this.hour || (currentHour === this.hour && currentMinute >= this.minute)准确判断当日发货时间是否已过。 - 修复节假日判断:精确比较日期的年、月、日,避免错误跳过正常发货日。
- 强化剩余秒数检查:用
while循环替代if,确保重新计算发货时间后剩余秒数一定为正。 - 优化发货日计算:调整跨周发货日的天数计算逻辑,确保跳转正确。
- 统一时间单位标识:将小时后缀从
t改为通用的h,符合用户认知习惯。
内容的提问来源于stack exchange,提问作者insidethehorror
相关产品推荐
相关产品推荐

