feat(directory): add a scroll to top button

This commit is contained in:
Haoyu Xu
2023-06-24 00:57:24 -04:00
parent eea45abbd6
commit a44c91b572
5 changed files with 186 additions and 24 deletions

View File

@@ -24,17 +24,24 @@ export default function Dropdown(props) {
<ul className={classes.menu} style={props.activeColor}>
{
props.menu.map((item) => {
if (item.type === 'group') {
switch (item.type) {
case 'date': {
return (
<section
key={item.name}
className={classes.group}
className={classes.date}
>
<section className={classes.line} />
<section className={classes.text}>{item.name}</section>
</section>
)
}
case 'custom': {
return (
item.component
)
}
default: {
return (
<li
key={item.name}
@@ -47,6 +54,8 @@ export default function Dropdown(props) {
<section className={classes.text}>{item.name}</section>
</li>
)
}
}
})
}
</ul>

View File

@@ -56,7 +56,7 @@
color: var(--link-highlight-color);
cursor: auto;
.group {
.date {
font-family: "Bender";
font-weight: bold;
font-size: 24px;

View File

@@ -0,0 +1,84 @@
.totop-button {
position: fixed;
user-select: none;
z-index: 2;
cursor: pointer;
height: 2rem;
right: 2rem;
bottom: 1rem;
display: flex;
flex-direction: column;
align-items: stretch;
justify-content: flex-start;
width: 3rem;
height: 3rem;
opacity: 0;
transition: opacity cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
&.show {
opacity: 1;
}
.bar {
position: absolute;
width: 100%;
height: 0.3rem;
background-color: var(--text-color-full);
will-change: auto;
}
.bar:nth-child(1) {
transform: rotateZ(45deg) scaleX(0.5) translateX(45%);
}
.bar:nth-child(2) {
transform: rotateZ(-45deg) scaleX(0.5) translateX(-45%);
}
.bar:nth-child(3), .bar:nth-child(4) {
transform: translateY(450%) rotateZ(90deg) scaleX(0.5);
}
&.clicked {
.bar:nth-child(1) {
animation: transform-1 2s cubic-bezier(0.65, 0.05, 0.36, 1) infinite;
}
.bar:nth-child(2) {
animation: transform-2 2s cubic-bezier(0.65, 0.05, 0.36, 1) infinite;
}
.bar:nth-child(3) {
animation: transform-3 2s cubic-bezier(0.65, 0.05, 0.36, 1) infinite;
}
.bar:nth-child(4) {
animation: transform-4 2s cubic-bezier(0.65, 0.05, 0.36, 1) infinite;
}
}
}
@keyframes transform-1 {
50% {
transform: translateY(450%) rotateZ(-90deg) scaleX(0.5);
}
}
@keyframes transform-2 {
50% {
transform: translateY(450%) rotateZ(90deg) scaleX(0.5);
}
}
@keyframes transform-3 {
50% {
transform: rotateZ(225deg) scaleX(0.5) translateX(-45%);
}
}
@keyframes transform-4 {
50% {
transform: rotateZ(-45deg) scaleX(0.5) translateX(-45%);
}
}

View File

@@ -0,0 +1,67 @@
import React, {
useEffect,
useState,
useCallback
} from 'react';
import PropTypes from 'prop-types';
import classes from './scss/totop_button.module.scss'
export default function ToTopButton(props) {
const [hidden, setHidden] = useState(true)
const [clicked, setClicked] = useState(false)
useEffect(() => {
const handleButton = () => {
const scrollBarPos = window.pageYOffset || 0;
setHidden(!(scrollBarPos > 100))
}
window.addEventListener('scroll', handleButton)
return () => {
window.removeEventListener('scroll', handleButton)
}
}, [])
const smoothScroll = useCallback(
(target) => {
const targetElement = document.querySelector(target);
const targetPosition = targetElement.getBoundingClientRect().top + window.pageYOffset;
const startPosition = window.pageYOffset;
const distance = targetPosition - startPosition;
const duration = 1000;
let start = null;
window.requestAnimationFrame(step);
function step(timestamp) {
if (!start) start = timestamp;
const progress = timestamp - start;
window.scrollTo(0, easeInOutCubic(progress, startPosition, distance, duration));
if (progress < duration) window.requestAnimationFrame(step);
}
function easeInOutCubic(t, b, c, d) {
t /= d / 2;
if (t < 1) return c / 2 * t * t * t + b;
t -= 2;
return c / 2 * (t * t * t + 2) + b;
}
setClicked(true)
setTimeout(() => {
setClicked(false)
}, duration)
}, [])
return (
<>
<section className={`${classes['totop-button']} ${clicked ? classes.clicked : ''} ${hidden ? '' : classes.show} ${props.className ? props.className : ''}`}
onClick={() => { smoothScroll("#root") }}
>
<section className={classes.bar}></section>
<section className={classes.bar}></section>
<section className={classes.bar}></section>
<section className={classes.bar}></section>
</section>
</>
)
}
ToTopButton.propTypes = {
onClick: PropTypes.func,
className: PropTypes.string,
};

View File

@@ -31,6 +31,7 @@ import Popup from '@/component/popup';
import ReturnButton from '@/component/return_button';
import Border from '@/component/border';
import CharIcon from '@/component/char_icon';
import ToTopButton from '@/component/totop_button';
const currentYear = new Date().getFullYear()
@@ -132,6 +133,7 @@ export default function Root() {
</section>
</section>
<HeaderButton />
<ToTopButton />
<Outlet />
<ScrollRestoration />
</main>
@@ -305,7 +307,7 @@ function HeaderButton() {
list.push({
name: key,
value: null,
type: "group",
type: "date",
})
value.forEach((item) => {
list.push({