Skip to content

Commit

Permalink
feat: motion transform style 3d
Browse files Browse the repository at this point in the history
  • Loading branch information
wkylin committed Jan 20, 2025
1 parent da6eacf commit 6a7b46d
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 3 deletions.
92 changes: 92 additions & 0 deletions src/components/stateless/AnimationTabs/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
'use client'

Check failure on line 1 in src/components/stateless/AnimationTabs/index.jsx

View workflow job for this annotation

GitHub Actions / Qodana for JS

ESLint

ESLint: Install the 'eslint' package
import React, { useState } from 'react'
import { motion } from 'motion/react'
import clsx from 'clsx'

const AnimationTabs = ({ tabs: propTabs, containerClassName, activeTabClassName, tabClassName, contentClassName }) => {
const [active, setActive] = useState(propTabs[0])
const [tabs, setTabs] = useState(propTabs)

const moveSelectedTabToTop = (idx) => {
const newTabs = [...propTabs]
const selectedTab = newTabs.splice(idx, 1)
newTabs.unshift(selectedTab[0])
setTabs(newTabs)
setActive(newTabs[0])
}

const [hovering, setHovering] = useState(false)

return (
<>
<div
className={clsx(
'flex flex-row items-center justify-start [perspective:1000px] relative overflow-auto sm:overflow-visible no-visible-scrollbar max-w-full w-full',
containerClassName
)}
>
{propTabs.map((tab, idx) => (
<button
key={tab.title}
onClick={() => {
moveSelectedTabToTop(idx)
}}
onMouseEnter={() => setHovering(true)}
onMouseLeave={() => setHovering(false)}
className={clsx('relative px-4 py-2 rounded-full', tabClassName)}
style={{
transformStyle: 'preserve-3d',
}}
>
{active.value === tab.value && (
<motion.div

Check notice on line 42 in src/components/stateless/AnimationTabs/index.jsx

View workflow job for this annotation

GitHub Actions / Qodana for JS

Unresolved JSX component

Unresolved component motion.div
layoutId="clickedButton"
transition={{ type: 'spring', bounce: 0.3, duration: 0.6 }}
className={clsx('absolute inset-0 bg-gray-200 dark:bg-zinc-800 rounded-full', activeTabClassName)}
/>
)}

<span className="relative block text-black dark:text-white">{tab.title}</span>
</button>
))}
</div>
<FadeInDiv
tabs={tabs}
active={active}
key={active.value}
hovering={hovering}
className={clsx('mt-32', contentClassName)}
/>
</>
)
}

export const FadeInDiv = ({ className, tabs, hovering }) => {
const isActive = (tab) => {
return tab.value === tabs[0].value
}
return (
<div className="relative w-full h-full">
{tabs.map((tab, idx) => (
<motion.div

Check notice on line 71 in src/components/stateless/AnimationTabs/index.jsx

View workflow job for this annotation

GitHub Actions / Qodana for JS

Unresolved JSX component

Unresolved component motion.div
key={tab.value}
layoutId={tab.value}
style={{
scale: 1 - idx * 0.1,
top: hovering ? idx * -50 : 0,
zIndex: -idx,
opacity: idx < 3 ? 1 - idx * 0.1 : 0,
}}
animate={{
y: isActive(tab) ? [0, 40, 0] : 0,
}}
className={clsx('w-full h-full absolute top-0 left-0', className)}
>
{tab.content}
</motion.div>
))}
</div>
)
}

export default AnimationTabs
52 changes: 52 additions & 0 deletions src/pages/demo/index.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react'

Check failure on line 1 in src/pages/demo/index.jsx

View workflow job for this annotation

GitHub Actions / Qodana for JS

ESLint

ESLint: Install the 'eslint' package
import { Table } from 'antd'
import FixTabPanel from '@stateless/FixTabPanel'
import AnimationTabs from '@stateless/AnimationTabs'
const columns = [
{
title: 'Name',
Expand All @@ -20,9 +21,60 @@ const columns = [
dataIndex: 'address',
},
]

const tabs = [
{
title: 'Product',
value: 'product',
content: (
<div className="relative w-full h-full p-10 overflow-hidden text-xl font-bold text-white rounded-2xl md:text-4xl bg-gradient-to-br from-purple-700 to-violet-900">
<p>Product Tab</p>
</div>
),
},
{
title: 'Services',
value: 'services',
content: (
<div className="relative w-full h-full p-10 overflow-hidden text-xl font-bold text-white rounded-2xl md:text-4xl bg-gradient-to-br from-purple-700 to-violet-900">
<p>Services tab</p>
</div>
),
},
{
title: 'Playground',
value: 'playground',
content: (
<div className="relative w-full h-full p-10 overflow-hidden text-xl font-bold text-white rounded-2xl md:text-4xl bg-gradient-to-br from-purple-700 to-violet-900">
<p>Playground tab</p>
</div>
),
},
{
title: 'Content',
value: 'content',
content: (
<div className="relative w-full h-full p-10 overflow-hidden text-xl font-bold text-white rounded-2xl md:text-4xl bg-gradient-to-br from-purple-700 to-violet-900">
<p>Content tab</p>
</div>
),
},
{
title: 'Random',
value: 'random',
content: (
<div className="relative w-full h-full p-10 overflow-hidden text-xl font-bold text-white rounded-2xl md:text-4xl bg-gradient-to-br from-purple-700 to-violet-900">
<p>Random tab</p>
</div>
),
},
]
const ProDemo = () => {
return (
<FixTabPanel>
<div className="h-[20rem] [perspective:1000px] relative b flex flex-col max-w-5xl mx-auto w-full items-start justify-start">
<AnimationTabs tabs={tabs} />
</div>
<Table columns={columns} dataSource={[]} pagination={{ pageSize: 50 }} scroll={{ y: 240 }} />
</FixTabPanel>
)
Expand Down
14 changes: 13 additions & 1 deletion src/pages/motion/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
useMotionValue,
animate,
} from 'motion/react'
import clsx from 'clsx'
import FixTabPanel from '@stateless/FixTabPanel'
import HorizontalScrollParallax from '@stateless/HorizontalScroll'
import styles from './index.module.less'
Expand Down Expand Up @@ -267,11 +268,22 @@ const ParallaxVert = () => {
{navItems.map((item, index) => (
<li
key={index}
className={`${styles.navItem} ${index === activeNavItemIndex ? `${styles.navItemActive}` : ''}`}
className={styles.navItem}
ref={(el) => (navItemsRef.current[index] = el)}
onClick={() => handleNavItemClick(index)}
style={{
transformStyle: 'preserve-3d',
}}
>
{item}
{index === activeNavItemIndex && (
<motion.div

Check notice on line 280 in src/pages/motion/index.jsx

View workflow job for this annotation

GitHub Actions / Qodana for JS

Unresolved JSX component

Unresolved component motion.div
layoutId="clickedButton"
transition={{ type: 'spring', bounce: 0.3, duration: 0.6 }}
className={clsx('absolute inset-0 bg-gray-200 dark:bg-zinc-800 rounded-full')}
style={{ zIndex: -1 }}
/>
)}
</li>
))}
</ul>
Expand Down
4 changes: 2 additions & 2 deletions src/pages/motion/index.module.less
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
.navItem {
cursor: pointer;
padding: 5px 20px;
transition: all 0.3s ease;
color: #fff;
color: #000;
border-radius: 15px;
position: relative;
}

.navItemActive {
Expand Down

0 comments on commit 6a7b46d

Please sign in to comment.