feat: better animation

This commit is contained in:
Ou 2024-11-10 23:26:02 +08:00
parent a1d25f7d6f
commit a05f0d54b5
6 changed files with 112 additions and 58 deletions

View File

@ -28,6 +28,7 @@
"@atlaskit/pragmatic-drag-and-drop": "^1.4.0",
"@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^1.4.0",
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.3",
"@formkit/auto-animate": "^0.8.2",
"@iconify-json/si": "^1.2.1",
"@tanstack/react-query-devtools": "^5.59.9",
"@tanstack/react-router": "^1.64.0",

79
pnpm-lock.yaml generated
View File

@ -26,6 +26,9 @@ importers:
'@atlaskit/pragmatic-drag-and-drop-hitbox':
specifier: ^1.0.3
version: 1.0.3
'@formkit/auto-animate':
specifier: ^0.8.2
version: 0.8.2
'@iconify-json/si':
specifier: ^1.2.1
version: 1.2.1
@ -89,6 +92,9 @@ importers:
md5:
specifier: ^2.3.0
version: 2.3.0
motion:
specifier: ^10.18.0
version: 10.18.0
ofetch:
specifier: ^1.4.1
version: 1.4.1
@ -1473,6 +1479,9 @@ packages:
resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==}
engines: {node: '>=14'}
'@formkit/auto-animate@0.8.2':
resolution: {integrity: sha512-SwPWfeRa5veb1hOIBMdzI+73te5puUBHmqqaF1Bu7FjvxlYSz/kJcZKSa9Cg60zL0uRNeJL2SbRxV6Jp6Q1nFQ==}
'@humanfs/core@0.19.0':
resolution: {integrity: sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==}
engines: {node: '>=18.18.0'}
@ -1540,6 +1549,24 @@ packages:
resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==}
hasBin: true
'@motionone/animation@10.18.0':
resolution: {integrity: sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw==}
'@motionone/dom@10.18.0':
resolution: {integrity: sha512-bKLP7E0eyO4B2UaHBBN55tnppwRnaE3KFfh3Ps9HhnAkar3Cb69kUCJY9as8LrccVYKgHA+JY5dOQqJLOPhF5A==}
'@motionone/easing@10.18.0':
resolution: {integrity: sha512-VcjByo7XpdLS4o9T8t99JtgxkdMcNWD3yHU/n6CLEz3bkmKDRZyYQ/wmSf6daum8ZXqfUAgFeCZSpJZIMxaCzg==}
'@motionone/generators@10.18.0':
resolution: {integrity: sha512-+qfkC2DtkDj4tHPu+AFKVfR/C30O1vYdvsGYaR13W/1cczPrrcjdvYCj0VLFuRMN+lP1xvpNZHCRNM4fBzn1jg==}
'@motionone/types@10.17.1':
resolution: {integrity: sha512-KaC4kgiODDz8hswCrS0btrVrzyU2CSQKO7Ps90ibBVSQmjkrt2teqta6/sOG59v7+dPnKMAg13jyqtMKV2yJ7A==}
'@motionone/utils@10.18.0':
resolution: {integrity: sha512-3XVF7sgyTSI2KWvTf6uLlBJ5iAgRgmvp3bpuOiQJvInd4nZ19ET8lX5unn30SlmRH7hXbBbH+Gxd0m0klJ3Xtw==}
'@napi-rs/pinyin-android-arm-eabi@1.7.5':
resolution: {integrity: sha512-dnurqJdedbU8D1Ngudf10nXvc4BbVSe4ki9U2LUZoGMDGa069t4c87BHRXvcy2By3YpOozORv9/QTMxAQFVOLg==}
engines: {node: '>= 10.0'}
@ -3971,6 +3998,9 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
hey-listen@1.0.8:
resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==}
hookable@5.5.3:
resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
@ -4605,6 +4635,9 @@ packages:
mockdate@3.0.5:
resolution: {integrity: sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ==}
motion@10.18.0:
resolution: {integrity: sha512-MVAZZmwM/cp77BrNe1TxTMldxRPjwBNHheU5aPToqT4rJdZxLiADk58H+a0al5jKLxkB0OdgNq6DiVn11cjvIQ==}
mri@1.2.0:
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
engines: {node: '>=4'}
@ -7441,6 +7474,8 @@ snapshots:
'@fastify/busboy@2.1.1': {}
'@formkit/auto-animate@0.8.2': {}
'@humanfs/core@0.19.0': {}
'@humanfs/node@0.16.5':
@ -7534,6 +7569,41 @@ snapshots:
- encoding
- supports-color
'@motionone/animation@10.18.0':
dependencies:
'@motionone/easing': 10.18.0
'@motionone/types': 10.17.1
'@motionone/utils': 10.18.0
tslib: 2.8.0
'@motionone/dom@10.18.0':
dependencies:
'@motionone/animation': 10.18.0
'@motionone/generators': 10.18.0
'@motionone/types': 10.17.1
'@motionone/utils': 10.18.0
hey-listen: 1.0.8
tslib: 2.8.0
'@motionone/easing@10.18.0':
dependencies:
'@motionone/utils': 10.18.0
tslib: 2.8.0
'@motionone/generators@10.18.0':
dependencies:
'@motionone/types': 10.17.1
'@motionone/utils': 10.18.0
tslib: 2.8.0
'@motionone/types@10.17.1': {}
'@motionone/utils@10.18.0':
dependencies:
'@motionone/types': 10.17.1
hey-listen: 1.0.8
tslib: 2.8.0
'@napi-rs/pinyin-android-arm-eabi@1.7.5':
optional: true
@ -10357,6 +10427,8 @@ snapshots:
dependencies:
function-bind: 1.1.2
hey-listen@1.0.8: {}
hookable@5.5.3: {}
hosted-git-info@2.8.9: {}
@ -10973,6 +11045,13 @@ snapshots:
mockdate@3.0.5: {}
motion@10.18.0:
dependencies:
'@motionone/animation': 10.18.0
'@motionone/dom': 10.18.0
'@motionone/types': 10.17.1
'@motionone/utils': 10.18.0
mri@1.2.0: {}
mrmime@2.0.0: {}

View File

@ -1,11 +1,12 @@
import type { PropsWithChildren } from "react"
import type { SourceID } from "@shared/types"
import { motion } from "framer-motion"
import type { BaseEventPayload, ElementDragType } from "@atlaskit/pragmatic-drag-and-drop/dist/types/internal-types"
import { extractClosestEdge } from "@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge"
import { reorderWithEdge } from "@atlaskit/pragmatic-drag-and-drop-hitbox/util/reorder-with-edge"
import { createPortal } from "react-dom"
import { useThrottleFn } from "ahooks"
import { useAutoAnimate } from "@formkit/auto-animate/react"
import { motion } from "framer-motion"
import { DndContext } from "../common/dnd"
import { useSortable } from "../common/dnd/useSortable"
import type { ItemsProps } from "./card"
@ -15,12 +16,14 @@ import { currentSourcesAtom } from "~/atoms"
const AnimationDuration = 200
export function Dnd() {
const [items, setItems] = useAtom(currentSourcesAtom)
const [parent] = useAutoAnimate({ duration: AnimationDuration })
useEntireQuery(items)
return (
<DndWrapper items={items} setItems={setItems}>
<motion.ol
className="grid w-full gap-6"
ref={parent}
style={{
gridTemplateColumns: "repeat(auto-fill, minmax(350px, 1fr))",
}}
@ -42,7 +45,6 @@ export function Dnd() {
{items.map(id => (
<motion.li
key={id}
layout
transition={{
type: "tween",
duration: AnimationDuration / 1000,

View File

@ -1,6 +1,6 @@
import { AnimatePresence, motion } from "framer-motion"
import { useCallback, useMemo, useRef } from "react"
import { useHoverDirty, useMount, useUpdateEffect, useWindowSize } from "react-use"
import { useMount, useWindowSize } from "react-use"
import { useAutoAnimate } from "@formkit/auto-animate/react"
import type { ToastItem } from "~/atoms/types"
import { Timer } from "~/utils"
@ -12,33 +12,20 @@ export function Toast() {
return t > width * 0.9 ? width * 0.9 : t
}, [width])
const toastItems = useAtomValue(toastAtom)
const [parent] = useAutoAnimate({ duration: 200 })
return (
<AnimatePresence>
{toastItems.length && (
<motion.ol
initial="hidden"
animate="visible"
style={{
width: WIDTH,
left: center,
}}
variants={{
visible: {
transition: {
delayChildren: 0.1,
staggerChildren: 0.2,
},
},
}}
className="absolute top-0 z-99 flex flex-col gap-2"
>
{
toastItems.map(k => <Item key={k.id} info={k} />)
}
</motion.ol>
)}
</AnimatePresence>
<ol
ref={parent}
style={{
width: WIDTH,
left: center,
}}
className="absolute top-0 z-99 flex flex-col gap-2"
>
{
toastItems.map(k => <Item key={k.id} info={k} />)
}
</ol>
)
}
@ -67,30 +54,22 @@ function Item({ info }: { info: ToastItem }) {
return () => timer.current?.clear()
})
const ref = useRef(null)
const isHoverd = useHoverDirty(ref)
useUpdateEffect(() => {
if (isHoverd) {
const [hoverd, setHoverd] = useState(false)
useEffect(() => {
if (hoverd) {
timer.current?.pause()
} else {
timer.current?.resume()
}
}, [isHoverd])
}, [hoverd])
return (
<motion.li
ref={ref}
layout
variants={{
hidden: { y: 0, opacity: 0 },
visible: {
y: 15,
opacity: 1,
},
}}
<li
className={$(
"bg-base rounded-lg shadow-xl relative",
)}
onMouseEnter={() => setHoverd(true)}
onMouseLeave={() => setHoverd(false)}
>
<div className={$(
`bg-${color}-500 dark:bg-${color} bg-op-40! p2 backdrop-blur-5 rounded-lg w-full`,
@ -98,7 +77,7 @@ function Item({ info }: { info: ToastItem }) {
)}
>
{
isHoverd
hoverd
? <button type="button" className={`i-ph:x-circle color-${color}-500 i-ph:info`} onClick={() => hidden(false)} />
: <span className={`i-ph:info color-${color}-500 `} />
}
@ -117,6 +96,6 @@ function Item({ info }: { info: ToastItem }) {
)}
</div>
</div>
</motion.li>
</li>
)
}

View File

@ -1,5 +1,4 @@
import { motion } from "framer-motion"
import { useHoverDirty } from "react-use"
function ThemeToggle() {
const { isDark, toggleDark } = useDark()
@ -16,13 +15,8 @@ function ThemeToggle() {
export function Menu() {
const { loggedIn, login, logout, userInfo, enableLogin } = useLogin()
const [shown, show] = useState(false)
const ref = useRef<HTMLElement>(null)
const isHover = useHoverDirty(ref)
useEffect(() => {
show(isHover)
}, [shown, isHover])
return (
<span ref={ref} className="relative">
<span className="relative" onMouseEnter={() => show(true)} onMouseLeave={() => show(false)}>
<span className="flex items-center scale-90">
{
enableLogin && loggedIn && userInfo.avatar

View File

@ -3,15 +3,14 @@ import { defineConfig } from "vite"
import react from "@vitejs/plugin-react-swc"
import { TanStackRouterVite } from "@tanstack/router-plugin/vite"
import unocss from "unocss/vite"
import dotenv from "dotenv"
import unimport from "unimport/unplugin"
import nitro from "./nitro.config"
import { projectDir } from "./shared/dir"
import pwa from "./pwa.config"
dotenv.config({
path: join(projectDir, ".env.server"),
})
// dotenv.config({
// path: join(projectDir, ".env.server"),
// })
export default defineConfig({
resolve: {