50天50个小项目系列文章-(Day1)

127 阅读2分钟

50天50个小项目系列文章-(Day1)👽

做这个系列文章的目的是为了提高自己的前端基础水平,毕竟前端基础三件套是重中之重吗😉,下面是我的GitHub地址和这个小demo对应的代码仓库。

1.技术栈

  • Typescript
  • sass
  • React

2.项目介绍

AnimatedCountdown:具有动画效果的计时器

实现效果

image-20220731172831986

image-20220731173104430

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;