练习小项目7:天气状态切换器②
这是上一篇 练习小项目7:天气状态切换器-CSDN博客的扩展
扩展新功能:
- 自动轮播天气状态
- 添加“暂停/恢复轮播”按钮
- 文字添加淡入淡出动画
🧠 思路提示:
添加“暂停/恢复轮播”按钮
-
使用
setInterval
每隔几秒执行一次更新。 -
i
是当前的天气索引,通过(i + 1) % weatherData.length
实现循环。
添加“暂停/恢复轮播”按钮
-
新增按钮:在 HTML 中添加一个按钮,比如文字是“暂停”。
-
用变量保存定时器 ID:调用
setInterval
时会返回一个定时器 ID,存下来(如:let timer = setInterval(...)
)。 -
切换状态:
-
如果正在播放(有定时器),点击按钮时:
-
clearInterval(timer)
清除定时器; -
按钮文字改成“恢复”;
-
状态变量(比如
isPlaying
)设为false
。
-
-
如果已暂停(没有定时器),点击按钮时:
-
重新调用
setInterval()
启动轮播; -
按钮文字改成“暂停”;
-
isPlaying
设为true
。
-
-
文字添加淡入淡出动画
页面结构(HTML 参考):
<div class="container"><h2 id="weatherText" class="fade">☀️ 晴天</h2><button id="changeBtn">切换天气</button><button id="pauseOrPlayBtn">暂停</button></div>
实践代码如下:
JS:
const weatherText = document.getElementById('weatherText')
const changeBtn = document.getElementById('changeBtn')
const pauseOrPlayBtn = document.getElementById('pauseOrPlayBtn')const weatherData = [{ icon: "☀️", text: "晴天", class: "sunny" },{ icon: "☁️", text: "多云", class: "cloudy" },{ icon: "🌧️", text: "雨天", class: "rainy" }
]let timer = null
let isPlay = truepauseOrPlayBtn.addEventListener('click', () => {isPlay = !isPlaypauseOrPlayBtn.textContent = !isPlay ? '恢复' : '暂停'if (!isPlay) {clearInterval(timer)} else {clearInterval(timer) // 避免重复timer = setInterval(() => {updateWeather((i + 1) % weatherData.length)}, 3000)}
})let i = 0
const updateWeather = (index) => {// 添加 fade-out 类,触发淡出weatherText.classList.add('fade-out')setTimeout(() => {// 更新文本weatherText.textContent = `${weatherData[index].icon} ${weatherData[index].text}`// 移除所有旧的 classweatherData.forEach(weather => {document.body.classList.remove(weather.class)})// 添加新的 classdocument.body.classList.add(weatherData[index].class)// 淡入weatherText.classList.remove('fade-out') // 变回 fade 状态}, 500)// 更新全局索引 ii = index
}
changeBtn.addEventListener('click', () => {// 每次调用时,传入下一个索引updateWeather((i + 1) % weatherData.length)
})timer = setInterval(() => {updateWeather((i + 1) % weatherData.length)
}, 3000);// 初始设置
updateWeather(0)
// 让界面初始化时更清晰,而不是说你不用设置按钮文字。而是要“主动设置一次”初始文字,不要依赖 HTML 里写死或者默认值
pauseOrPlayBtn.textContent = '暂停'
CSS:
body {transition: background-color 0.3s;font-family: sans-serif;text-align: center;padding-top: 50px;}.sunny {background-color: #ffe066;color: #333;}.cloudy {background-color: #d0d0d0;color: #333;}.rainy {background-color: #7f8fa6;color: white;}button {margin-top: 20px;padding: 10px 20px;}.fade {opacity: 1;transition: opacity 0.5s ease;}.fade-out {opacity: 0;}
页面效果展示 :
额外知识记录:
✅为什么要在初始代码updateWeather(0)后设置pauseOrPlayBtn.textContent = '暂停' 初始化按钮的文字?
updateWeather(0)
pauseOrPlayBtn.textContent = '暂停'
- 当网页刚加载时(在点击按钮之前),按钮上的文字是靠 HTML 中写的内容决定的。
- 如果你忘记在 HTML 中写上默认文字,比如
<button id="pauseOrPlayBtn">暂停</button>
,那这个按钮一开始可能是空的。 - 所以为了保证按钮在加载时总是显示正确的文字,我们可以在 JS 的初始化代码里写一行pauseOrPlayBtn.textContent = '暂停'。
建议的本意是为了让界面初始化时更清晰,而不是说你不用设置按钮文字。而是要“主动设置一次”初始文字,不要依赖 HTML 里写死或者默认值
✅实现淡入淡出动画效果时,为什么要使用 setTimeout ?
如果你不使用 setTimeout
,会发生这样的事:
weatherText.classList.add('fade-out')
weatherText.textContent = "☁️ 多云" // 🔴 马上就换文字了!
weatherText.classList.remove('fade-out')
因为 JavaScript 执行太快,页面还没来得及显示“淡出”的过程,文字就已经换掉了,然后马上又“淡入”,视觉上就根本没有“淡出”的效果。所以使用 setTimeout
,延迟文字替换。
步骤 | 发生时机 | 说明 |
---|---|---|
添加 .fade-out | 立刻执行 | 开始慢慢变透明 |
setTimeout(...) | 延迟 500ms 后执行 | 内容已经淡出,此时换内容 |
移除 .fade-out | 在 setTimeout 里 | 开始淡入,慢慢显示新内容 |
扩展一下:
setTimeout
和 void element.offsetWidth(在
练习小项目3:随机正能量语录生成器-CSDN博客中使用到)
都是用来“强制浏览器触发样式重绘(reflow)或重排(repaint)”。但是不同的原理和机制。
1. void element.offsetWidth
的原理
-
访问
element.offsetWidth
是读取元素的布局信息,浏览器会强制“同步计算”最新的布局状态,这就触发了浏览器的 回流(reflow)。 -
通过
void
取值(void element.offsetWidth
)的写法,只是为了读取属性值而不使用它,目的是强制浏览器先完成这一步的计算。 -
常用在:当你动态修改了元素的样式或类名后,想强制浏览器“立刻应用变化”,然后再执行下一步操作,比如添加动画类,从而确保动画能被正确触发。
2. setTimeout(fn, 0)
的原理
-
setTimeout
是把任务放到 事件队列的宏任务队列,在当前所有同步代码执行完后才执行。 -
用它来“分割执行步骤”,让浏览器有机会先渲染、更新页面,然后再执行定时器中的代码。
-
作用相当于“延迟执行”,间隔大约是几毫秒,保证样式修改和布局更新能被浏览器先处理。
区别总结
方式 | 作用机制 | 触发时机 | 适用场景 |
---|---|---|---|
void element.offsetWidth | 读取元素布局属性,触发同步回流 | 立即同步触发,阻塞后续代码 | 想立即强制刷新布局状态,配合CSS动画 |
setTimeout(fn, 0) | 异步延迟执行,放入宏任务队列 | 等同步代码执行完成后再执行 | 想分开两步执行,给浏览器时间渲染页面 |
举个简单比喻
-
void element.offsetWidth
就像你“马上打断别人,让他先确认一下现在状态”,同步立刻得到确认。 -
setTimeout(fn, 0)
就像说:“先让他忙完手头的事,等下再去问”,异步排队。