feat(directory): added animated cursor

This commit is contained in:
Haoyu Xu
2023-10-23 16:47:09 -04:00
parent 865c0fccfe
commit b8a2247406
22 changed files with 100 additions and 29 deletions

View File

@@ -15,6 +15,7 @@
--home-item-background-linear-gradient-color: rgba(0, 0, 0, 10%);
--home-item-outline-color: rgba(41, 41, 41, 30%);
--button-color: #999;
--cursor-color: var(--text-color);
}
@mixin dark-theme() {
@@ -30,6 +31,7 @@
--home-item-background-linear-gradient-color: rgba(255, 255, 255, 10%);
--home-item-outline-color: rgba(214, 214, 214, 30%);
--button-color: #666;
--cursor-color: var(--text-color);
}
@media (prefers-color-scheme: dark) {

View File

@@ -1,5 +1,6 @@
import React, {
useState,
useCallback
} from 'react'
import PropTypes from 'prop-types';
import classes from './scss/dropdown.module.scss'
@@ -11,9 +12,17 @@ export default function Dropdown(props) {
setHidden(!hidden)
}
const onMouseEnter = useCallback((item) => {
document.documentElement.style.setProperty('--cursor-color', item.color);
}, [])
const onMouseLeave = useCallback(() => {
document.documentElement.style.setProperty('--cursor-color', 'var(--text-color)');
}, [])
return (
<>
<section className={`${classes.dropdown} ${hidden ? '' : classes.active} ${props.className ? props.className : ''} ${props.left ? classes.left : ''}`}>
<section className={`${classes.dropdown} ${hidden ? '' : classes.active} ${props.className ? props.className : ''} ${props.left ? classes.left : ''}`} data-type="clickable">
<section
className={classes.text}
onClick={() => toggleDropdown()}
@@ -56,6 +65,9 @@ export default function Dropdown(props) {
toggleDropdown()
}}
style={item.color ? { color: item.color } : {}}
data-type="clickable"
onMouseEnter={() => onMouseEnter(item)}
onMouseLeave={() => onMouseLeave()}
>
{
item.icon ? (

View File

@@ -26,7 +26,7 @@ export default function Popup(props) {
</section>
</section>
<section className={`${classes.overlay} ${hidden ? '' : classes.active}`}
onClick={() => toggle()} />
onClick={() => toggle()} data-type="clickable"/>
</section>
<span
className={classes['entry-text']}

View File

@@ -8,6 +8,7 @@ export default function ReturnButton(props) {
<>
<section className={`${classes['return-button']} ${props.className ? props.className : ''}`}
onClick={() => props.onClick()}
data-type="clickable"
>
<section className={classes.wrapper}>
<section className={classes["arrow-left"]}></section>

View File

@@ -4,7 +4,6 @@
user-select: none;
z-index: 2;
padding: 0.5em;
cursor: pointer;
&.left {
.popup, .menu {
@@ -37,7 +36,6 @@
z-index: -1;
top: 2em;
right: 0;
cursor: auto;
transition: all cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
overflow: auto;
padding: 0.5rem;
@@ -92,7 +90,6 @@
border: 1px solid var(--border-color);
visibility: hidden;
color: var(--link-highlight-color);
cursor: auto;
&.left {
left: 0;
@@ -120,7 +117,6 @@
}
.item {
cursor: pointer;
padding: 0.5rem;
font-size: 1rem;
display: flex;
@@ -161,7 +157,6 @@
bottom: 0;
width: 100vw;
height: 100vh;
cursor: auto;
}
&.active,

View File

@@ -1,7 +1,3 @@
.entry-text {
cursor: pointer;
}
.popup {
position: fixed;
left: 0;

View File

@@ -9,7 +9,6 @@
align-items: flex-start;
padding: 0.6rem 0;
width: 3rem;
cursor: pointer;
.wrapper {
display: flex;

View File

@@ -49,7 +49,6 @@
right: 1rem;
width: 2rem;
height: 2rem;
cursor: pointer;
opacity: 0;
transition: all cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
visibility: hidden;

View File

@@ -3,7 +3,6 @@
user-select: none;
z-index: 2;
padding: 8px 36px 8px 8px;
cursor: pointer;
display: flex;
flex-direction: row;
align-items: center;

View File

@@ -2,7 +2,6 @@
position: fixed;
user-select: none;
z-index: 2;
cursor: pointer;
height: 2rem;
right: 2rem;
bottom: 1rem;

View File

@@ -21,6 +21,7 @@ export default function Switch(props) {
props.handleOnClick(!on)
}
}}
data-type="clickable"
>
<span className={classes.text}>{i18n(props.text)}</span>
<section className={classes.wrapper}>

View File

@@ -52,6 +52,7 @@ export default function ToTopButton(props) {
<>
<section className={`${classes['totop-button']} ${clicked ? classes.clicked : ''} ${hidden ? '' : classes.show} ${props.className ? props.className : ''}`}
onClick={() => { smoothScroll("#root") }}
data-type="clickable"
>
<section className={classes.bar}></section>
<section className={classes.bar}></section>

View File

@@ -20,6 +20,7 @@ import { useHeader } from '@/state/header';
import VoiceElement from '@/component/voice';
import spine from '!/libs/spine-player'
import '!/libs/spine-player.css'
import AnimatedCursor from "react-animated-cursor"
import useUmami from '@/state/insights';
const voiceOnAtom = atomWithStorage('voiceOn', false)
@@ -186,6 +187,25 @@ export default function Error() {
handleAduioStateChange={handleAduioStateChange}
/>
</main>
<AnimatedCursor
innerSize={8}
outerSize={36}
innerScale={1}
outerScale={0.5}
outerAlpha={0.7}
hasBlendMode={true}
innerStyle={{
backgroundColor: 'var(--cursor-color)'
}}
outerStyle={{
backgroundColor: 'transparent',
border: '3px solid var(--cursor-color)'
}}
clickables={[
'a',
'section[data-type="clickable"]',
]}
/>
</section>
);
}

View File

@@ -2,7 +2,7 @@ import React, {
useState,
useEffect,
useMemo,
useCallback
useCallback,
} from 'react'
import PropTypes from 'prop-types';
import {
@@ -29,6 +29,7 @@ import Popup from '@/component/popup';
import Border from '@/component/border';
import CharIcon from '@/component/char_icon';
import ToTopButton from '@/component/totop_button';
import AnimatedCursor from "react-animated-cursor"
const currentYear = new Date().getFullYear()
@@ -79,8 +80,7 @@ export default function Root() {
setTimeout(() => {
document.querySelector('.loader').style.display = 'none'
}, 500)
}
, [])
}, [])
return (
<>
@@ -88,6 +88,7 @@ export default function Root() {
<section
className={`${header.navButton} ${drawerHidden ? '' : header.active}`}
onClick={() => toggleDrawer()}
data-type="clickable"
>
<section className={header.bar} />
<section className={header.bar} />
@@ -138,6 +139,25 @@ export default function Root() {
<ScrollRestoration />
</main>
<FooterElement />
<AnimatedCursor
innerSize={8}
outerSize={36}
innerScale={1}
outerScale={0.5}
outerAlpha={0.7}
hasBlendMode={true}
innerStyle={{
backgroundColor: 'var(--cursor-color)'
}}
outerStyle={{
backgroundColor: 'transparent',
border: '3px solid var(--cursor-color)'
}}
clickables={[
'a',
'section[data-type="clickable"]',
]}
/>
</>
)
}
@@ -150,7 +170,7 @@ function FooterElement() {
return (
<footer className={footer.footer}>
<section className={`${footer.links} ${footer.section}`}>
<section className={footer.item}>
<section className={footer.item} data-type="clickable">
<Popup
className={footer.link}
title={i18n('disclaimer')}
@@ -164,7 +184,7 @@ function FooterElement() {
<section className={footer.item}>
<Link reloadDocument to="https://gura.ch/aklive2d-gh" target="_blank" className={footer.link}>GitHub</Link>
</section>
<section className={footer.item}>
<section className={footer.item} data-type="clickable">
<Popup
className={footer.link}
title={i18n('contact_us')}
@@ -268,6 +288,7 @@ function HeaderTabsElement({ item }) {
item.onClick && item.onClick(e, currentTab)
}}
style={item.style}
data-type="clickable"
>
<section className={classes['text-wrapper']}>
<span>{i18n(item.key)}</span>
@@ -306,7 +327,7 @@ function HeaderButton() {
)
} else {
return (
<section className={header['back-arrow']}>
<section className={header['back-arrow']} data-type="clickable">
<Link to="/" className={header.link}>
<section className={header.arrow1} />
<section className={header.arrow2} />

View File

@@ -28,6 +28,7 @@ export default function Changelogs() {
setExtraArea([])
setFastNavigation([])
setHeaderIcon(null)
document.documentElement.style.setProperty('--cursor-color', 'var(--text-color)');
}, [setExtraArea, setFastNavigation, setHeaderIcon, setTitle])
useEffect(() => {

View File

@@ -64,6 +64,7 @@ export default function Home() {
useEffect(() => {
setContent(config?.operators || [])
document.documentElement.style.setProperty('--cursor-color', 'var(--text-color)');
}, [config])
const handleAduioStateChange = useCallback((e, state) => {
@@ -258,6 +259,15 @@ export default function Home() {
function OperatorElement({ item, hidden, handleVoicePlay }) {
const { textDefaultLang, language, alternateLang } = useLanguage()
const onMouseEnter = useCallback(() => {
handleVoicePlay(`/${item.link}/assets/${JSON.parse(import.meta.env.VITE_VOICE_FOLDERS).main}/${import.meta.env.VITE_APP_VOICE_URL}`)
document.documentElement.style.setProperty('--cursor-color', item.color);
}, [handleVoicePlay, item.color, item.link])
const onMouseLeave = useCallback(() => {
document.documentElement.style.setProperty('--cursor-color', 'var(--text-color)');
}, [])
return useMemo(() => {
return (
<NavLink
@@ -266,7 +276,8 @@ function OperatorElement({ item, hidden, handleVoicePlay }) {
hidden={hidden}
>
<section
onMouseEnter={() => handleVoicePlay(`/${item.link}/assets/${JSON.parse(import.meta.env.VITE_VOICE_FOLDERS).main}/${import.meta.env.VITE_APP_VOICE_URL}`)}
onMouseEnter={() => onMouseEnter()}
onMouseLeave={() => onMouseLeave()}
>
<section className={classes['background-filler']} />
<section className={classes.outline} />
@@ -296,7 +307,7 @@ function OperatorElement({ item, hidden, handleVoicePlay }) {
</section>
</NavLink>
)
}, [item, hidden, language, alternateLang, textDefaultLang, handleVoicePlay])
}, [item, hidden, language, alternateLang, textDefaultLang, onMouseEnter, onMouseLeave])
}
function VoiceSwitchElement({ src, replay, handleAduioStateChange }) {

View File

@@ -130,6 +130,7 @@ export default function Operator() {
fetch(`/${import.meta.env.VITE_DIRECTORY_FOLDER}/voice_${config.link}.json`).then(res => res.json()).then(data => {
setVoiceConfig(data)
})
document.documentElement.style.setProperty('--cursor-color', config.color);
}
}, [key, operators, setHeaderIcon])
@@ -411,7 +412,7 @@ export default function Operator() {
<section className={classes['styled-selection']}>
{item.options.map((option) => {
return (
<section className={`${classes.content} ${option.activeRule && option.activeRule() ? classes.active : ''}`} onClick={(e) => option.onClick(e)} key={option.name}>
<section className={`${classes.content} ${option.activeRule && option.activeRule() ? classes.active : ''}`} onClick={(e) => option.onClick(e)} key={option.name} data-type="clickable">
<section className={classes.option}>
<section className={classes.outline} />
<section className={`${classes.text} ${classes['no-overflow']}`}>{i18n(option.name)}</section>

View File

@@ -66,9 +66,9 @@
.item {
position: relative;
cursor: pointer;
width: 12rem;
margin: 1.25rem;
flex-shrink: 0;
background-image: repeating-linear-gradient(
90deg,
var(--home-item-background-linear-gradient-color) 0,
@@ -202,7 +202,6 @@
.content {
padding: 0.8rem 0;
cursor: pointer;
transition: transform cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
.option {

View File

@@ -55,10 +55,8 @@
font-weight: 700;
padding: 0 1rem;
text-transform: uppercase;
cursor: pointer;
border-bottom: 0.3rem solid transparent;
display: inline-block;
cursor: pointer;
text-decoration: none;
.text-wrapper {
@@ -77,6 +75,9 @@
&.active {
border-bottom-color: currentColor;
}
@media only screen and (max-width: 430px) {
line-height: 2em;
}
}
}
@media only screen and (max-width: 430px) {

View File

@@ -36,7 +36,6 @@
font-size: 2rem;
width: 2rem;
height: 2rem;
cursor: pointer;
display: flex;
justify-content: center;
align-items: flex-start;

View File

@@ -46,6 +46,7 @@
"jsdom": "^22.1.0",
"node-fetch": "^3.3.2",
"react": "^18.2.0",
"react-animated-cursor": "^2.11.2",
"react-dom": "^18.2.0",
"react-refresh": "^0.14.0",
"react-router-dom": "^6.17.0",

13
pnpm-lock.yaml generated
View File

@@ -23,6 +23,9 @@ dependencies:
react:
specifier: ^18.2.0
version: 18.2.0
react-animated-cursor:
specifier: ^2.11.2
version: 2.11.2(react-dom@18.2.0)(react@18.2.0)
react-dom:
specifier: ^18.2.0
version: 18.2.0(react@18.2.0)
@@ -2978,6 +2981,16 @@ packages:
strip-json-comments: 2.0.1
dev: false
/react-animated-cursor@2.11.2(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-LV0PtST61tdeW/chCqFt1JLp6i0d5fD9v2uradBcPAkKKCi2Iw7sAcD11ADS1TjXPPj9MOoD9YubEt++/IbZ+g==}
peerDependencies:
react: ^18.2.0
react-dom: ^18.2.0
dependencies:
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/react-dom@18.2.0(react@18.2.0):
resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
peerDependencies: