1.整体效果
https://mmbiz.qpic.cn/mmbiz_gif/iaVb9cSjKNDeLc7vbAUXJ4yFicKsd2d94ibVgYMJYz3frKeib1tibZhf78kc70gVp9EoqQw16Xzia6OialDaibic9o4iaMfg/640?wx_fmt=gif&from=appmsg&wxfrom=13&tp=wxpic
实现了一个倒计时动画效果,包括数字区域和倒计时结束区域。数字区域显示倒计时数字,数字进入时有动画效果,数字离开时也有动画效果。倒计时结束后,数字区域隐藏,倒计时结束区域显示,显示时也有动画效果。用户可以点击重新开始按钮重新开始倒计时。
2.完整代码
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>倒计时动画效果</title>
<link rel="stylesheet" type="text/css" href="6_12.css">
</head>
<body>
<div class="container">
<div class="counter">
<div class="nums">
<span class="in">3</span>
<span>2</span>
<span>1</span>
<span>0</span>
</div>
<h3>准备</h3>
</div>
<div class="final">
<h1>活动开始</h1>
<button class="replay">重来</button>
</div>
</div>
</body>
<script src="6_12.js"></script>
</html>
CSS
/* 清除所有元素的默认margin和padding */
* {
margin: 0; /* 清除外边距 */ padding: 0; /* 清除内边距 */}
/* 设置body的高度为视口高度,并使用flex布局进行居中 */body {
height: 100vh; /* 视口高度 */ display: flex; /* 弹性布局 */ justify-content: center; /* 主轴居中对齐 */ align-items: center; /* 交叉轴居中对齐 */ background: linear-gradient(to bottom, #fdfbfb, #ebedee); /* 背景线性渐变,从上到下 */}
/* 容器元素,设置为相对定位 */.container {
position: relative; /* 相对定位 */}
/* 计数器容器,宽度100%,文本居中对齐 */.counter {
width: 100%; /* 宽度100% */
text-align: center; /* 文本居中对齐 */}
/* 隐藏计数器动画 */.counter.hide {
transform: scale(0); /* 缩放到0 */
animation: hide 0.2s ease-out; /* 应用名为hide的动画,持续0.2秒,使用ease-out缓动函数 */}
/* 数字容器样式 */.nums {
width: 250px; /* 宽度250px */
height: 80px; /* 高度80px */
color: #5696ff; /* 字体颜色 */ font-size: 80px; /* 字体大小 */ font-weight: bold; /* 字体加粗 */ display: flex; /* 弹性布局 */ justify-content: center; /* 主轴居中对齐 */ align-items: center; /* 交叉轴居中对齐 */ margin-bottom: 15px; /* 下边距15px */
position: relative; /* 相对定位 */ overflow: hidden; /* 溢出内容隐藏 */}
/* 数字容器内的span元素样式 */.nums span {
position: absolute; /* 绝对定位 */ transform-origin: bottom center; /* 变换原点设置在底部中心 */ transform: rotate(120deg); /* 旋转120度 */}
/* 数字进入动画 */.nums span.in {
animation: goIn 0.5s ease-in-out; /* 应用名为goIn的动画,持续0.5秒,使用ease-in-out缓动函数 */}
/* 数字退出动画 */.nums span.out {
animation: goOut 0.5s ease-in-out; /* 应用名为goOut的动画,持续0.5秒,使用ease-in-out缓动函数 */}
/* 最终显示的内容容器样式 */.final {
position: absolute; /* 绝对定位 */ top: 0; /* 顶部对齐 */ left: 0; /* 左侧对齐 */ width: 100%; /* 宽度100% */
text-align: center; /* 文本居中对齐 */ transform: scale(0); /* 缩放到0 */
}
/* 最终显示内容显示动画 */.final.show {
transform: scale(1); /* 缩放到1 */
animation: show 0.3s ease-in; /* 应用名为show的动画,持续0.3秒,使用ease-in缓动函数 */}
/* 重播按钮样式 */.replay {
width: 100%; /* 宽度100% */
background-color: #5696ff; /* 背景颜色 */ color: #fff; /* 字体颜色 */ border: none; /* 无边框 */ padding: 10px 0; /* 上下内边距10px */
margin-top: 20px; /* 上边距20px */
cursor: pointer; /* 鼠标悬停时变为手形图标 */}
/* 隐藏动画:从正常大小逐渐缩小到0 */
@keyframes hide {
0% {
transform: scale(1); /* 动画开始时,元素为正常大小 */ }
100% {
transform: scale(0); /* 动画结束时,元素缩小到0 */
}
}
/* 显示动画:从0开始放大,稍微超过正常大小,然后回到正常大小 */@keyframes show {
0% {
transform: scale(0); /* 动画开始时,元素为0大小 */ }
80% {
transform: scale(1.4); /* 动画过程中,元素放大到1.4倍大小 */ }
100% {
transform: scale(1); /* 动画结束时,元素回到正常大小 */ }
}
/* 进入动画:数字从旋转120度逐渐恢复到0度 */@keyframes goIn {
0% {
transform: rotate(120deg); /* 动画开始时,元素旋转120度 */ }
30% {
transform: rotate(-20deg); /* 动画过程中,元素旋转到-20度 */ }
60% {
transform: rotate(10deg); /* 动画过程中,元素旋转到10度 */ }
90%,
100% {
transform: rotate(0); /* 动画结束前和结束时,元素旋转到0度 */ }
}
/* 退出动画:数字从0度逐渐旋转到-120度 */@keyframes goOut {
0%,
30% {
transform: rotate(0); /* 动画开始时和动画过程中,元素为0度 */ }
60% {
transform: rotate(20deg); /* 动画过程中,元素旋转到20度 */ }
100% {
transform: rotate(-120deg); /* 动画结束时,元素旋转到-120度 */ }
}
JavaScript
// 获取所有类名为'nums'的元素下的'span'子元素,并将它们存储在'nums'数组中
const nums = document.querySelectorAll('.nums span');
// 获取类名为'counter'的元素,并存储在'counter'变量中
const counter = document.querySelector('.counter');
// 获取类名为'final'的元素,并存储在'final'变量中
const final = document.querySelector('.final');
// 获取类名为'replay'的元素,并存储在'replay'变量中
const replay = document.querySelector('.replay');
// 重置函数:将界面重置为初始状态
function reset() {
// 移除'counter'元素上的'hide'类,使其可见
counter.classList.remove('hide');
// 移除'final'元素上的'show'类,使其隐藏
final.classList.remove('show');
// 遍历'nums'数组中的每个元素
nums.forEach(num => {
// 移除当前'span'元素上的所有类名
num.className = '';
});
// 给'nums'数组中的第一个'span'元素添加'in'类,使其开始进入动画
nums[0].classList.add('in');
}
// 运行函数:为'nums'数组中的每个'span'元素添加动画结束事件监听器
function run() {
nums.forEach((num, index) => {
// 当动画结束时触发此函数
num.addEventListener('animationend', e => {
// 如果动画名称为'goIn'且当前元素不是最后一个元素
if (e.animationName === 'goIn' && index !== nums.length - 1) {
// 移除当前元素的'in'类,使其停止进入动画
num.classList.remove('in');
// 给当前元素添加'out'类,使其开始退出动画
num.classList.add('out');
}
// 如果动画名称为'goOut'且当前元素有下一个兄弟元素
else if (e.animationName === 'goOut' && num.nextElementSibling) {
// 给下一个兄弟元素添加'in'类,使其开始进入动画
num.nextElementSibling.classList.add('in');
}
// 如果以上条件都不满足(即最后一个元素动画结束)
else {
// 隐藏'counter'元素
counter.classList.add('hide');
// 显示'final'元素
final.classList.add('show');
}
})
})
}
// 当'replay'元素被点击时,触发重置和运行函数
replay.addEventListener('click', function () {
reset();
run();
});
// 页面加载完成后立即运行run函数
run();