master
parent
3010ec84f3
commit
d1de5b0d43
|
|
@ -5,30 +5,40 @@ import { useEffect, useRef, useState } from "react";
|
||||||
* BottomUtils 组件动画 Hook
|
* BottomUtils 组件动画 Hook
|
||||||
*
|
*
|
||||||
* 功能说明:
|
* 功能说明:
|
||||||
* 1. 监听显示/隐藏状态变化
|
* 1. 监听显示/隐藏状态变化,执行从下向上进入、从上向下离开的滑动动画
|
||||||
* 2. 执行从下向上进入、从上向下离开的动画效果
|
* 2. 支持模式切换(港口工具 ↔ 分公司工具),实现平滑过渡
|
||||||
* 3. 支持模式切换(港口工具 ↔ 分公司工具)
|
* 3. 支持首次加载动画,初始化时自动进入
|
||||||
* 4. 支持首次加载动画
|
* 4. 使用延迟显示模式,确保动画过渡完整可见
|
||||||
*
|
*
|
||||||
* 动画效果:
|
* 动画参数配置:
|
||||||
* - 进入动画:从下方 100px 处滑入,同时淡入(y: 100 → 0, opacity: 0 → 1)
|
* - 进入动画:y: 100 → 0(从下向上滑入),opacity: 0 → 1(淡入)
|
||||||
* - 离开动画:向下方 100px 处滑出,同时淡出(y: 0 → 100, opacity: 1 → 0)
|
* 时长:0.5s,缓动:easeOut(先快后慢,营造舒适的进入体验)
|
||||||
|
* - 离开动画:y: 0 → 100(从上向下滑出),opacity: 1 → 0(淡出)
|
||||||
|
* 时长:0.3s,缓动:easeIn(先慢后快,让用户感觉到响应迅速)
|
||||||
*
|
*
|
||||||
* 模式切换流程:
|
* 模式切换动画流程:
|
||||||
* 1. 旧模式执行离开动画(0.3秒)
|
* 1. 旧模式离开(0.3s):向下滑出并淡出
|
||||||
* 2. 离开动画完成后,更新 displayedMode 为新模式
|
* 2. 更新 displayedMode 为新模式
|
||||||
* 3. 设置元素到初始位置(下方 100px)
|
* 3. 新模式进入(0.5s):从下方滑入并淡入
|
||||||
* 4. 执行新模式的进入动画(0.5秒)
|
|
||||||
*
|
*
|
||||||
* @param {boolean} shouldShowPort - 是否应该显示港口工具
|
* @param {boolean} shouldShowPort - 是否应该显示港口工具
|
||||||
* @param {boolean} shouldShowBranchOffice - 是否应该显示分公司工具
|
* @param {boolean} shouldShowBranchOffice - 是否应该显示分公司工具
|
||||||
* @returns {object} 返回包含以下属性的对象:
|
* @returns {object} 返回包含以下属性的对象:
|
||||||
* - controls: motion 动画控制器
|
* - controls: motion 动画控制器,绑定到 motion.div 的 animate 属性
|
||||||
* - displayedMode: 延迟显示的模式('port' | 'branchOffice' | null)
|
* - displayedMode: 延迟显示的模式,用于决定渲染 port 还是 branchOffice 内容
|
||||||
* - isVisible: 元素可见性状态
|
* - isVisible: 元素可见性状态,用于控制内容的显示/隐藏
|
||||||
*/
|
*/
|
||||||
export function useBottomUtilsAnimation(shouldShowPort, shouldShowBranchOffice) {
|
export function useBottomUtilsAnimation(shouldShowPort, shouldShowBranchOffice) {
|
||||||
// ==================== 状态和引用管理 ====================
|
// ==================== 状态管理 ====================
|
||||||
|
//
|
||||||
|
// 动画时长说明:
|
||||||
|
// - 进入动画:0.5s,较慢,营造舒适的进入体验
|
||||||
|
// - 离开动画:0.3s,较快,让用户感觉到响应迅速
|
||||||
|
//
|
||||||
|
// 缓动函数说明:
|
||||||
|
// - easeOut:进入时使用,先快后慢,更自然
|
||||||
|
// - easeIn:离开时使用,先慢后快,快速消失
|
||||||
|
// ====================
|
||||||
|
|
||||||
// motion 动画控制器,用于控制 motion.div 的动画
|
// motion 动画控制器,用于控制 motion.div 的动画
|
||||||
const controls = useAnimation();
|
const controls = useAnimation();
|
||||||
|
|
@ -43,16 +53,29 @@ export function useBottomUtilsAnimation(shouldShowPort, shouldShowBranchOffice)
|
||||||
|
|
||||||
// 延迟显示的模式状态
|
// 延迟显示的模式状态
|
||||||
// 作用:在模式切换时,先显示旧模式,动画完成后再更新为新模式
|
// 作用:在模式切换时,先显示旧模式,动画完成后再更新为新模式
|
||||||
// 这样确保用户看到完整的离开动画和进入动画
|
// 这样确保用户看到完整的离开动画和进入动画,实现平滑过渡
|
||||||
const [displayedMode, setDisplayedMode] = useState(
|
const [displayedMode, setDisplayedMode] = useState(
|
||||||
shouldShowPort ? "port" : shouldShowBranchOffice ? "branchOffice" : null,
|
shouldShowPort ? "port" : shouldShowBranchOffice ? "branchOffice" : null,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 元素可见性状态
|
// 元素可见性状态
|
||||||
// 注意:motion.div 始终挂载,isVisible 只控制内容的显示/隐藏
|
// 注意:motion.div 始终挂载,isVisible 只控制内容的显示/隐藏
|
||||||
// 这样可以保证动画控制器始终有效
|
// 这样可以保证动画控制器始终有效,避免重复创建/销毁
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
|
|
||||||
|
// ==================== 主题:监听状态变化并执行动画 ====================
|
||||||
|
//
|
||||||
|
// useEffect 的职责:
|
||||||
|
// 1. 监听 shouldShowPort 和 shouldShowBranchOffice 的变化
|
||||||
|
// 2. 根据不同场景自动执行相应的动画
|
||||||
|
//
|
||||||
|
// 场景说明:
|
||||||
|
// - 首次渲染:初始化并执行进入动画
|
||||||
|
// - 隐藏状态:执行离开动画并隐藏元素
|
||||||
|
// - 模式切换:旧模式离开 -> 新模式进入
|
||||||
|
// - 显示状态:从隐藏切换到可见
|
||||||
|
// ====================
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// ==================== 计算当前应该显示的模式 ====================
|
// ==================== 计算当前应该显示的模式 ====================
|
||||||
// 根据 shouldShowPort 和 shouldShowBranchOffice 判断当前应该显示哪个模式
|
// 根据 shouldShowPort 和 shouldShowBranchOffice 判断当前应该显示哪个模式
|
||||||
|
|
@ -192,7 +215,6 @@ export function useBottomUtilsAnimation(shouldShowPort, shouldShowBranchOffice)
|
||||||
}, [shouldShowPort, shouldShowBranchOffice, controls, displayedMode, isVisible]);
|
}, [shouldShowPort, shouldShowBranchOffice, controls, displayedMode, isVisible]);
|
||||||
|
|
||||||
// ==================== 返回值 ====================
|
// ==================== 返回值 ====================
|
||||||
// 返回动画控制器、延迟显示的模式和可见性状态
|
|
||||||
return {
|
return {
|
||||||
controls, // motion 动画控制器,绑定到 motion.div 的 animate 属性
|
controls, // motion 动画控制器,绑定到 motion.div 的 animate 属性
|
||||||
displayedMode, // 延迟显示的模式,用于决定渲染 port 还是 branchOffice 内容
|
displayedMode, // 延迟显示的模式,用于决定渲染 port 还是 branchOffice 内容
|
||||||
|
|
|
||||||
|
|
@ -2,52 +2,91 @@ import { useAnimation } from "motion/react";
|
||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PortUtils 父子项动画 Hook
|
* PortUtils 父子项动画 Hook(港口工具栏)
|
||||||
*
|
*
|
||||||
* 功能说明:
|
* 功能说明:
|
||||||
* 1. 实现父级和子级的复杂交互动画
|
* 1. 实现父级和子级的复杂交互动画序列
|
||||||
* 2. 支持点击父级展开/收起子级
|
* 2. 支持点击父级展开/收起子级,展现流畅的动画效果
|
||||||
* 3. 初始化时不执行动画,只执行 useBottomUtilsAnimation 的容器动画
|
* 3. 初始化时不执行动画,首次点击后执行完整的展开/收起动画序列
|
||||||
* 4. 首次点击后执行完整的展开/收起动画序列
|
* 4. 使用依次动画(stagger)和并行动画,营造层次感
|
||||||
*
|
*
|
||||||
* 动画流程:
|
* 动画流程详解:
|
||||||
*
|
*
|
||||||
* 【初始显示】
|
* 【初始显示】
|
||||||
* - 使用 useBottomUtilsAnimation 的主容器动画
|
* - 使用 useBottomUtilsAnimation 的主容器动画
|
||||||
* - 不执行父级的依次显示动画
|
* - 不执行父级的依次显示动画,保持静止
|
||||||
*
|
*
|
||||||
* 【点击父级展开子级】
|
* 【展开动画序列】(点击父级展开子级)
|
||||||
* 1. 所有父级依次下落隐藏(y: 0 → 30, opacity: 1 → 0, x: 0)
|
* 1. 所有父级依次下落隐藏(stagger:0.05s 间隔)
|
||||||
* 2. 选中的父级显示回来(y: 30 → 0, opacity: 0 → 1, x: 0)
|
* - y: 0 → 30(向下移动)
|
||||||
* 3. 选中的父级移动到最左边(x: 0 → -offsetLeft)
|
* - opacity: 1 → 0(淡出)
|
||||||
* 4. 子级展开(opacity: 0 → 1)
|
* - x: 0(重置水平位置)
|
||||||
|
* - 单个动画时长:0.1s
|
||||||
|
* 2. 选中的父级单独显示回来
|
||||||
|
* - y: 30 → 0(回到原位)
|
||||||
|
* - opacity: 0 → 1(淡入)
|
||||||
|
* - x: 0(保持水平位置)
|
||||||
|
* - 动画时长:0.1s
|
||||||
|
* 3. 选中的父级移动到最左边
|
||||||
|
* - x: 0 → -offsetLeft(计算目标位置)
|
||||||
|
* - 缓动:easeInOut(平滑过渡)
|
||||||
|
* - 动画时长:0.1s
|
||||||
|
* 4. 子级展开
|
||||||
|
* - opacity: 0 → 1(淡入)
|
||||||
|
* - 延迟:0.05s(等待步骤3完成)
|
||||||
|
* - 动画时长:0.1s
|
||||||
*
|
*
|
||||||
* 【点击父级收起子级】
|
* 【收起动画序列】(点击父级收起子级)
|
||||||
* 1. 子级收起(opacity: 1 → 0)
|
* 1. 子级收起
|
||||||
* 2. 所有父级在各自当前位置下落(y: 0 → 30, opacity: 1 → 0,保持 x 位置)
|
* - opacity: 1 → 0(淡出)
|
||||||
* 3. 所有父级回到原位(x: 当前位置 → 0,仍在下方)
|
* - 动画时长:0.1s
|
||||||
* 4. 所有父级依次从下向上显示(y: 30 → 0, opacity: 0 → 1, x: 0)
|
* 2. 所有父级在各自当前位置下落(并行执行)
|
||||||
|
* - y: 0 → 30(向下移动)
|
||||||
|
* - opacity: 1 → 0(淡出)
|
||||||
|
* - 保持各自的 x 位置(选中的在左边,其他在原位)
|
||||||
|
* - 动画时长:0.1s
|
||||||
|
* 3. 所有父级回到原位(并行执行,仍在下方)
|
||||||
|
* - x: 当前位置 → 0(重置水平位置)
|
||||||
|
* - 动画时长:0.1s
|
||||||
|
* 4. 所有父级依次从下向上显示(stagger:0.05s 间隔)
|
||||||
|
* - y: 30 → 0(回到原位)
|
||||||
|
* - opacity: 0 → 1(淡入)
|
||||||
|
* - x: 0(保持水平位置)
|
||||||
|
* - 单个动画时长:0.1s
|
||||||
|
*
|
||||||
|
* 动画配置:
|
||||||
|
* - duration: 0.1s(单个动画的持续时间)
|
||||||
|
* - staggerDelay: 0.05s(每个父级的延迟时间,用于依次动画)
|
||||||
*
|
*
|
||||||
* @param {number} parentCount - 父级数量
|
* @param {number} parentCount - 父级数量
|
||||||
* @param {number} currentIndex - 当前选中的父级索引(-1 表示未选中)
|
* @param {number} currentIndex - 当前选中的父级索引(-1 表示未选中)
|
||||||
* @param {boolean} isPortMode - 是否是港口模式
|
* @param {boolean} isPortMode - 是否是港口模式
|
||||||
* @returns {object} 返回包含以下属性的对象:
|
* @returns {object} 返回包含以下属性的对象:
|
||||||
* - parentControls: 父级动画控制器数组
|
* - parentControls: 父级动画控制器数组,每个父级独立控制
|
||||||
* - childControls: 子级动画控制器
|
* - childControls: 子级动画控制器,控制子级的展开/收起
|
||||||
* - optionRefs: 父级元素引用数组
|
* - optionRefs: 父级元素引用数组,用于计算位置偏移量
|
||||||
* - isAnimating: 是否正在执行动画
|
* - isAnimating: 是否正在执行动画,用于防止动画冲突
|
||||||
* - hasInteracted: 是否已经交互过
|
* - hasInteracted: 是否已经交互过,首次点击后才执行完整动画
|
||||||
* - shouldHideInactive: 是否应该隐藏非激活的父级
|
* - shouldHideInactive: 是否应该隐藏非激活的父级,子级展开时隐藏其他父级
|
||||||
* - animationConfig: 动画配置对象(包含 duration 和 staggerDelay)
|
* - animationConfig: 动画配置对象(包含 duration 和 staggerDelay)
|
||||||
*/
|
*/
|
||||||
export function usePortUtilsAnimation(parentCount, currentIndex, isPortMode) {
|
export function usePortUtilsAnimation(parentCount, currentIndex, isPortMode) {
|
||||||
// ==================== 动画配置 ====================
|
// ==================== 动画配置 ====================
|
||||||
const animationConfig = {
|
const animationConfig = {
|
||||||
duration: 0.1, // 单个动画的持续时间(秒)
|
duration: 0.1, // 单个动画的持续时间(秒)
|
||||||
staggerDelay: 0.05, // 每个父级的延迟时间(秒),用于依次动画
|
staggerDelay: 0.05, // 每个父级的延迟时间(秒),用于依次动画产生波浪效果
|
||||||
};
|
};
|
||||||
|
|
||||||
// ==================== 状态和引用管理 ====================
|
// ==================== 状态管理 ====================
|
||||||
|
//
|
||||||
|
// 动画时长说明:
|
||||||
|
// - 单个动画:0.1s,非常快,营造敏捷的响应感
|
||||||
|
// - stagger 延迟:0.05s,相邻父级之间的时间间隔,产生波浪效果
|
||||||
|
//
|
||||||
|
// 并行 vs 依次:
|
||||||
|
// - 并行动画:多个动画同时执行(如所有父级同时回到原位)
|
||||||
|
// - 依次动画:动画按顺序执行(如父级依次下落,每个间隔 0.05s)
|
||||||
|
// ====================
|
||||||
|
|
||||||
// 标记是否正在执行动画
|
// 标记是否正在执行动画
|
||||||
// 作用:防止在动画执行过程中触发新的动画,避免动画冲突
|
// 作用:防止在动画执行过程中触发新的动画,避免动画冲突
|
||||||
|
|
@ -58,17 +97,17 @@ export function usePortUtilsAnimation(parentCount, currentIndex, isPortMode) {
|
||||||
const [hasInteracted, setHasInteracted] = useState(false);
|
const [hasInteracted, setHasInteracted] = useState(false);
|
||||||
|
|
||||||
// 标记是否应该隐藏非激活的父级
|
// 标记是否应该隐藏非激活的父级
|
||||||
// 作用:当子级展开时,隐藏未选中的父级,避免干扰
|
// 作用:当子级展开时,隐藏未选中的父级,避免视觉干扰
|
||||||
// 在动画执行期间设置为 false,让所有父级参与动画
|
// 在动画执行期间设置为 false,让所有父级参与动画
|
||||||
// 动画完成后设置为 true,隐藏未选中的父级
|
// 动画完成后设置为 true,隐藏未选中的父级
|
||||||
const [shouldHideInactive, setShouldHideInactive] = useState(false);
|
const [shouldHideInactive, setShouldHideInactive] = useState(false);
|
||||||
|
|
||||||
// 父级元素的 ref 数组
|
// 父级元素的 ref 数组
|
||||||
// 作用:获取父级 DOM 元素,用于计算位置偏移量
|
// 作用:获取父级 DOM 元素,用于计算位置偏移量(offsetLeft)
|
||||||
const optionRefs = useRef([]);
|
const optionRefs = useRef([]);
|
||||||
|
|
||||||
// 为每个父级创建独立的动画控制器
|
// 为每个父级创建独立的动画控制器
|
||||||
// 作用:每个父级可以独立控制动画
|
// 作用:每个父级可以独立控制动画,实现依次动画效果
|
||||||
const parentControls = Array.from({ length: parentCount }, () => useAnimation());
|
const parentControls = Array.from({ length: parentCount }, () => useAnimation());
|
||||||
|
|
||||||
// 子级的动画控制器
|
// 子级的动画控制器
|
||||||
|
|
@ -76,12 +115,28 @@ export function usePortUtilsAnimation(parentCount, currentIndex, isPortMode) {
|
||||||
const childControls = useAnimation();
|
const childControls = useAnimation();
|
||||||
|
|
||||||
// ==================== 初始化 ====================
|
// ==================== 初始化 ====================
|
||||||
|
// 设置子级为初始状态(隐藏),确保一开始不可见
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 设置子级为初始状态(隐藏)
|
|
||||||
// 作用:确保子级一开始不可见
|
|
||||||
childControls.set({ opacity: 0 });
|
childControls.set({ opacity: 0 });
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// ==================== 主题:展开/收起动画序列 ====================
|
||||||
|
//
|
||||||
|
// 展开动画(performExpandAnimation):
|
||||||
|
// 1. 所有父级依次下落隐藏(stagger 效果)
|
||||||
|
// 2. 选中的父级单独显示回来
|
||||||
|
// 3. 选中的父级移动到最左边
|
||||||
|
// 4. 子级展开
|
||||||
|
//
|
||||||
|
// 收起动画(performCollapseAnimation):
|
||||||
|
// 1. 子级收起
|
||||||
|
// 2. 所有父级在各自当前位置下落(并行)
|
||||||
|
// 3. 所有父级回到原位(并行)
|
||||||
|
// 4. 所有父级依次从下向上显示(stagger 效果)
|
||||||
|
//
|
||||||
|
// 使用 useCallback 缓存函数,避免不必要的重建
|
||||||
|
// ====================
|
||||||
|
|
||||||
// ==================== 展开动画序列 ====================
|
// ==================== 展开动画序列 ====================
|
||||||
const performExpandAnimation = useCallback(async () => {
|
const performExpandAnimation = useCallback(async () => {
|
||||||
if (isAnimating)
|
if (isAnimating)
|
||||||
|
|
@ -253,14 +308,13 @@ export function usePortUtilsAnimation(parentCount, currentIndex, isPortMode) {
|
||||||
}, [currentIndex, isPortMode]);
|
}, [currentIndex, isPortMode]);
|
||||||
|
|
||||||
// ==================== 返回值 ====================
|
// ==================== 返回值 ====================
|
||||||
// 返回动画控制器和相关状态,供组件使用
|
|
||||||
return {
|
return {
|
||||||
parentControls, // 父级动画控制器数组
|
parentControls, // 父级动画控制器数组,每个父级独立控制
|
||||||
childControls, // 子级动画控制器
|
childControls, // 子级动画控制器,控制子级的展开/收起
|
||||||
optionRefs, // 父级元素引用数组
|
optionRefs, // 父级元素引用数组,用于计算位置偏移量
|
||||||
isAnimating, // 是否正在执行动画
|
isAnimating, // 是否正在执行动画,用于防止动画冲突
|
||||||
hasInteracted, // 是否已经交互过
|
hasInteracted, // 是否已经交互过,首次点击后才执行完整动画
|
||||||
shouldHideInactive, // 是否应该隐藏非激活的父级
|
shouldHideInactive, // 是否应该隐藏非激活的父级,子级展开时隐藏其他父级
|
||||||
animationConfig, // 动画配置对象(包含 duration 和 staggerDelay)
|
animationConfig, // 动画配置对象(包含 duration 和 staggerDelay)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,37 +5,35 @@ import { useEffect, useRef, useState } from "react";
|
||||||
* CenterUtils 弹跳动画 Hook
|
* CenterUtils 弹跳动画 Hook
|
||||||
*
|
*
|
||||||
* 功能说明:
|
* 功能说明:
|
||||||
* 1. 监听组件的显示/隐藏状态变化
|
* 1. 监听组件的显示/隐藏状态变化,执行炫酷的弹跳动画效果
|
||||||
* 2. 执行炫酷的弹跳动画效果(类似 animate.css 的 bounceIn/bounceOut)
|
* 2. 支持两种动画类型:上下弹跳(up-down)和缩放弹跳(scale)
|
||||||
* 3. 支持多种动画类型(上下弹跳、缩放弹跳)
|
* 3. 使用位置、缩放、透明度三重动画效果,营造生动的视觉体验
|
||||||
* 4. 使用弹跳、缩放、透明度三重动画效果,让动画更加生动
|
* 4. 延迟隐藏机制,确保离开动画完整播放后再移除组件
|
||||||
* 5. 延迟隐藏机制,确保离开动画完整播放
|
|
||||||
*
|
*
|
||||||
* 动画效果详解(上下弹跳模式 up-down):
|
* 动画参数配置:
|
||||||
* - 进入动画:
|
* - up-down 模式进入动画:
|
||||||
* - 位置:从上方 120px 处冲到下方 40px(超出目标),轻微上弹到 -10px,最后稳定到 0
|
* - 位置关键帧:y: [-120, 40, -10, 0](从上方120px -> 冲到下方40px -> 轻微上弹 -> 稳定)
|
||||||
* - 缩放:从 0.9 倍缩小到 1.05 倍(轻微放大),轻微缩小到 0.98,回到正常 1.0
|
* - 缩放关键帧:scale: [0.9, 1.05, 0.98, 1](90% -> 105% -> 98% -> 100%)
|
||||||
* - 透明度:从透明到不透明
|
* - 透明度关键帧:opacity: [0, 1, 1, 1](透明 -> 不透明)
|
||||||
* - 时长:0.7 秒
|
* - 时长:0.7s,缓动:[0.34, 1.56, 0.64, 1](强弹性曲线,产生过冲和回弹)
|
||||||
* - 缓动:使用强弹性贝塞尔曲线 [0.34, 1.56, 0.64, 1],产生明显的过冲和回弹效果
|
* - 关键帧时间点:times: [0, 0.5, 0.75, 1]
|
||||||
*
|
*
|
||||||
* - 离开动画:
|
* - up-down 模式离开动画:
|
||||||
* - 位置:从正常位置向上弹跳 50px,回落到 20px,然后向上离开到 -120px
|
* - 位置关键帧:y: [0, -50, 20, -120](正常 -> 向上弹跳50px -> 回落到20px -> 向上离开到-120px)
|
||||||
* - 缩放:轻微放大到 1.02,然后缩小到 0.85
|
* - 缩放关键帧:scale: [1, 1.02, 0.95, 0.85](100% -> 102% -> 95% -> 85%)
|
||||||
* - 透明度:保持可见再淡出
|
* - 透明度关键帧:opacity: [1, 1, 0.8, 0](保持可见 -> 淡出)
|
||||||
* - 时长:0.5 秒
|
* - 时长:0.5s,缓动:[0.68, -0.6, 0.32, 1.6](超弹性曲线,产生夸张反向弹跳)
|
||||||
* - 缓动:使用超弹性曲线 [0.68, -0.6, 0.32, 1.6],产生夸张的反向弹跳
|
* - 关键帧时间点:times: [0, 0.2, 0.5, 1]
|
||||||
*
|
*
|
||||||
* 动画效果详解(缩放弹跳模式 scale):
|
* - scale 模式进入动画:
|
||||||
* - 进入动画:
|
* - 缩放关键帧:scale: [0, 1.15, 0.92, 1](0 -> 115% -> 92% -> 100%)
|
||||||
* - 缩放:从 0 放大到 1.15 倍(超出目标),缩小到 0.92,回到正常 1.0
|
* - 时长:0.7s,缓动:[0.34, 1.56, 0.64, 1]
|
||||||
* - 时长:0.7 秒
|
* - 关键帧时间点:times: [0, 0.4, 0.7, 1]
|
||||||
* - 缓动:使用强弹性贝塞尔曲线
|
|
||||||
*
|
*
|
||||||
* - 离开动画:
|
* - scale 模式离开动画:
|
||||||
* - 缩放:从 1.0 放大到 1.1,缩小到 0.85,最后消失到 0
|
* - 缩放关键帧:scale: [1, 1.1, 0.85, 0](100% -> 110% -> 85% -> 0)
|
||||||
* - 时长:0.5 秒
|
* - 时长:0.5s,缓动:[0.68, -0.6, 0.32, 1.6]
|
||||||
* - 缓动:使用超弹性曲线
|
* - 关键帧时间点:times: [0, 0.3, 0.6, 1]
|
||||||
*
|
*
|
||||||
* @param {boolean} isVisible - 控制组件的显示/隐藏
|
* @param {boolean} isVisible - 控制组件的显示/隐藏
|
||||||
* - true: 组件应该显示,执行进入动画
|
* - true: 组件应该显示,执行进入动画
|
||||||
|
|
@ -45,23 +43,46 @@ import { useEffect, useRef, useState } from "react";
|
||||||
* - 'scale':缩放弹跳动画(从中心放大进入,缩小离开)
|
* - 'scale':缩放弹跳动画(从中心放大进入,缩小离开)
|
||||||
* @returns {object} 返回包含以下属性的对象:
|
* @returns {object} 返回包含以下属性的对象:
|
||||||
* - controls: motion 动画控制器,绑定到 motion.div 的 animate 属性
|
* - controls: motion 动画控制器,绑定到 motion.div 的 animate 属性
|
||||||
* - isVisible: 延迟的可见性状态(用于等待离开动画完成后再隐藏组件)
|
* - isVisible: 延迟的可见性状态,用于条件渲染(等待离开动画完成)
|
||||||
*/
|
*/
|
||||||
export function useCenterUtilsAnimation(isVisible, type = "up-down") {
|
export function useCenterUtilsAnimation(isVisible, type = "up-down") {
|
||||||
// ==================== 状态和引用管理 ====================
|
// ==================== 状态管理 ====================
|
||||||
|
//
|
||||||
|
// 动画时长说明:
|
||||||
|
// - 进入动画:0.7s,较慢,展示完整的弹跳效果
|
||||||
|
// - 离开动画:0.5s,较快,让用户感觉到响应迅速
|
||||||
|
//
|
||||||
|
// 缓动函数说明:
|
||||||
|
// - 进入动画:[0.34, 1.56, 0.64, 1],强弹性曲线,产生明显的过冲和回弹
|
||||||
|
// - 离开动画:[0.68, -0.6, 0.32, 1.6],超弹性曲线,产生夸张的反向弹跳
|
||||||
|
//
|
||||||
|
// 三重动画效果:
|
||||||
|
// - 位置动画(y)/ 缩放动画(scale):产生弹跳的物理运动效果
|
||||||
|
// - 透明度动画(opacity):平滑的进入和离开
|
||||||
|
// ====================
|
||||||
|
|
||||||
// motion 动画控制器,用于控制 motion.div 的动画
|
// motion 动画控制器,用于控制 motion.div 的动画
|
||||||
const controls = useAnimation();
|
const controls = useAnimation();
|
||||||
|
|
||||||
// 标记是否是首次渲染
|
// 标记是否是首次渲染
|
||||||
// 作用:首次渲染时需要特殊处理,确保初始状态正确设置
|
// 作用:首次渲染时需要特殊处理,确保初始状态正确设置
|
||||||
// 首次渲染不执行离开动画,直接执行进入动画
|
|
||||||
const isFirstRender = useRef(true);
|
const isFirstRender = useRef(true);
|
||||||
|
|
||||||
// 组件的实际显示状态
|
// 组件的实际显示状态
|
||||||
// 作用:延迟隐藏,确保离开动画执行完成后才将组件从 DOM 中移除
|
// 作用:延迟隐藏,确保离开动画执行完成后才将组件从 DOM 中移除
|
||||||
const [displayState, setDisplayState] = useState(isVisible);
|
const [displayState, setDisplayState] = useState(isVisible);
|
||||||
|
|
||||||
|
// ==================== 主题:监听可见性变化并执行弹跳动画 ====================
|
||||||
|
//
|
||||||
|
// useEffect 的职责:
|
||||||
|
// 1. 监听 isVisible 和 type 的变化
|
||||||
|
// 2. 根据显示/隐藏状态执行相应的弹跳动画
|
||||||
|
//
|
||||||
|
// 场景说明:
|
||||||
|
// - 显示状态:根据 type 执行 up-down 或 scale 弹跳进入动画
|
||||||
|
// - 隐藏状态:根据 type 执行 up-down 或 scale 弹跳离开动画
|
||||||
|
// ====================
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// ==================== 显示状态:执行进入动画 ====================
|
// ==================== 显示状态:执行进入动画 ====================
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
|
|
@ -176,11 +197,9 @@ export function useCenterUtilsAnimation(isVisible, type = "up-down") {
|
||||||
}
|
}
|
||||||
}, [isVisible, type, controls, displayState]);
|
}, [isVisible, type, controls, displayState]);
|
||||||
|
|
||||||
// 返回动画控制器和延迟的可见性状态
|
// ==================== 返回值 ====================
|
||||||
// - controls:用于绑定到 motion.div 的 animate 属性
|
|
||||||
// - isVisible:延迟的可见性状态,用于条件渲染(等待离开动画完成)
|
|
||||||
return {
|
return {
|
||||||
controls,
|
controls, // motion 动画控制器,绑定到 motion.div 的 animate 属性
|
||||||
isVisible: displayState,
|
isVisible: displayState, // 延迟的可见性状态,用于条件渲染(等待离开动画完成)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue