如何让CSS:hover触发的动画结束后元素保持最终状态
如何让CSS:hover触发的动画结束后元素保持最终状态
嗨,我完全懂你的困扰——现在鼠标移开礼盒后,所有元素都会立刻跳回初始关闭状态,而你想让它们稳稳停在动画结束的打开状态对吧?其实这个问题的解决思路很清晰,核心就是把依赖:hover伪类的临时触发逻辑,改成用自定义类来控制持久状态,这样状态就不会随着鼠标离开而消失了。下面是具体的修改步骤和完整代码:
核心修改思路
原来的:hover伪类是临时性的,鼠标离开后对应的样式就会被移除,所以元素会回到初始状态。我们改用一个自定义类(比如.open)来触发动画和最终样式,一旦这个类被添加到元素上,就会一直保留,从而让元素维持动画结束后的状态。
具体修改步骤
- 调整CSS选择器:把所有
.box-body:hover开头的选择器,替换成.box-body.open,这样当元素拥有.open类时,就会应用动画和最终样式。同时保留动画的forwards属性,确保动画结束后停在最后一帧。 - 添加JavaScript交互:给礼盒元素添加点击/hover事件,在触发动画后添加
.open类,让状态固定下来。考虑到移动端已经是点击触发,我们统一用点击事件来实现跨端一致的体验,桌面端用户也可以通过点击来保持打开状态。
修改后的完整代码
HTML部分
<div class="container"> <div class="row"> <div class="col-12"> <h3 class="text-center text-light my-5"><strong>点击或hover打开礼盒</strong></h3> </div> <div class="col-12 mt-5 d-flex justify-content-center"> <div class="box"> <div class="box-body"> <img class="img" src="https://via.placeholder.com/150"> <div class="box-lid"> <div class="box-bowtie"></div> </div> </div> </div> </div> </div> </div>
CSS部分
body { background: black; } .box { position: relative; } .box::before { content: ""; width: 440px; height: 440px; background-color: #89cff0; position: absolute; z-index: -1; top: 50%; left: 50%; -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%); border-radius: 50%; } .box-body { position: relative; height: 200px; width: 200px; margin-top: 123.3333333333px; background-color: #cc231e; border-bottom-left-radius: 5%; border-bottom-right-radius: 5%; box-shadow: 0px 4px 8px 0px rgba(0, 0, 0, 0.3); background: linear-gradient(#762c2c,#ff0303); cursor: pointer; /* 提前加上光标样式,不用等hover */ } .box-body .img{ opacity: 0; transform: translateY(0%); /* 修正原代码里的拼写错误ransform */ transition: all 0.5s; margin: 0 auto; display: block; } /* 把hover替换成.open类 */ .box-body.open { -webkit-animation: box-body 1s forwards ease-in-out; animation: box-body 1s forwards ease-in-out; } .box-body.open .img{ opacity: 1; z-index: 0; transform: translateY(-157px); } .box-body.open .box-lid { -webkit-animation: box-lid 1s forwards ease-in-out; animation: box-lid 1s forwards ease-in-out; } .box-body.open .box-bowtie::before { -webkit-animation: box-bowtie-left 1.1s forwards ease-in-out; animation: box-bowtie-left 1.1s forwards ease-in-out; } .box-body.open .box-bowtie::after { -webkit-animation: box-bowtie-right 1.1s forwards ease-in-out; animation: box-bowtie-right 1.1s forwards ease-in-out; } .box-body::after { content: ""; position: absolute; top: 0; bottom: 0; left: 50%; -webkit-transform: translateX(-50%); transform: translateX(-50%); width: 50px; background: linear-gradient(#ffffff,#ffefa0) } .box-lid { position: absolute; z-index: 1; left: 50%; -webkit-transform: translateX(-50%); transform: translateX(-50%); bottom: 90%; height: 40px; background-color: #cc231e; height: 40px; width: 220px; border-radius: 5%; box-shadow: 0 8px 4px -4px rgba(0, 0, 0, 0.3); } .box-lid::after { content: ""; position: absolute; top: 0; bottom: 0; left: 50%; -webkit-transform: translateX(-50%); transform: translateX(-50%); width: 50px; background: linear-gradient(#ffefa0,#fff) } .box-bowtie { z-index: 1; height: 100%; } .box-bowtie::before, .box-bowtie::after { content: ""; width: 83.3333333333px; height: 83.3333333333px; border: 16.6666666667px solid white; border-radius: 50% 50% 0 50%; position: absolute; bottom: 99%; z-index: -1; } .box-bowtie::before { left: 50%; -webkit-transform: translateX(-100%) skew(10deg, 10deg); transform: translateX(-100%) skew(10deg, 10deg); } .box-bowtie::after { left: 50%; -webkit-transform: translateX(0%) rotate(90deg) skew(10deg, 10deg); transform: translateX(0%) rotate(90deg) skew(10deg, 10deg); } @-webkit-keyframes box-lid { 0%, 42% { -webkit-transform: translate3d(-50%, 0%, 0) rotate(0deg); transform: translate3d(-50%, 0%, 0) rotate(0deg); } 60% { -webkit-transform: translate3d(-85%, -230%, 0) rotate(-25deg); transform: translate3d(-85%, -230%, 0) rotate(-25deg); } 90%, 100% { -webkit-transform: translate3d(-119%, 225%, 0) rotate(-70deg); transform: translate3d(-119%, 225%, 0) rotate(-70deg); } } @keyframes box-lid { 0%, 42% { -webkit-transform: translate3d(-50%, 0%, 0) rotate(0deg); transform: translate3d(-50%, 0%, 0) rotate(0deg); } 60% { -webkit-transform: translate3d(-85%, -230%, 0) rotate(-25deg); transform: translate3d(-85%, -230%, 0) rotate(-25deg); } 90%, 100% { -webkit-transform: translate3d(-119%, 225%, 0) rotate(-70deg); transform: translate3d(-119%, 225%, 0) rotate(-70deg); } } @-webkit-keyframes box-body { 0% { -webkit-transform: translate3d(0%, 0%, 0) rotate(0deg); transform: translate3d(0%, 0%, 0) rotate(0deg); } 25% { -webkit-transform: translate3d(0%, 25%, 0) rotate(20deg); transform: translate3d(0%, 25%, 0) rotate(20deg); } 50% { -webkit-transform: translate3d(0%, -15%, 0) rotate(0deg); transform: translate3d(0%, -15%, 0) rotate(0deg); } 70% { -webkit-transform: translate3d(0%, 0%, 0) rotate(0deg); transform: translate3d(0%, 0%, 0) rotate(0deg); } } @keyframes box-body { 0% { -webkit-transform: translate3d(0%, 0%, 0) rotate(0deg); transform: translate3d(0%, 0%, 0) rotate(0deg); } 25% { -webkit-transform: translate3d(0%, 25%, 0) rotate(20deg); transform: translate3d(0%, 25%, 0) rotate(20deg); } 50% { -webkit-transform: translate3d(0%, -15%, 0) rotate(0deg); transform: translate3d(0%, -15%, 0) rotate(0deg); } 70% { -webkit-transform: translate3d(0%, 0%, 0) rotate(0deg); transform: translate3d(0%, 0%, 0) rotate(0deg); } } @-webkit-keyframes box-bowtie-right { 0%, 50%, 75% { -webkit-transform: translateX(0%) rotate(90deg) skew(10deg, 10deg); transform: translateX(0%) rotate(90deg) skew(10deg, 10deg); } 90%, 100% { -webkit-transform: translate(-50%, -15%) rotate(45deg) skew(10deg, 10deg); transform: translate(-50%, -15%) rotate(45deg) skew(10deg, 10deg); box-shadow: 0px 4px 8px -4px rgba(0, 0, 0, 0.3); } } @keyframes box-bowtie-right { 0%, 50%, 75% { -webkit-transform: translateX(0%) rotate(90deg) skew(10deg, 10deg); transform: translateX(0%) rotate(90deg) skew(10deg, 10deg); } 90%, 100% { -webkit-transform: translate(-50%, -15%) rotate(45deg) skew(10deg, 10deg); transform: translate(-50%, -15%) rotate(45deg) skew(10deg, 10deg); box-shadow: 0px 4px 8px -4px rgba(0, 0, 0, 0.3); } } @-webkit-keyframes box-bowtie-left { 0% { -webkit-transform: translateX(-100%) rotate(0deg) skew(10deg, 10deg); transform: translateX(-100%) rotate(0deg) skew(10deg, 10deg); } 50%, 75% { -webkit-transform: translate(-50%, -15%) rotate(45deg) skew(10deg, 10deg); transform: translate(-50%, -15%) rotate(45deg) skew(10deg, 10deg); } 90%, 100% { -webkit-transform: translateX(-100%) rotate(0deg) skew(10deg, 10deg); transform: translateX(-100%) rotate(0deg) skew(10deg, 10deg); } } @keyframes box-bowtie-left { 0% { -webkit-transform: translateX(-100%) rotate(0deg) skew(10deg, 10deg); transform: translateX(-100%) rotate(0deg) skew(10deg, 10deg); } 50%, 75% { -webkit-transform: translate(-50%, -15%) rotate(45deg) skew(10deg, 10deg); transform: translate(-50%, -15%) rotate(45deg) skew(10deg, 10deg); } 90%, 100% { -webkit-transform: translateX(-100%) rotate(0deg) skew(10deg, 10deg); transform: translateX(-100%) rotate(0deg) skew(10deg, 10deg); } }
JavaScript部分
const boxBody = document.querySelector('.box-body'); // 点击触发打开状态,同时支持桌面端hover触发(可选) boxBody.addEventListener('click', () => { // 只添加一次类,避免重复触发动画 if (!boxBody.classList.contains('open')) { boxBody.classList.add('open'); } }); // 可选:桌面端hover时也触发,鼠标移开不关闭 boxBody.addEventListener('mouseenter', () => { if (!boxBody.classList.contains('open')) { boxBody.classList.add('open'); } });
额外说明
- 我还修正了你CSS里的一个小拼写错误:
.box-body .img里的ransform改成了transform,不然图片的过渡效果会失效。 - 现在不管是点击还是hover(桌面端),礼盒动画完成后都会保持打开状态,鼠标移开也不会回去,和移动端的体验一致啦。
备注:内容来源于stack exchange,提问作者Erick
相关产品推荐
相关产品推荐

