Skip to content

Commit

Permalink
feat: shifting card
Browse files Browse the repository at this point in the history
  • Loading branch information
wkylin committed Oct 22, 2024
1 parent bca5a84 commit 81b9df5
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/components/stateless/AvatarCard/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const AvatarCard = ({ avatar, text }) => {
<div className={styles.avatarCard}>
<div className={styles.card}>
<figure>
<img alt="" src={avatar} />
<img alt="" src={avatar} className={styles.avatar} />
</figure>
<p className={styles.content}>{text}</p>
</div>
Expand Down
31 changes: 31 additions & 0 deletions src/components/stateless/GradientTracking/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React, { useLayoutEffect, useRef } from 'react'

import styles from './index.module.less'

const GradientTracking = () => {
const ref = useRef(null)

useLayoutEffect(() => {
const btn = ref.current
const handleMove = (e) => {
const rect = e.target.getBoundingClientRect()
const x = e.clientX - rect.left
const y = e.clientY - rect.top
btn.style.setProperty('--x', x + 'px')
btn.style.setProperty('--y', y + 'px')
}

btn.addEventListener('mousemove', handleMove)
return () => {
btn.removeEventListener('mousemove', handleMove)
}
}, [])

return (
<button className={styles.gradientTracking} ref={ref}>
<span>Hover me</span>
</button>
)
}

export default GradientTracking
37 changes: 37 additions & 0 deletions src/components/stateless/GradientTracking/index.module.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.gradientTracking {
position: relative;
margin: 10px 0;
background: #7983ff;
padding: 5px 10px;
font-size: 18px;
border: none;
border-radius: 8px;
color: white;
cursor: pointer;
outline: none;
overflow: hidden;
}

.gradientTracking span {
position: relative;
pointer-events: none;
}

.gradientTracking::before {
--size: 0;
content: '';
position: absolute;
left: var(--x);
top: var(--y);
width: var(--size);
height: var(--size);
background: radial-gradient(circle closest-side, pink, transparent);
transform: translate(-50%, -50%);
transition:
width 0.2s ease,
height 0.2s ease;
}

.gradientTracking:hover::before {
--size: 200px;
}
27 changes: 27 additions & 0 deletions src/components/stateless/ShiftingCard/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React, { useLayoutEffect, useRef } from 'react'
import styles from './index.module.less'
const ShiftingCard = () => {
const ref = useRef(null)
useLayoutEffect(() => {
const card = ref.current
const { x, y, width, height } = card.getBoundingClientRect()
const cx = x + width / 2
const cy = y + height / 2
const handleMove = (e) => {
const { pageX, pageY } = e
const dx = (cx - pageX) / (width / 2)
const dy = (cy - pageY) / (height / 2)
e.target.style.setProperty('--dx', dx)
e.target.style.setProperty('--dy', dy)
}
card.addEventListener('mousemove', handleMove)
return () => card.removeEventListener('mousemove', handleMove)
}, [])

return (
<div ref={ref} className={styles.shiftingCard}>
<h3>Shifting Card</h3>
</div>
)
}
export default ShiftingCard
31 changes: 31 additions & 0 deletions src/components/stateless/ShiftingCard/index.module.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* Perspective card */
.perspective-card {
transform: perspective(1500px) rotateY(15deg);
transition: transform 1s ease 0s;
}

.perspective-card:hover {
transform: perspective(3000px) rotateY(5deg);
}

/* Base card styles */
.shiftingCard {
position: relative;
display: inline-block;
box-sizing: border-box;
height: 360px;
width: 240px;
padding: 8px;
border-radius: 1rem;
box-shadow: rgba(0, 0, 0, 0.25) 0px 25px 50px -12px;
background-size: cover;
transform: rotateX(calc(10deg * var(--dx, 0))) rotateY(calc(10deg * var(--dy, 0)));
transition: transform 0.2s ease-out;
background-image: url('https://picsum.photos/id/1018/480/720');
}

.shiftingCard h3 {
width: 100%;
text-align: center;
margin-top: 30%;
}
17 changes: 15 additions & 2 deletions src/pages/home/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@ import StarRating from '@stateless/StarRating'
import IsometricCard from '@stateless/IsometricCard'
import AvatarCard from '@stateless/AvatarCard'
import LineBordered from '@stateless/LineBordered'
import GradientTracking from '@stateless/GradientTracking'
import ShiftingCard from '@stateless/ShiftingCard'

import { oneApiChat, prettyObject } from '@utils/aidFn'

import styles from './index.module.less'

const Home = () => {
const [aiText, setAiText] = useState('')
const aiTextRef = useRef(null)
Expand Down Expand Up @@ -149,10 +153,11 @@ const Home = () => {

return (
<FixTabPanel>
<h2>
<h2 style={{ marginBottom: 15 }}>
<TypedText>Cool! Hi, React & Ant Design!</TypedText>
</h2>
<h2>React version: {version}</h2>

<h2 className={styles.avatar}>React version: {version}</h2>

<section>
I love coding in <AlternatingText alternateText={['javascript', 'typescript', 'rect', 'vue']} />.
Expand All @@ -171,6 +176,14 @@ const Home = () => {
</section>
<StarRating value={2} />
<LineBordered text="A line bordered text." />

<section>
<GradientTracking />
</section>

<section>
<ShiftingCard />
</section>
<section style={{ width: 600, margin: '30px 0' }}>
<Input defaultValue={apiKey} placeholder="api key" onChange={changeApiKey} style={{ marginBottom: 20 }} />
<Flex align="center">
Expand Down
25 changes: 25 additions & 0 deletions src/pages/home/index.module.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.avatar {
animation: levitate 10s ease 1s infinite reverse;
}

@keyframes levitate {
0% {
transform: translateY(0);
}

30% {
transform: translateY(-10px);
}

50% {
transform: translateY(4px);
}

70% {
transform: translateY(-15px);
}

to {
transform: translateY(0);
}
}

0 comments on commit 81b9df5

Please sign in to comment.