JS切换CSS动画实现字母悬停旋转变色时第二次动画失效如何解决
问题原因
- CSS语法存在错误:
@keyframes定义结束后多余的分号、括号闭合逻辑异常,导致unrotate动画没有被浏览器正确解析。 - 动画触发逻辑缺陷:直接切换元素的动画类名时,浏览器会默认保留元素之前的动画完成态,不会重新触发新动画,需要强制触发重排重置动画状态。
- 非标准属性兼容问题:你使用的
ind.path[0]属于非标准属性,部分浏览器无法正常识别,会导致获取元素失败。
修复代码
修正后CSS代码
@import url("https://fonts.googleapis.com/css2?family=Luckiest+Guy&family=Roboto:wght@400;700&display=swap"); :root { --font-color: rgb(243, 14, 224); } .landing-text { font-family: "Luckiest Guy", cursive; flex-wrap: wrap; height: 100vh; font-size: 6vw; margin-top: 40vh; margin-left: 4rem; } .landing-text div{ display: flex; max-height: 120px; } /* 统一span默认样式,避免动画跳变 */ .landing-text span { cursor: pointer; transform: rotateY(0deg); color: #000; } .rotate { animation: rotate 2s forwards; } @keyframes rotate { 100% { transform: rotateY(720deg); color: var(--font-color); } } .unrotate { animation: unrotate 2s forwards; } @keyframes unrotate { 100%{ transform: rotateY(720deg); color: black; } }
修正后JS代码
const landingEl = document.getElementById("landing"); const landingText = document.getElementById("landing-text"); const landingFraze = `Lorem ipsum dolor sit amet consectetur adipisicing elit.`; const spans = document.getElementsByTagName('span'); let letterList = []; let text; function rotate(e) { // 用标准属性直接获取触发事件的元素 const spiningLeter = e.currentTarget; let oldClass = '' // 先移除旧动画类 if(spiningLeter.classList.contains('rotate')) { oldClass = 'rotate' spiningLeter.classList.remove('rotate') } else if(spiningLeter.classList.contains('unrotate')) { oldClass = 'unrotate' spiningLeter.classList.remove('unrotate') } // 强制触发重排,重置动画状态 void spiningLeter.offsetWidth; // 切换新动画类 spiningLeter.classList.add(oldClass === 'rotate' || oldClass === '' ? 'unrotate' : 'rotate') } function everyLeter() { let text = landingFraze; let sentenceList = text.split("."); sentenceList.forEach((element, i) => { if(!element.trim()) return // 过滤分割产生的空字符串 let div = document.createElement('div'); let list = element.split(""); list.forEach((leter, index) => { let span = document.createElement("span"); span.id = `${index}a${i}`; span.addEventListener('mouseenter', rotate); let leterP = document.createElement('p'); if(leter === ' ') { leterP.style.marginLeft = '20px' } if(list.length === index + 1){ leterP.textContent = `${leter}.`; } else { leterP.textContent = leter; } span.appendChild(leterP); div.appendChild(span); }); landingText.appendChild(div); }); } everyLeter();
核心修复说明
- 清理了CSS语法错误,保证两个动画都能被浏览器正常识别
- 移除旧动画类后通过
void 元素.offsetWidth触发浏览器重排,清空之前的动画缓存,保证新动画可以正常触发 - 替换非标准的
event.path为标准的event.currentTarget,兼容全量浏览器 - 过滤了字符串分割产生的空值,避免生成多余的空DOM节点
内容的提问来源于stack exchange,提问作者Dmitrij Beleckij
相关产品推荐
相关产品推荐

