为已应用CSS变换的元素添加抖动动画时丢失原有变换的问题
卡片抖动丢失翻转状态问题修复
问题背景
我正在制作卡片交互效果作为多个项目的基础,在实现卡片抖动效果时遇到问题:已翻转的卡片在触发抖动动画后,会丢失之前的翻转状态。
操作步骤:
- 点击两张卡片完成翻转
- 选中其中一张(会上移)
- 再点击另一张触发抖动(表示未被选中),此时卡片会显示背面而非正面
我尝试过调整z-index、opacity、display属性,也尝试在动画中重复设置rotateY(180deg),最接近的方案是使用:not选择器,但仅能抖动卡片内的文本而非整个卡片。
问题代码
HTML
<body> <form onsubmit="return false"> <label for="selectLimit"> Select Limit:</label> <input id="numSelectLimit" name="selectLimit" type="number" min="1" max="100" value="1" oninput="onInput()"> </form> <div class="card"> <div class="cardInner" onclick="clickFlip4(this)"> <div class="cardBack"> <p>BACK</p> </div> <div class="cardFront"> <p>FRONT</p> </div> </div> </div> <div class="card"> <div class="cardInner" onclick="clickFlip4(this)"> <div class="cardBack"> <p>BACK</p> </div> <div class="cardFront"> <p>FRONT</p> </div> </div> </div> </body>
CSS
.card { background-color: transparent; width: 275px; height: 400px; perspective: 1000px; display: inline-block; padding: 65px 3px; } .cardInner { position: relative; width: 100%; height: 100%; text-align: center; transition: transform 0.8s; transform-style: preserve-3d; border-radius: 15px; } .cardFlipped { transform: rotateY(180deg); } .cardBack, .cardFront { position: absolute; width: 100%; height: 100%; -webkit-backface-visibility: hidden; backface-visibility: hidden; border-radius: 15px; border: 3px solid #000000; } .cardBack { background-color: #ffffff; color: black; } .cardFront { background-color: dodgerblue; color: white; transform: rotateY(180deg); } .selected, .selected2, .selected3 { margin-top: -60px; } form, label, input { font: inherit; } .shake { animation: horizontal-shaking 0.5s; } @keyframes horizontal-shaking { 0% { transform: translateX(0) } 20% { transform: translateX(5px) } 40% { transform: translateX(-5px) } 60% { transform: translateX(5px) } 80% { transform: translateX(-5px) } 100% { transform: translateX(0) } }
JavaScript
let selectLimit = parseInt(document.getElementById("numSelectLimit").value); let currentlySelected = []; function onInput() { selectLimit = parseInt(document.getElementById("numSelectLimit").value); } function clickFlip4(element) { element.classList.toggle("cardFlipped"); element.setAttribute("onclick", "clickSelect3(this)"); } function clickSelect3(element) { if (element.classList.contains("selected3")) { var newArray = []; for (var i = 0; i < currentlySelected.length; i++) { if (currentlySelected[i] === element) continue; else newArray[newArray.length] = element; } currentlySelected = newArray; element.classList.toggle("selected3"); } else { if (currentlySelected.length !== selectLimit) { currentlySelected[currentlySelected.length] = element; element.classList.toggle("selected3"); } else { element.classList.toggle("shake"); setTimeout(function() { element.classList.toggle("shake"); }, 500); } } }
解决方案
问题根源:抖动动画的transform会覆盖.cardFlipped类中的rotateY(180deg)——CSS动画的transform优先级更高,且会重置之前的变换值。
方案1:适配翻转状态的双动画
为已翻转的卡片单独定义包含旋转变换的抖动动画:
/* 已翻转卡片的抖动动画 */ .cardFlipped.shake { animation: flipped-horizontal-shaking 0.5s; } /* 未翻转卡片的抖动动画 */ @keyframes horizontal-shaking { 0% { transform: translateX(0); } 20% { transform: translateX(5px); } 40% { transform: translateX(-5px); } 60% { transform: translateX(5px); } 80% { transform: translateX(-5px); } 100% { transform: translateX(0); } } /* 已翻转卡片的抖动动画,保留rotateY(180deg) */ @keyframes flipped-horizontal-shaking { 0% { transform: rotateY(180deg) translateX(0); } 20% { transform: rotateY(180deg) translateX(5px); } 40% { transform: rotateY(180deg) translateX(-5px); } 60% { transform: rotateY(180deg) translateX(5px); } 80% { transform: rotateY(180deg) translateX(-5px); } 100% { transform: rotateY(180deg) translateX(0); } }
方案2:CSS变量动态复用变换值
更灵活的方式,用CSS变量存储翻转状态,动画中自动复用:
- 修改卡片基础样式和翻转类:
.cardInner { --card-rotation: none; position: relative; width: 100%; height: 100%; text-align: center; transition: transform 0.8s; transform-style: preserve-3d; border-radius: 15px; } .cardFlipped { --card-rotation: rotateY(180deg); transform: var(--card-rotation); }
- 修改抖动动画:
.shake { animation: horizontal-shaking 0.5s; } @keyframes horizontal-shaking { 0% { transform: var(--card-rotation) translateX(0); } 20% { transform: var(--card-rotation) translateX(5px); } 40% { transform: var(--card-rotation) translateX(-5px); } 60% { transform: var(--card-rotation) translateX(5px); } 80% { transform: var(--card-rotation) translateX(-5px); } 100% { transform: var(--card-rotation) translateX(0); } }
额外优化:简化JS数组操作
移除选中元素的逻辑可以简化为:
function clickSelect3(element) { if (element.classList.contains("selected3")) { // 用filter简化数组过滤 currentlySelected = currentlySelected.filter(item => item !== element); element.classList.toggle("selected3"); } else { if (currentlySelected.length !== selectLimit) { currentlySelected.push(element); element.classList.toggle("selected3"); } else { element.classList.toggle("shake"); setTimeout(() => { element.classList.toggle("shake"); }, 500); } } }
内容的提问来源于stack exchange,提问作者Peter H
相关产品推荐
相关产品推荐

