Skip to content

Commit 0850bd9

Browse files
authored
[Feat/Animation] add sample card animation(#33)
[Feat/Animation] add sample card animation
1 parent 846d59f commit 0850bd9

File tree

3 files changed

+132
-157
lines changed

3 files changed

+132
-157
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use client';
2+
3+
import { MouseEvent, useCallback } from 'react';
4+
import { motion, useMotionTemplate, useMotionValue } from 'framer-motion';
5+
6+
export default function Spotlight() {
7+
let mouseX = useMotionValue(0);
8+
let mouseY = useMotionValue(0);
9+
10+
const handleMouseMove = useCallback(
11+
({ currentTarget, clientX, clientY }: MouseEvent) => {
12+
let { left, top } = currentTarget.getBoundingClientRect();
13+
14+
mouseX.set(clientX - left);
15+
mouseY.set(clientY - top);
16+
},
17+
[mouseX, mouseY]
18+
);
19+
20+
return (
21+
<div
22+
className='group relative h-screen w-full max-w-md rounded-xl border border-white/10 bg-gray-900 px-8 py-16 shadow-2xl'
23+
onMouseMove={handleMouseMove}
24+
>
25+
<motion.div
26+
className='pointer-events-none absolute -inset-px rounded-xl opacity-0 transition duration-300 group-hover:opacity-100'
27+
style={{
28+
background: useMotionTemplate`
29+
radial-gradient(
30+
650px circle at ${mouseX}px ${mouseY}px,
31+
rgba(14, 165, 233, 0.15),
32+
transparent 80%
33+
)
34+
`,
35+
}}
36+
/>
37+
<div>
38+
<h3 className='text-base font-semibold leading-7 text-sky-500'>
39+
Byline
40+
</h3>
41+
<div className='mt-2 flex items-center gap-x-2'>
42+
<span className='text-5xl font-bold tracking-tight text-white'>
43+
Hero
44+
</span>
45+
</div>
46+
<p className='mt-6 text-base leading-7 text-gray-300'>
47+
Lorem ipsum dolor sit amet consectetur adipisicing elit, facilis illum
48+
eum ullam nostrum atque quam.
49+
</p>
50+
</div>
51+
</div>
52+
);
53+
}

apps/web/components/examples/card/demo.tsx

Lines changed: 79 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -11,75 +11,90 @@ import {
1111
Separator,
1212
TypographyMuted,
1313
} from 'ui/components';
14+
import {
15+
MotionValue,
16+
animate,
17+
motion,
18+
useMotionTemplate,
19+
useMotionValue,
20+
useTransform,
21+
} from 'framer-motion';
22+
import { useCallback, MouseEvent, useRef } from 'react';
1423

1524
export default function CardDemo() {
16-
const IMAGE =
17-
'https://store.storeimages.cdn-apple.com/8756/as-images.apple.com/is/mbp-digitalmat-gallery-2-202206_GEO_KR?wid=364&hei=333&fmt=png-alpha&.v=1665427482788';
25+
const cardRef = useRef<HTMLDivElement>(null);
26+
const mouseX = useMotionValue(0);
27+
const mouseY = useMotionValue(0);
28+
const depth = 45;
29+
30+
const handleMouseMove = useCallback(
31+
({ currentTarget, clientX, clientY }: MouseEvent) => {
32+
let { left, top } = currentTarget.getBoundingClientRect();
33+
34+
mouseX.set(clientX - left);
35+
mouseY.set(clientY - top);
36+
},
37+
[mouseX, mouseY]
38+
);
39+
40+
let rotateX = useTransform<number, number>(mouseX, (newMouseX) => {
41+
if (!cardRef.current) return 0;
42+
43+
const rect = cardRef.current.getBoundingClientRect();
44+
const newRotateX = newMouseX - rect.left - rect.height / 2;
45+
46+
return -newRotateX / depth;
47+
});
48+
let rotateY = useTransform<number, number>(mouseY, (newMouseY) => {
49+
if (!cardRef.current) return 0;
50+
51+
const rect = cardRef.current.getBoundingClientRect();
52+
const newRotateY = newMouseY - rect.top - rect.width / 2;
53+
54+
return newRotateY / depth;
55+
});
1856

1957
return (
20-
<Card className='flex h-full w-full justify-evenly'>
21-
<Card className='select-none rounded-none'>
22-
<Image
23-
className='h-full flex-1 bg-cover'
24-
width={300}
25-
height={500}
26-
src={IMAGE}
27-
alt='MacBook pro 13'
28-
/>
29-
</Card>
30-
<Card className='flex-1 select-none rounded-none'>
31-
<CardHeader>
32-
<CardTitle>MacBook Pro 13</CardTitle>
33-
<div className='flex items-baseline justify-between'>
34-
<CardDescription>₩1,790,000부터</CardDescription>
35-
<Button
36-
className='pointer-events-none rounded-3xl bg-blue-500'
37-
size='sm'
38-
>
39-
구입하기
40-
</Button>
41-
</div>
42-
</CardHeader>
43-
<CardContent className='flex flex-col gap-y-2'>
44-
<div className='flex items-center gap-x-2'>
45-
<Icons.monitor size={30} />
46-
<TypographyMuted className='overflow-hidden text-ellipsis text-xs'>
47-
차세대 Apple M2 칩과 전문가급 성능을 유지해주는 액티브 쿨링
48-
시스템으로 더 많은 일을 더 빠르게 처리
49-
</TypographyMuted>
50-
</div>
51-
<Separator />
52-
<div className='flex items-center gap-x-2'>
53-
<Icons.batteryFull size={30} />
54-
<TypographyMuted className='overflow-hidden text-ellipsis text-xs'>
55-
하루 종일 쓰고도 밤 늦게까지 너끈한 최대 20시간의 배터리 사용
56-
시간각주¹
57-
</TypographyMuted>
58-
</div>
59-
<Separator />
60-
<div className='flex items-center gap-x-2'>
61-
<Icons.diagonal size={30} />
62-
<TypographyMuted className='overflow-hidden text-ellipsis text-xs'>
63-
생생한 색상과 놀라운 디테일을 보여주는 500 니트 밝기의 Retina
64-
디스플레이
65-
</TypographyMuted>
66-
</div>
67-
<Separator />
68-
<div className='flex items-center gap-x-2'>
69-
<Icons.plugZap size={30} />
70-
<TypographyMuted className='overflow-hidden text-ellipsis text-xs'>
71-
고속 액세서리에 연결하고 전원을 공급해주는 Thunderbolt 포트 2개
72-
</TypographyMuted>
73-
</div>
74-
<Separator />
58+
// <div className='flex h-[500px] w-full items-center justify-center border-2 bg-gray-600'>
59+
// <motion.div
60+
// onMouseMove={handleMouseMove}
61+
// ref={cardRef}
62+
// className=' h-[80%] w-[80%] border-2 bg-white backdrop-blur-xl'
63+
// style={{ rotateX, rotateY }}
64+
// >
65+
// 3D Card
66+
// </motion.div>
67+
// </div>
68+
<Card
69+
className='group relative w-full max-w-md rounded-xl border border-white/10 bg-gray-900 px-8 py-16 shadow-2xl shadow-gray-900'
70+
onMouseMove={handleMouseMove}
71+
>
72+
<motion.div
73+
className='pointer-events-none absolute -inset-px rounded-xl border-2 opacity-0 transition duration-300 group-hover:opacity-100'
74+
style={{
75+
background: useMotionTemplate`
76+
radial-gradient(
77+
650px circle at ${mouseX}px ${mouseY}px,
78+
rgba(14, 165, 233, 0.15),
79+
transparent 90%
80+
)
81+
`,
82+
}}
83+
/>
84+
<CardHeader>
85+
<CardTitle className='text-5xl font-bold tracking-tight text-white'>
86+
Card
87+
</CardTitle>
88+
<CardDescription className='text-base font-semibold leading-7 text-sky-500'>
89+
Component
90+
</CardDescription>
91+
<CardContent className='mt-2 flex items-center gap-x-2'>
92+
<p className='mt-6 text-base leading-7 text-gray-300'>
93+
Lorem ipsum dolor sit amet consectetur adipisicing elit, facilis
94+
illum eum ullam nostrum atque quam.
95+
</p>
7596
</CardContent>
76-
<CardFooter className='flex cursor-pointer select-none items-center gap-x-1 '>
77-
<TypographyMuted className='text-blue-500'>
78-
MacBook Pro 13 더 살펴보기
79-
</TypographyMuted>
80-
<Icons.chevronRight className='text-blue-500' size={15} />
81-
</CardFooter>
82-
</Card>
97+
</CardHeader>
8398
</Card>
8499
);
85100
}

apps/web/content/docs/components/accordion.mdx

Lines changed: 0 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -12,96 +12,3 @@ radix:
1212
<AccordionDemo />
1313
</div>
1414
</ComponentExample>
15-
16-
## Installation
17-
18-
```bash
19-
npx shadcn-ui add accordion
20-
```
21-
22-
<Accordion type="single" collapsible>
23-
<AccordionItem value="manual-installation">
24-
<AccordionTrigger>Manual Installation</AccordionTrigger>
25-
<AccordionContent>
26-
27-
1. Install the `@radix-ui/react-accordion` component from radix-ui:
28-
29-
```bash
30-
npm install @radix-ui/react-accordion
31-
```
32-
33-
2. Copy and paste the following code into your project.
34-
35-
<ComponentSource src='/components/ui/accordion.tsx' />
36-
37-
<Callout>
38-
39-
This is the `<Accordion />` primitive. You can place it in a file at `components/ui/accordion.tsx`.
40-
41-
</Callout>
42-
</AccordionContent>
43-
</AccordionItem>
44-
</Accordion>
45-
46-
## tailwind.config.js
47-
48-
Add the following animations to your `tailwind.config.js` file:
49-
50-
```ts
51-
/** @type {import('tailwindcss').Config} */
52-
module.exports = {
53-
theme: {
54-
extend: {
55-
keyframes: {
56-
'accordion-down': {
57-
from: { height: 0 },
58-
to: { height: 'var(--radix-accordion-content-height)' },
59-
},
60-
'accordion-up': {
61-
from: { height: 'var(--radix-accordion-content-height)' },
62-
to: { height: 0 },
63-
},
64-
},
65-
animation: {
66-
'accordion-down': 'accordion-down 0.2s ease-out',
67-
'accordion-up': 'accordion-up 0.2s ease-out',
68-
},
69-
},
70-
},
71-
};
72-
```
73-
74-
## Usage
75-
76-
```tsx
77-
import {
78-
Accordion,
79-
AccordionContent,
80-
AccordionItem,
81-
AccordionTrigger,
82-
} from '@/components/ui/accordion';
83-
```
84-
85-
```tsx
86-
<Accordion type='single' collapsible>
87-
<AccordionItem value='item-1'>
88-
<AccordionTrigger>접근 가능성은 어떤가요 ?</AccordionTrigger>
89-
<AccordionContent>
90-
WAI-ARIA 의 디자인 패턴은 차용 했습니다.
91-
</AccordionContent>
92-
<AccordionContent>
93-
Yes. It adheres to the WAI-ARIA design pattern.
94-
</AccordionContent>
95-
</AccordionItem>
96-
<AccordionItem value='item-2'>
97-
<AccordionTrigger>ㅇㅇㅇ</AccordionTrigger>
98-
<AccordionContent>Yㅇㅇㅇ</AccordionContent>
99-
</AccordionItem>
100-
<AccordionItem value='item-3'>
101-
<AccordionTrigger>Is it ㅇㅇㅇ?</AccordionTrigger>
102-
<AccordionContent>
103-
Yes. It adheres to the WAI-ARIA design pattern.
104-
</AccordionContent>
105-
</AccordionItem>
106-
</Accordion>
107-
```

0 commit comments

Comments
 (0)