50天50个小项目系列文章-(Day1)👽
做这个系列文章的目的是为了提高自己的前端基础水平,毕竟前端基础三件套是重中之重吗😉,下面是我的GitHub地址和这个小demo对应的代码仓库。
1.技术栈
- Typescript
- sass
- React
2.项目介绍
AnimatedCountdown:具有动画效果的计时器
实现效果
3.代码实现
3.1sass部分
.h4font {
font-size: 20px;
margin: 5px;
//字母大写
text-transform: uppercase;
color: black;
}
.count {
//绝对定位并且居中
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
.nums {
color: #3498db;
font-size: 50px;
position: relative;
overflow: hidden;
width: 250px;
height: 50px;
//rotate()函数表示旋转,因为父元素设置了overflow,所以超出height的部分不可见
span {
position: absolute;
top: 50%;
left: 50%;
//rotate(120deg);使得数字旋转120°
transform: translate(-50%, -50%) rotate(120deg);
transform-origin: bottom center;
}
//in类选择器定义了数字由初始状态到显示的动画过程
.in {
transform: translate(-50%, -50%) rotate(0deg);
animation: goIn 0.5s ease-in-out;
}
//out类选择器定义了数字由显示到不可见最终状态的动画过程。
.out {
animation: goOut 0.5s ease-in-out;
}
//动画关键帧,使得动画效果更丝滑
@keyframes goIn {
0% {
transform: translate(-50%, -50%) rotate(120deg);
}
30% {
transform: translate(-50%, -50%) rotate(-20deg);
}
60% {
transform: translate(-50%, -50%) rotate(10deg);
}
100% {
transform: translate(-50%, -50%) rotate(0deg);
}
}
@keyframes goOut {
0% {
transform: translate(-50%, -50%) rotate(0deg);
}
60% {
transform: translate(-50%, -50%) rotate(20deg);
}
100% {
transform: translate(-50%, -50%) rotate(-120deg);
}
}
}
}
//reply按钮的消失动画效果
.hide {
transform: translate(-50%, -50%) scale(0);
animation: hiden 0.2s ease-out;
@keyframes hiden {
0% {
transform: translate(-50%, -50%) scale(1);
}
100% {
transform: translate(-50%, -50%) scale(0);
}
}
}
.final {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0);
text-align: center;
}
.show {
transform: translate(-50%, -50%) scale(1);
animation: show 0.2s ease-out;
}
@keyframes show {
0% {
transform: translate(-50%, -50%) scale(0);
}
30% {
transform: translate(-50%, -50%) scale(1.4);
}
100% {
transform: translate(-50%, -50%) scale(1);
}
}
.replay {
background-color: #3498db;
border-radius: 3px;
border: none;
color: aliceblue;
padding: 5px;
text-align: center;
display: inline-block;
cursor: pointer;
transition: all 0.3s;
span {
cursor: pointer;
display: inline-block;
position: relative;
transition: 0.3s;
}
span::after {
content: '\00bb';
position: absolute;
opacity: 0;
top: 0;
right: -20px;
transition: 0.5s;
}
}
.replay:hover {
span {
padding-right: 25px;
}
span:after {
opacity: 1;
right: 0;
}
}
3.2react部分
import React, { useState } from 'react';
import classNames from 'classnames';
import styles from './index.module.scss';
interface props {
num: number,
}
const AnimatedCountDown = (props: props) => {
const [num, setNum] = useState(props.num);
const [instatus, setInStatus] = useState(true);
const [outStatus, setOutStatus] = useState(false);
const [hide, setHide] = useState(false);
const animation = (e: React.AnimationEvent<HTMLSpanElement>) => {
if (e.animationName === styles.goIn) {
setInStatus(false);
setOutStatus(true);
if (num === 0) {
setHide(true);
}
}
else {
setInStatus(true);
setOutStatus(false);
setNum(num - 1);
}
}
return (
<>
<div className={classNames({ [styles.count]: !hide }, { [styles.hide]: hide })}>
<div className={styles.nums}>
<span
onAnimationEnd={(e) => { animation(e) }}
className={classNames({ [styles.out]: outStatus }, { [styles.in]: instatus })}
>
{num}
</span>
</div>
<h4 className={styles.h4font}>Get Ready</h4>
</div>
<div className={classNames(styles.final, { [styles.show]: hide })}>
<h1>GO</h1>
<button className={classNames(styles.replay)} onClick={() => { setNum(props.num); setHide(false); }}>
<span>Replay</span>
</button>
</div>
</>
)
}
export default AnimatedCountDown;