滑动动画
通过向特定方向滑动元素来创建动态移动效果
滑动动画
滑动(Slide)动画使元素从特定方向平滑地出现或消失。在需要方向性过渡时非常有效。
基本用法
import { transition } from '@ssgoi/react';
import { slide } from '@ssgoi/react/transitions';
function Component() {
const [isVisible, setIsVisible] = useState(true);
return (
<div>
{isVisible && (
<div ref={transition({ key: 'slide-element', ...slide() })}>
应用了滑动动画的元素
</div>
)}
</div>
);
}
选项
interface SlideOptions {
direction?: 'left' | 'right' | 'up' | 'down'; // 滑动方向(默认:'left')
distance?: number | string; // 移动距离(默认:100)
opacity?: number; // 起始不透明度(默认:0)
fade?: boolean; // 添加淡入淡出效果(默认:true)
axis?: 'x' | 'y'; // 移动轴(direction的替代)
spring?: {
stiffness?: number; // 弹簧刚度(默认:400)
damping?: number; // 阻尼系数(默认:35)
};
}
选项说明
- direction: 滑动方向
'left'
: 从左侧出现'right'
: 从右侧出现'up'
: 从上方出现'down'
: 从下方出现
- distance: 移动距离(像素或CSS单位)
- opacity: 起始不透明度(0-1)
- fade: 与滑动同时使用淡入淡出效果
- axis: 简单的轴指定('x' 或 'y')
- spring: 弹簧物理设置
使用示例
方向滑动
// 从左滑动
const slideLeft = slide({
direction: 'left'
});
// 从右滑动
const slideRight = slide({
direction: 'right'
});
// 从上滑动
const slideUp = slide({
direction: 'up'
});
// 从下滑动
const slideDown = slide({
direction: 'down'
});
距离调整
// 短距离
const shortSlide = slide({
direction: 'left',
distance: 50 // 仅移动50px
});
// 长距离
const longSlide = slide({
direction: 'right',
distance: '100vw' // 移动屏幕宽度
});
// 使用rem单位
const remSlide = slide({
direction: 'up',
distance: '5rem'
});
无淡入淡出的滑动
const slideNoFade = slide({
direction: 'left',
fade: false, // 移除淡入淡出效果
opacity: 1 // 保持完全不透明状态
});
实用示例
侧边栏菜单
function Sidebar({ isOpen, onClose }) {
return (
<>
{isOpen && (
<>
{/* 背景遮罩 */}
<div
className="fixed inset-0 bg-black/50 z-40"
onClick={onClose}
/>
{/* 侧边栏 */}
<div
ref={transition({
key: 'sidebar',
...slide({
direction: 'left',
distance: 300,
fade: false
})
})}
className="fixed left-0 top-0 h-full w-72 bg-white shadow-lg z-50"
>
<nav className="p-4">
<h2>菜单</h2>
{/* 菜单项 */}
</nav>
</div>
</>
)}
</>
);
}
通知提示
function Toast({ message, isVisible }) {
return (
<>
{isVisible && (
<div
ref={transition({
key: 'toast',
...slide({
direction: 'up',
distance: 100
})
})}
className="fixed bottom-4 right-4 bg-gray-800 text-white px-4 py-2 rounded-lg shadow-lg"
>
{message}
</div>
)}
</>
);
}
标签内容切换
function TabContent({ activeTab }) {
const getSlideDirection = (tab) => {
const tabs = ['tab1', 'tab2', 'tab3'];
const currentIndex = tabs.indexOf(activeTab);
const targetIndex = tabs.indexOf(tab);
return currentIndex < targetIndex ? 'left' : 'right';
};
return (
<div className="relative overflow-hidden h-64">
{activeTab === 'tab1' && (
<div
ref={transition({
key: 'tab1',
...slide({ direction: getSlideDirection('tab1') })
})}
className="absolute inset-0 p-4"
>
标签1内容
</div>
)}
{activeTab === 'tab2' && (
<div
ref={transition({
key: 'tab2',
...slide({ direction: getSlideDirection('tab2') })
})}
className="absolute inset-0 p-4"
>
标签2内容
</div>
)}
</div>
);
}
轮播图滑动
function Carousel({ images, currentIndex }) {
return (
<div className="relative w-full h-96 overflow-hidden">
{images.map((image, index) => (
index === currentIndex && (
<img
key={index}
ref={transition({
key: `slide-${index}`,
...slide({
direction: 'left',
distance: '100%',
spring: { stiffness: 300, damping: 30 }
})
})}
src={image}
alt={`幻灯片 ${index + 1}`}
className="absolute inset-0 w-full h-full object-cover"
/>
)
))}
</div>
);
}
高级用法
基于轴的滑动
// X轴滑动(左右)
const slideX = slide({
axis: 'x', // 使用axis代替direction
distance: 200
});
// Y轴滑动(上下)
const slideY = slide({
axis: 'y',
distance: 150
});
连续动画
function SequentialSlides() {
const [step, setStep] = useState(0);
return (
<div>
{step >= 0 && (
<div
ref={transition({
key: 'step-1',
...slide({ direction: 'right', distance: 50 })
})}
>
步骤1
</div>
)}
{step >= 1 && (
<div
ref={transition({
key: 'step-2',
...slide({ direction: 'right', distance: 50 })
})}
>
步骤2
</div>
)}
<button onClick={() => setStep(step + 1)}>下一步</button>
</div>
);
}
响应式滑动
function ResponsiveSlide() {
const isMobile = window.innerWidth < 768;
const responsiveSlide = slide({
direction: isMobile ? 'up' : 'left',
distance: isMobile ? 50 : 100,
spring: {
stiffness: isMobile ? 500 : 400,
damping: 35
}
});
return (
<div ref={transition({ key: 'responsive', ...responsiveSlide })}>
响应式滑动
</div>
);
}
性能优化
transform: translate()
使用GPU加速,性能优秀- 不改变布局,不会触发重排
- 即使同时应用于多个元素也能保持流畅的动画
性能提示
// 好的做法:使用transform
const goodSlide = slide(); // 使用transform: translateX/Y
// 避免的做法:直接修改position
const badSlide = {
in: (element) => ({
tick: (progress) => {
// 触发重排!
element.style.left = `${(1 - progress) * -100}px`;
}
})
};
无障碍考虑
<div
ref={transition({
key: 'accessible-slide',
...slide()
})}
role="region"
aria-live="polite"
aria-label="滑动内容"
>
无障碍滑动内容
</div>
推荐使用场景
- 导航菜单:侧边栏、下拉菜单
- 通知/提示:从屏幕边缘出现
- 标签/步骤切换:带方向性的内容过渡
- 图片画廊:幻灯片、轮播图
- 表单步骤:多步骤表单的步骤间过渡