1
1
'use client'
2
2
3
3
import { useQuery } from '@tanstack/react-query'
4
- import React , { createElement , forwardRef } from 'react'
4
+ import React , { createElement , forwardRef , useEffect , useRef } from 'react'
5
5
import clsx from 'clsx'
6
- import { m } from 'framer-motion'
6
+ import { m , useInView } from 'framer-motion'
7
7
import Link from 'next/link'
8
8
import type { PropsWithChildren } from 'react'
9
9
@@ -14,13 +14,18 @@ import { TextUpTransitionView } from '~/components/ui/transition/TextUpTransitio
14
14
import { isSupportIcon , SocialIcon } from '~/components/widgets/home/SocialIcon'
15
15
import { PeekLink } from '~/components/widgets/peek/PeekLink'
16
16
import { PostMetaBar } from '~/components/widgets/post'
17
- import { softBouncePrest , softSpringPreset } from '~/constants/spring'
17
+ import {
18
+ microReboundPreset ,
19
+ softBouncePrest ,
20
+ softSpringPreset ,
21
+ } from '~/constants/spring'
18
22
import { useConfig } from '~/hooks/data/use-config'
19
23
import { isDev } from '~/lib/env'
20
24
import { clsxm } from '~/lib/helper'
21
25
import { noopObj } from '~/lib/noop'
22
26
import { apiClient } from '~/lib/request'
23
27
import { routeBuilder , Routes } from '~/lib/route-builder'
28
+ import { springScrollToTop } from '~/lib/scroller'
24
29
import { useAggregationSelector } from '~/providers/root/aggregation-data-provider'
25
30
26
31
import { useHomeQueryData } from './query'
@@ -34,6 +39,9 @@ const Screen = forwardRef<
34
39
className ?: string
35
40
} >
36
41
> ( ( props , ref ) => {
42
+ const inViewRef = useRef < HTMLSpanElement > ( null )
43
+ const inView = useInView ( inViewRef , { once : true } )
44
+
37
45
return (
38
46
< div
39
47
ref = { ref }
@@ -43,23 +51,20 @@ const Screen = forwardRef<
43
51
props . className ,
44
52
) }
45
53
>
46
- { props . children }
54
+ < span ref = { inViewRef } />
55
+ { inView && props . children }
47
56
</ div >
48
57
)
49
58
} )
50
59
Screen . displayName = 'Screen'
51
60
52
61
export default function Home ( ) {
62
+ useEffect ( ( ) => {
63
+ if ( isDev ) return
64
+ springScrollToTop ( )
65
+ } , [ ] )
53
66
return (
54
67
< div >
55
- < Screen className = "mt-[-4.5rem]" >
56
- < h1 >
57
- 这个页面还没构思,待到春去秋来,我会在这里写下一些关于我自己的故事。
58
- </ h1 >
59
- < h1 > 其他页面基本已完成。你可以在顶部的导航栏中找到它们。</ h1 >
60
- < h1 > 欢迎给我反馈问题,谢谢您。</ h1 >
61
- </ Screen >
62
-
63
68
< Welcome />
64
69
65
70
< PostScreen />
@@ -184,9 +189,8 @@ const Welcome = () => {
184
189
185
190
< m . div
186
191
initial = { { opacity : 0.0001 , y : 50 } }
187
- whileInView = { { opacity : 1 , y : 0 } }
192
+ animate = { { opacity : 1 , y : 0 } }
188
193
transition = { softBouncePrest }
189
- viewport = { { once : true } }
190
194
className = { clsx (
191
195
'absolute bottom-0 left-0 right-0 flex flex-col center' ,
192
196
@@ -215,11 +219,10 @@ const PostScreen = () => {
215
219
opacity : 0.0001 ,
216
220
y : 50 ,
217
221
} }
218
- whileInView = { {
222
+ animate = { {
219
223
opacity : 1 ,
220
224
y : 0 ,
221
225
} }
222
- viewport = { { once : true } }
223
226
transition = { softSpringPreset }
224
227
className = "text-2xl font-medium leading-loose"
225
228
>
@@ -234,8 +237,7 @@ const PostScreen = () => {
234
237
235
238
return (
236
239
< m . li
237
- viewport = { { once : true } }
238
- whileInView = { {
240
+ animate = { {
239
241
opacity : 1 ,
240
242
x : 0 ,
241
243
} }
@@ -289,8 +291,7 @@ const PostScreen = () => {
289
291
290
292
< m . div
291
293
initial = { { opacity : 0.0001 , y : 10 } }
292
- whileInView = { { opacity : 1 , y : 0 } }
293
- viewport = { { once : true } }
294
+ animate = { { opacity : 1 , y : 0 } }
294
295
transition = { {
295
296
...softBouncePrest ,
296
297
delay : 0.3 + 0.2 * posts . length ,
@@ -326,10 +327,28 @@ const NoteScreen = () => {
326
327
< TwoColumnLayout leftContainerClassName = "block lg:flex" >
327
328
< div >
328
329
< section className = "flex flex-col justify-end" >
329
- < h3 className = "mb-6 text-center text-xl" >
330
+ < m . h3
331
+ className = "mb-6 text-center text-xl"
332
+ initial = { { opacity : 0.0001 , y : 50 } }
333
+ animate = { { opacity : 1 , y : 0 } }
334
+ transition = { softBouncePrest }
335
+ >
330
336
看看我的近况,这是我最近的所思所想
331
- </ h3 >
332
- < div
337
+ </ m . h3 >
338
+ < m . div
339
+ initial = { { opacity : 0.00001 , scale : 0.94 , y : 20 } }
340
+ animate = { {
341
+ y : 0 ,
342
+ scale : 1 ,
343
+ opacity : 1 ,
344
+ } }
345
+ viewport = { {
346
+ once : true ,
347
+ } }
348
+ transition = { {
349
+ ...softSpringPreset ,
350
+ delay : 0.3 ,
351
+ } }
333
352
className = { clsx (
334
353
'relative flex h-[150px] w-full rounded-md ring-1 ring-slate-200 center dark:ring-neutral-800' ,
335
354
'hover:shadow-md hover:shadow-slate-100 dark:hover:shadow-neutral-900' ,
@@ -355,16 +374,35 @@ const NoteScreen = () => {
355
374
} }
356
375
/>
357
376
) }
358
- </ div >
377
+ </ m . div >
359
378
</ section >
360
379
361
380
{ hasHistory && (
362
381
< section className = "mt-[20%]" >
363
- < div className = "text-lg opacity-80" > 这里还有一些历史回顾</ div >
382
+ < m . div
383
+ initial = { { opacity : 0.0001 , y : 50 } }
384
+ animate = { { opacity : 0.8 , y : 0 } }
385
+ transition = { {
386
+ ...softBouncePrest ,
387
+ delay : 0.5 ,
388
+ } }
389
+ className = "text-lg"
390
+ >
391
+ 这里还有一些历史回顾
392
+ </ m . div >
364
393
< ul className = "shiro-timeline mt-4" >
365
- { history . map ( ( note ) => {
394
+ { history . map ( ( note , i ) => {
366
395
return (
367
- < li key = { note . id } className = "flex min-w-0 justify-between" >
396
+ < m . li
397
+ key = { note . id }
398
+ initial = { { opacity : 0.0001 , y : 20 } }
399
+ animate = { { opacity : 1 , y : 0 } }
400
+ transition = { {
401
+ ...microReboundPreset ,
402
+ delay : 0.8 + 0.1 * i ,
403
+ } }
404
+ className = "flex min-w-0 justify-between"
405
+ >
368
406
< PeekLink
369
407
className = "min-w-0 flex-shrink truncate"
370
408
href = { routeBuilder ( Routes . Note , { id : note . nid } ) }
@@ -378,18 +416,47 @@ const NoteScreen = () => {
378
416
displayAbsoluteTimeAfterDay = { 180 }
379
417
/>
380
418
</ span >
381
- </ li >
419
+ </ m . li >
382
420
)
383
421
} ) }
384
422
</ ul >
423
+
424
+ < m . div
425
+ className = "mt-8"
426
+ initial = { { opacity : 0.00001 , scale : 0.96 , y : 10 } }
427
+ animate = { {
428
+ y : 0 ,
429
+ scale : 1 ,
430
+ opacity : 1 ,
431
+ } }
432
+ transition = { {
433
+ ...softSpringPreset ,
434
+ delay : 1 ,
435
+ } }
436
+ >
437
+ < MotionButtonBase >
438
+ < Link
439
+ className = "shiro-link--underline"
440
+ href = { routeBuilder ( Routes . Posts , { } ) }
441
+ >
442
+ 还有更多,要不要看看?
443
+ </ Link >
444
+ </ MotionButtonBase >
445
+ </ m . div >
385
446
</ section >
386
447
) }
387
448
</ div >
388
- < h2 className = "text-2xl font-medium leading-loose" >
449
+ < m . h2
450
+ className = "text-2xl font-medium leading-loose"
451
+ initial = { { opacity : 0.0001 } }
452
+ animate = { {
453
+ opacity : 1 ,
454
+ } }
455
+ >
389
456
而在这里,你会看到一个不同的我,
390
457
< br />
391
458
一个在生活中发现美,感受痛苦,洞察人性的我。
392
- </ h2 >
459
+ </ m . h2 >
393
460
</ TwoColumnLayout >
394
461
</ Screen >
395
462
)
@@ -404,28 +471,77 @@ const FriendScreen = () => {
404
471
staleTime : 1000 * 60 ,
405
472
} )
406
473
return (
407
- < Screen >
408
- < h2 >
409
- 这些是我珍视的人,他们陪伴我走过人生的每一段旅程,和我一起笑,一起哭,一起成长。
410
- </ h2 >
411
- < ul >
412
- { data ?. data . map ( ( friend ) => {
413
- return < li key = { friend . id } > { friend . name } </ li >
414
- } ) }
415
- </ ul >
416
- 还有更多..
474
+ < Screen className = "flex center" >
475
+ < div className = "flex min-w-0 flex-col" >
476
+ < BottomToUpTransitionView className = "text-center text-3xl font-medium" >
477
+ 这些是我珍视的人,他们陪伴我走过人生的每一段旅程。
478
+ </ BottomToUpTransitionView >
479
+ < ul
480
+ className = { clsx (
481
+ 'mt-12 grid max-w-5xl grid-cols-3 gap-10 p-4 md:grid-cols-4 lg:grid-cols-5 lg:p-0' ,
482
+
483
+ 'min-w-0 [&_*]:flex [&_*]:flex-col [&_*]:center' ,
484
+ ) }
485
+ >
486
+ { data ?. data . map ( ( friend , i ) => {
487
+ return (
488
+ < li key = { friend . id } className = "min-w-0 max-w-full" >
489
+ < m . div
490
+ initial = { { scale : 0.001 , opacity : 0.0001 } }
491
+ animate = { { scale : 1 , opacity : 1 } }
492
+ transition = { {
493
+ delay : i * 0.1 + 0.3 ,
494
+ ...softBouncePrest ,
495
+ } }
496
+ >
497
+ < a
498
+ href = { friend . url }
499
+ className = "w-full min-w-0"
500
+ target = "_blank"
501
+ rel = "noreferrer"
502
+ >
503
+ < div
504
+ className = "aspect-square h-[80px] w-[80px] rounded-full bg-contain bg-center ring-1 ring-slate-200/80 dark:bg-neutral-800/80"
505
+ style = { {
506
+ backgroundImage : `url(${ friend . avatar } )` ,
507
+ } }
508
+ aria-hidden
509
+ />
510
+ < span className = "mt-5 w-full min-w-0 truncate" >
511
+ { friend . name }
512
+ </ span >
513
+ </ a >
514
+ </ m . div >
515
+ </ li >
516
+ )
517
+ } ) }
518
+ </ ul >
519
+
520
+ < BottomToUpTransitionView
521
+ delay = { 1500 }
522
+ className = "mt-16 w-full text-center"
523
+ >
524
+ < MotionButtonBase >
525
+ < Link
526
+ className = "shiro-link--underline"
527
+ href = { routeBuilder ( Routes . Friends , { } ) }
528
+ >
529
+ 还有更多,要不要看看?
530
+ </ Link >
531
+ </ MotionButtonBase >
532
+ </ BottomToUpTransitionView >
533
+ </ div >
417
534
</ Screen >
418
535
)
419
536
}
420
537
421
538
const MoreScreen = ( ) => {
539
+ return null
422
540
return (
423
541
< Screen >
424
- < h1 >
425
- 最后,这是关于这个小宇宙以及我自己的一些小秘密。如果你有任何问题或者想要分享的想法,都可以随时找到我。
426
- </ h1 >
542
+ < h2 className = "text-2xl font-medium" > 感谢看到这里。</ h2 >
427
543
428
- < div > Like this? </ div >
544
+ < div className = "mt-12 flex w-full center" > TODO </ div >
429
545
</ Screen >
430
546
)
431
547
}
0 commit comments