From a44c91b57286ea43e44f580d24290cb6f7a5f1a1 Mon Sep 17 00:00:00 2001 From: Haoyu Xu Date: Sat, 24 Jun 2023 00:57:24 -0400 Subject: [PATCH] feat(directory): add a scroll to top button --- directory/src/component/dropdown.jsx | 53 +++++++----- .../src/component/scss/dropdown.module.scss | 2 +- .../component/scss/totop_button.module.scss | 84 +++++++++++++++++++ directory/src/component/totop_button.jsx | 67 +++++++++++++++ directory/src/routes/Root.jsx | 4 +- 5 files changed, 186 insertions(+), 24 deletions(-) create mode 100644 directory/src/component/scss/totop_button.module.scss create mode 100644 directory/src/component/totop_button.jsx diff --git a/directory/src/component/dropdown.jsx b/directory/src/component/dropdown.jsx index b54ede2..3ab7869 100644 --- a/directory/src/component/dropdown.jsx +++ b/directory/src/component/dropdown.jsx @@ -24,29 +24,38 @@ export default function Dropdown(props) { diff --git a/directory/src/component/scss/dropdown.module.scss b/directory/src/component/scss/dropdown.module.scss index 36ac819..58afc33 100644 --- a/directory/src/component/scss/dropdown.module.scss +++ b/directory/src/component/scss/dropdown.module.scss @@ -56,7 +56,7 @@ color: var(--link-highlight-color); cursor: auto; - .group { + .date { font-family: "Bender"; font-weight: bold; font-size: 24px; diff --git a/directory/src/component/scss/totop_button.module.scss b/directory/src/component/scss/totop_button.module.scss new file mode 100644 index 0000000..deb33a5 --- /dev/null +++ b/directory/src/component/scss/totop_button.module.scss @@ -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%); + } +} \ No newline at end of file diff --git a/directory/src/component/totop_button.jsx b/directory/src/component/totop_button.jsx new file mode 100644 index 0000000..d4daf97 --- /dev/null +++ b/directory/src/component/totop_button.jsx @@ -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 ( + <> +
{ smoothScroll("#root") }} + > +
+
+
+
+
+ + ) +} +ToTopButton.propTypes = { + onClick: PropTypes.func, + className: PropTypes.string, +}; \ No newline at end of file diff --git a/directory/src/routes/Root.jsx b/directory/src/routes/Root.jsx index e316add..581aa8f 100644 --- a/directory/src/routes/Root.jsx +++ b/directory/src/routes/Root.jsx @@ -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() { + @@ -305,7 +307,7 @@ function HeaderButton() { list.push({ name: key, value: null, - type: "group", + type: "date", }) value.forEach((item) => { list.push({