如何用原生JavaScript实现类VueJS的过渡效果?
解决原生JS模拟Vue过渡时淡入失效的问题
我来帮你搞定这个淡入没效果的问题!你的淡出逻辑是对的,但淡入时的DOM操作时机出了问题——浏览器会把连续的样式变更合并成一次重绘,直接跳过了过渡需要的初始透明状态,导致过渡完全没触发。
问题根源
当你点击show按钮时,你连续做了这几步:
- 移除
hide类(元素从display:none变成默认的display:flex) - 添加
div-enter类(opacity:0) - 立刻替换成
div-enter-to类(opacity:1)
浏览器会把这三个操作合并成一次渲染,直接让元素以opacity:1显示,完全没有过渡的过程,自然也不会触发transitionend事件。
修复方案
我们需要给浏览器一个“缓冲时间”,让它先渲染出元素显示且透明的状态,再切换到完全可见的状态。用requestAnimationFrame就能完美实现这个效果(它会在浏览器下一次重绘前执行回调)。
修改后的完整代码
HTML(无需改动)
<button id="btnShow">show</button> <button id="btnHide">hide</button> <div> fade out, then set display: none<br /> then do the reverse... </div>
CSS(优化过渡目标,移除无效属性)
.hide { display: none; } /* 移除opacity:0,因为display:none时它无效 */ .div-enter { opacity: 0; } .div-enter-to { opacity: 1; } .div-leave { opacity: 1; } .div-leave-to { opacity: 0; } /* 原有样式保留,优化过渡属性 */ html, body { height: 100vh; width: 100vw; margin: 0; padding: 1em 1.5em; font-size: 100%; } div { display: flex; align-items: center; justify-content: center; flex-direction: column; margin: 1em 0; padding: 3rem 0; width: 50%; background: #4bfa; border-radius: 5px; transition: opacity 1s linear; /* 只针对opacity过渡,提升性能 */ } button { border: none; border-radius: 2px; } button:hover { cursor: pointer; }
JavaScript(调整淡入逻辑的时机)
const btnShow = document.querySelector('#btnShow'); const btnHide = document.querySelector('#btnHide'); const div = document.querySelector('div'); btnShow.addEventListener('click', () => { console.log('btnShow clicked'); div.classList.remove('hide'); // 先设置初始透明状态 div.classList.add('div-enter'); // 让浏览器先渲染初始状态,再触发过渡 requestAnimationFrame(() => { div.classList.replace('div-enter', 'div-enter-to'); }); div.addEventListener('transitionend', function() { console.log('show transition ended'); div.classList.remove('div-enter-to'); }, { once: true }); }); btnHide.addEventListener('click', () => { console.log('btnHide clicked'); div.classList.add('div-leave'); div.classList.replace('div-leave', 'div-leave-to'); div.addEventListener('transitionend', () => { console.log('hide transition ended'); div.classList.replace('div-leave-to', 'hide'); }, { once: true }); });
关键改动说明
- CSS优化:把过渡从
all改成opacity,避免其他属性(比如布局相关)意外触发过渡,同时移除hide类里无效的opacity:0。 - JS淡入逻辑:用
requestAnimationFrame延迟切换到div-enter-to类,让浏览器有时间渲染出元素显示但透明的状态,这样过渡动画就能正常触发了。
现在点击show按钮,元素会从透明慢慢淡入,也能正常触发transitionend事件啦!
内容的提问来源于stack exchange,提问作者user1960364
相关产品推荐
相关产品推荐

