@rxdrag/website-lib-core 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +1 -1
- package/package.json +11 -7
- package/src/component-logic/gsap.d.ts +4 -0
- package/src/component-logic/index.ts +8 -0
- package/src/component-logic/link-client.ts +33 -0
- package/src/component-logic/link.ts +50 -0
- package/src/component-logic/modal.ts +36 -0
- package/src/component-logic/motion.ts +272 -0
- package/src/component-logic/number.ts +45 -0
- package/src/component-logic/popover.ts +51 -0
- package/src/component-logic/tabs.ts +10 -0
- package/src/controller/AnimateController.ts +138 -0
- package/src/controller/AosController.ts +240 -0
- package/src/controller/FlipController.ts +339 -0
- package/src/controller/ModalController.ts +127 -0
- package/src/controller/NumberController.ts +161 -0
- package/src/controller/PageLoader.ts +163 -0
- package/src/controller/PopoverController.ts +116 -0
- package/src/controller/TabsController.ts +271 -0
- package/src/controller/applyAnimation.ts +86 -0
- package/src/controller/applyInitialState.ts +79 -0
- package/src/{scripts → controller}/consts.ts +0 -2
- package/src/controller/index.ts +9 -0
- package/src/controller/popup.ts +346 -0
- package/src/controller/utils.ts +48 -0
- package/src/entify/Entify.ts +354 -365
- package/src/entify/IEntify.ts +91 -0
- package/src/entify/index.ts +3 -2
- package/src/entify/lib/newQueryProductOptions.ts +2 -3
- package/src/entify/lib/newQueryProductsMediaOptions.ts +19 -18
- package/src/entify/lib/queryAllProducts.ts +11 -3
- package/src/entify/lib/queryFeaturedProducts.ts +3 -3
- package/src/entify/lib/queryLatestPosts.ts +2 -2
- package/src/entify/lib/queryOneTheme.ts +1 -1
- package/src/entify/lib/queryPostCategories.ts +3 -3
- package/src/entify/lib/queryPostSlugs.ts +2 -2
- package/src/entify/lib/queryPosts.ts +92 -92
- package/src/entify/lib/queryProductCategories.ts +3 -3
- package/src/entify/lib/queryProducts.ts +69 -69
- package/src/entify/lib/queryUserPosts.ts +2 -2
- package/src/entify/lib/searchProducts.ts +2 -2
- package/src/index.ts +3 -1
- package/src/lib/formatDate.ts +15 -0
- package/src/lib/index.ts +3 -0
- package/src/lib/pagination.ts +114 -0
- package/src/lib/utils.ts +119 -0
- package/src/motion/consts.ts +428 -598
- package/src/motion/convertToGsapVars.ts +102 -0
- package/src/motion/index.ts +5 -1
- package/src/motion/normalizeAnimation.ts +28 -0
- package/src/motion/normalizeAosAnimation.ts +22 -0
- package/src/motion/normalizePopupAnimation.ts +24 -0
- package/src/motion/types.ts +133 -46
- package/src/react/components/AttachmentIcon/index.tsx +53 -0
- package/src/react/components/ContactForm/index.tsx +341 -0
- package/src/react/components/Icon/index.tsx +10 -0
- package/src/react/components/Medias/index.tsx +347 -347
- package/src/react/components/ProductCard/ProductCta/index.tsx +7 -5
- package/src/react/components/RichTextOutline/index.tsx +76 -76
- package/src/react/components/Scroller.tsx +5 -1
- package/src/react/components/SearchInput.tsx +36 -34
- package/src/react/components/ToTop.tsx +63 -28
- package/src/react/components/index.ts +3 -1
- package/src/react/hooks/useScroll.ts +16 -10
- package/src/react/components/EnquiryForm/index.tsx +0 -334
- package/src/scripts/actions.ts +0 -304
- package/src/scripts/events.ts +0 -33
- package/src/scripts/index.ts +0 -3
- /package/src/react/components/{EnquiryForm → ContactForm}/Input.tsx +0 -0
- /package/src/react/components/{EnquiryForm → ContactForm}/Submit.tsx +0 -0
- /package/src/react/components/{EnquiryForm → ContactForm}/Textarea.tsx +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { AnimationConfig, convertToGsapVars } from "../motion";
|
|
2
|
+
import { gsap } from "gsap/dist/gsap";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 根据AnimationConfig选择并应用正确的GSAP动画方法
|
|
6
|
+
* @param element 目标元素
|
|
7
|
+
* @param config 动画配置
|
|
8
|
+
* @param options 额外选项
|
|
9
|
+
* @returns GSAP动画实例
|
|
10
|
+
*/
|
|
11
|
+
export function applyAnimation(
|
|
12
|
+
element: Element,
|
|
13
|
+
config: AnimationConfig,
|
|
14
|
+
options: {
|
|
15
|
+
onComplete?: () => void;
|
|
16
|
+
ensureVisibleOnComplete?: boolean;
|
|
17
|
+
} = {}
|
|
18
|
+
): gsap.core.Tween | null {
|
|
19
|
+
// 处理onComplete回调
|
|
20
|
+
const addOnComplete = (vars: gsap.TweenVars) => {
|
|
21
|
+
if (options.ensureVisibleOnComplete || options.onComplete) {
|
|
22
|
+
const originalOnComplete = vars.onComplete;
|
|
23
|
+
vars.onComplete = function () {
|
|
24
|
+
// 确保元素可见(如果需要)
|
|
25
|
+
if (options.ensureVisibleOnComplete && element instanceof HTMLElement) {
|
|
26
|
+
element.style.opacity = "1";
|
|
27
|
+
element.style.visibility = "visible";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 调用原始的onComplete回调(如果有)
|
|
31
|
+
if (originalOnComplete && typeof originalOnComplete === "function") {
|
|
32
|
+
originalOnComplete.call(this);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 调用额外的onComplete回调(如果有)
|
|
36
|
+
if (options.onComplete) {
|
|
37
|
+
options.onComplete();
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return vars;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// 处理from动画
|
|
45
|
+
if (config.from) {
|
|
46
|
+
const fromVars = { ...config.from } as gsap.TweenVars;
|
|
47
|
+
|
|
48
|
+
// 复制动画控制参数
|
|
49
|
+
if (config.duration) fromVars.duration = config.duration;
|
|
50
|
+
if (config.delay) fromVars.delay = config.delay;
|
|
51
|
+
if (config.ease) fromVars.ease = config.ease;
|
|
52
|
+
|
|
53
|
+
// 添加onComplete回调
|
|
54
|
+
addOnComplete(fromVars);
|
|
55
|
+
|
|
56
|
+
// 使用gsap.from
|
|
57
|
+
return gsap.from(element, fromVars);
|
|
58
|
+
}
|
|
59
|
+
// 处理fromTo动画
|
|
60
|
+
else if (config.fromTo) {
|
|
61
|
+
const fromVars = { ...config.fromTo.from };
|
|
62
|
+
const toVars = { ...config.fromTo.to } as gsap.TweenVars;
|
|
63
|
+
|
|
64
|
+
// 复制动画控制参数到toVars
|
|
65
|
+
if (config.duration) toVars.duration = config.duration;
|
|
66
|
+
if (config.delay) toVars.delay = config.delay;
|
|
67
|
+
if (config.ease) toVars.ease = config.ease;
|
|
68
|
+
|
|
69
|
+
// 添加onComplete回调
|
|
70
|
+
addOnComplete(toVars);
|
|
71
|
+
|
|
72
|
+
// 使用gsap.fromTo
|
|
73
|
+
return gsap.fromTo(element, fromVars, toVars);
|
|
74
|
+
}
|
|
75
|
+
// 处理to动画(默认)
|
|
76
|
+
else {
|
|
77
|
+
// 使用convertToGsapVars转换配置
|
|
78
|
+
const gsapVars = convertToGsapVars(config);
|
|
79
|
+
|
|
80
|
+
// 添加onComplete回调
|
|
81
|
+
addOnComplete(gsapVars);
|
|
82
|
+
|
|
83
|
+
// 使用gsap.to
|
|
84
|
+
return gsap.to(element, gsapVars);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { AnimationStyles } from "../motion";
|
|
2
|
+
import { gsap } from "gsap/dist/gsap";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 将动画配置应用到元素的初始状态
|
|
6
|
+
* @param element 目标元素
|
|
7
|
+
* @param initialConfig 初始配置
|
|
8
|
+
*/
|
|
9
|
+
export function applyInitialState(
|
|
10
|
+
element: HTMLElement,
|
|
11
|
+
initialConfig: AnimationStyles
|
|
12
|
+
): void {
|
|
13
|
+
if (!initialConfig) return;
|
|
14
|
+
|
|
15
|
+
// 创建GSAP变量对象
|
|
16
|
+
const initialVars = {} as gsap.TweenVars;
|
|
17
|
+
|
|
18
|
+
// 处理基本动画属性
|
|
19
|
+
if (initialConfig.opacity !== undefined)
|
|
20
|
+
initialVars.opacity = Number(initialConfig.opacity);
|
|
21
|
+
if (initialConfig.scale !== undefined)
|
|
22
|
+
initialVars.scale = Number(initialConfig.scale);
|
|
23
|
+
if (initialConfig.rotate !== undefined)
|
|
24
|
+
initialVars.rotation = Number(initialConfig.rotate);
|
|
25
|
+
|
|
26
|
+
// 处理位置相关属性
|
|
27
|
+
if (initialConfig.x !== undefined)
|
|
28
|
+
initialVars.x =
|
|
29
|
+
typeof initialConfig.x === "string"
|
|
30
|
+
? initialConfig.x
|
|
31
|
+
: Number(initialConfig.x);
|
|
32
|
+
if (initialConfig.y !== undefined)
|
|
33
|
+
initialVars.y =
|
|
34
|
+
typeof initialConfig.y === "string"
|
|
35
|
+
? initialConfig.y
|
|
36
|
+
: Number(initialConfig.y);
|
|
37
|
+
if (initialConfig.z !== undefined)
|
|
38
|
+
initialVars.z =
|
|
39
|
+
typeof initialConfig.z === "string"
|
|
40
|
+
? initialConfig.z
|
|
41
|
+
: Number(initialConfig.z);
|
|
42
|
+
|
|
43
|
+
// 处理尺寸属性
|
|
44
|
+
if (initialConfig.width !== undefined)
|
|
45
|
+
initialVars.width =
|
|
46
|
+
typeof initialConfig.width === "string"
|
|
47
|
+
? initialConfig.width
|
|
48
|
+
: Number(initialConfig.width);
|
|
49
|
+
if (initialConfig.height !== undefined)
|
|
50
|
+
initialVars.height =
|
|
51
|
+
typeof initialConfig.height === "string"
|
|
52
|
+
? initialConfig.height
|
|
53
|
+
: Number(initialConfig.height);
|
|
54
|
+
|
|
55
|
+
// 处理颜色和背景
|
|
56
|
+
if (initialConfig.color !== undefined)
|
|
57
|
+
initialVars.color = initialConfig.color;
|
|
58
|
+
if (initialConfig.backgroundColor !== undefined)
|
|
59
|
+
initialVars.backgroundColor = initialConfig.backgroundColor;
|
|
60
|
+
|
|
61
|
+
// 处理变换原点
|
|
62
|
+
if (initialConfig.transformOrigin !== undefined)
|
|
63
|
+
initialVars.transformOrigin = initialConfig.transformOrigin;
|
|
64
|
+
|
|
65
|
+
// 处理显示和可见性
|
|
66
|
+
if (initialConfig.display !== undefined)
|
|
67
|
+
initialVars.display = initialConfig.display as string;
|
|
68
|
+
if (initialConfig.visibility !== undefined)
|
|
69
|
+
initialVars.visibility = initialConfig.visibility as string;
|
|
70
|
+
|
|
71
|
+
// 处理自定义transform
|
|
72
|
+
if (initialConfig.transform !== undefined) {
|
|
73
|
+
// 直接设置style.transform,而不是通过GSAP
|
|
74
|
+
element.style.transform = initialConfig.transform;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 设置初始状态
|
|
78
|
+
gsap.set(element, initialVars);
|
|
79
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./consts";
|
|
2
|
+
export * from "./utils";
|
|
3
|
+
export * from "./ModalController";
|
|
4
|
+
export * from "./AnimateController";
|
|
5
|
+
export * from "./AosController";
|
|
6
|
+
export * from "./PopoverController";
|
|
7
|
+
export * from "./PageLoader";
|
|
8
|
+
export * from "./TabsController";
|
|
9
|
+
export * from "./NumberController";
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { DATA_MOTION_POPUP, PopupAnimationConfig } from "../motion";
|
|
2
|
+
import { applyAnimation } from "./applyAnimation";
|
|
3
|
+
import { gsap } from "gsap/dist/gsap";
|
|
4
|
+
import {
|
|
5
|
+
EVENT_OPEN,
|
|
6
|
+
EVENT_CLOSE,
|
|
7
|
+
DATA_POPUP,
|
|
8
|
+
DATA_POPUP_ROLE,
|
|
9
|
+
PopupRole,
|
|
10
|
+
EVENT_SELECT,
|
|
11
|
+
EVENT_UNSELECT,
|
|
12
|
+
} from "./consts";
|
|
13
|
+
import { applyInitialState } from "./applyInitialState";
|
|
14
|
+
|
|
15
|
+
export type PopupEvent = {
|
|
16
|
+
//控件组的唯一标识
|
|
17
|
+
key: string;
|
|
18
|
+
//发出事件的element,popover对应Container,modal对应Trigger
|
|
19
|
+
target?: HTMLElement;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type SelectionEvent = {
|
|
23
|
+
//控件组的唯一标识
|
|
24
|
+
key: string;
|
|
25
|
+
//发出事件的element
|
|
26
|
+
target: HTMLElement;
|
|
27
|
+
selection: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// 事件类型映射,用于类型安全的事件处理
|
|
31
|
+
type EventMap = {
|
|
32
|
+
[EVENT_OPEN]: PopupEvent;
|
|
33
|
+
[EVENT_CLOSE]: PopupEvent;
|
|
34
|
+
[EVENT_SELECT]: SelectionEvent;
|
|
35
|
+
[EVENT_UNSELECT]: SelectionEvent;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export class EventBus {
|
|
39
|
+
// 事件监听器存储,使用类型安全的映射
|
|
40
|
+
private eventListeners: {
|
|
41
|
+
[K in keyof EventMap]?: Array<(data: EventMap[K]) => void>;
|
|
42
|
+
} & {
|
|
43
|
+
[key: string]: Array<(data: unknown) => void>;
|
|
44
|
+
} = {};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 触发自定义事件
|
|
48
|
+
* @param event - 事件名称
|
|
49
|
+
* @param options - 事件数据
|
|
50
|
+
*/
|
|
51
|
+
emit = <K extends keyof EventMap>(event: K, options?: EventMap[K]): void => {
|
|
52
|
+
try {
|
|
53
|
+
// 获取该事件的所有监听器
|
|
54
|
+
const listeners = this.eventListeners[event] || [];
|
|
55
|
+
|
|
56
|
+
// 调用所有监听器
|
|
57
|
+
listeners.forEach((callback) => {
|
|
58
|
+
try {
|
|
59
|
+
// 使用类型断言,因为我们知道监听器期望的类型与发送的类型匹配
|
|
60
|
+
if (event in this.eventListeners) {
|
|
61
|
+
// 对于已知的事件类型,我们可以安全地断言类型
|
|
62
|
+
(callback as (data: typeof options) => void)(options);
|
|
63
|
+
} else {
|
|
64
|
+
// 对于未知的事件类型,我们使用更通用的类型
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
66
|
+
callback(options as any);
|
|
67
|
+
}
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error(`处理事件回调失败: ${event}`, error);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error(`触发事件失败: ${event}`, error);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 监听自定义事件
|
|
79
|
+
* @param event - 事件名称
|
|
80
|
+
* @param callback - 事件回调函数
|
|
81
|
+
* @returns 取消监听的函数
|
|
82
|
+
*/
|
|
83
|
+
|
|
84
|
+
public on = <T = PopupEvent | SelectionEvent>(
|
|
85
|
+
event: string,
|
|
86
|
+
callback: (options: T) => void
|
|
87
|
+
): (() => void) => {
|
|
88
|
+
try {
|
|
89
|
+
// 确保事件监听器数组已初始化
|
|
90
|
+
if (!this.eventListeners[event]) {
|
|
91
|
+
this.eventListeners[event] = [];
|
|
92
|
+
}
|
|
93
|
+
//console.log("===$on", event, this.eventListeners);
|
|
94
|
+
|
|
95
|
+
// 将回调函数转换为适当的类型并添加到监听器列表中
|
|
96
|
+
const typedCallback = callback as unknown as (data: unknown) => void;
|
|
97
|
+
this.eventListeners[event].push(typedCallback);
|
|
98
|
+
|
|
99
|
+
// 返回取消监听的函数
|
|
100
|
+
return () => {
|
|
101
|
+
try {
|
|
102
|
+
if (this.eventListeners[event]) {
|
|
103
|
+
// 找到并移除监听器
|
|
104
|
+
const index = this.eventListeners[event].indexOf(typedCallback);
|
|
105
|
+
if (index !== -1) {
|
|
106
|
+
this.eventListeners[event].splice(index, 1);
|
|
107
|
+
|
|
108
|
+
// 如果没有更多监听器,清理数组
|
|
109
|
+
if (this.eventListeners[event].length === 0) {
|
|
110
|
+
delete this.eventListeners[event];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error(`解除事件监听失败: ${event}`, error);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.error(`注册事件监听失败: ${event}`, error);
|
|
120
|
+
return () => {};
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
destroy() {
|
|
125
|
+
this.eventListeners = {};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export class PopupController {
|
|
130
|
+
constructor(protected doc: Document | undefined) {}
|
|
131
|
+
protected unmountHandlers: (() => void)[] = [];
|
|
132
|
+
protected eventBus = new EventBus();
|
|
133
|
+
|
|
134
|
+
mount() {
|
|
135
|
+
this.unmount();
|
|
136
|
+
this.initPopupAnimations();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
unmount() {
|
|
140
|
+
this.unmountHandlers.forEach((handler) => handler());
|
|
141
|
+
this.unmountHandlers = [];
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* 打开弹出层
|
|
145
|
+
* @param popupKey - 弹出层的唯一标识
|
|
146
|
+
* @param target - 触发打开的目标元素
|
|
147
|
+
*/
|
|
148
|
+
open = (popupKey: string, target: HTMLElement): void => {
|
|
149
|
+
if (!this.doc) return;
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
// 查找 Modal 容器
|
|
153
|
+
const modalContainer = this.getPopupContainer(popupKey);
|
|
154
|
+
if (modalContainer) {
|
|
155
|
+
modalContainer.classList.add("open");
|
|
156
|
+
this.eventBus.emit(EVENT_OPEN, { key: popupKey, target });
|
|
157
|
+
} else {
|
|
158
|
+
console.warn(`未找到弹出层: ${popupKey}`);
|
|
159
|
+
}
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.error(`打开弹出层失败: ${popupKey}`, error);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* 关闭弹出层
|
|
167
|
+
* @param popupKey - 弹出层的唯一标识
|
|
168
|
+
* @param target - 可选的目标元素,如果提供则使用该元素,否则查找容器
|
|
169
|
+
*/
|
|
170
|
+
close = (popupKey: string, target?: HTMLElement): void => {
|
|
171
|
+
if (!this.doc) return;
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
const container = target || this.getPopupContainer(popupKey);
|
|
175
|
+
if (!container) {
|
|
176
|
+
console.warn(`找不到弹出层容器: ${popupKey}`);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 移除open类
|
|
181
|
+
container.classList.remove("open");
|
|
182
|
+
|
|
183
|
+
// 触发关闭事件
|
|
184
|
+
this.eventBus.emit(EVENT_CLOSE, {
|
|
185
|
+
key: popupKey,
|
|
186
|
+
target: container,
|
|
187
|
+
});
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error(`关闭弹出层失败: ${popupKey}`, error);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* 获取弹出层容器元素
|
|
195
|
+
* @param popupKey - 弹出层的唯一标识
|
|
196
|
+
* @returns 弹出层容器元素,如果未找到则返回undefined
|
|
197
|
+
*/
|
|
198
|
+
getPopupContainer = (popupKey: string): HTMLElement | undefined => {
|
|
199
|
+
if (!this.doc) return undefined;
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
return this.doc.querySelector(
|
|
203
|
+
`[${DATA_POPUP}="${popupKey}"][${DATA_POPUP_ROLE}="${PopupRole.ModalContainer}"], [${DATA_POPUP}="${popupKey}"][${DATA_POPUP_ROLE}="${PopupRole.PopoverContainer}"]`
|
|
204
|
+
) as HTMLElement | undefined;
|
|
205
|
+
} catch (error) {
|
|
206
|
+
console.error(`获取弹出层容器失败: ${popupKey}`, error);
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* 初始化弹窗动画
|
|
213
|
+
* @returns 清理函数
|
|
214
|
+
*/
|
|
215
|
+
initPopupAnimations = () => {
|
|
216
|
+
const unsubscribe = this.onOpenAll((event) => {
|
|
217
|
+
const openResponseElements = this.doc?.querySelectorAll(
|
|
218
|
+
`[${DATA_MOTION_POPUP}]`
|
|
219
|
+
);
|
|
220
|
+
const containerElement = this.getPopupContainer(event.key);
|
|
221
|
+
//响应open事件
|
|
222
|
+
openResponseElements?.forEach((element) => {
|
|
223
|
+
if (
|
|
224
|
+
this.doc?.defaultView &&
|
|
225
|
+
element instanceof this.doc.defaultView?.HTMLElement &&
|
|
226
|
+
containerElement?.contains(element)
|
|
227
|
+
) {
|
|
228
|
+
if (element.dataset.motionPopup) {
|
|
229
|
+
const animation = JSON.parse(element.dataset.motionPopup) as
|
|
230
|
+
| PopupAnimationConfig
|
|
231
|
+
| undefined;
|
|
232
|
+
if (animation?.initial) {
|
|
233
|
+
applyInitialState(element, animation.initial);
|
|
234
|
+
}
|
|
235
|
+
if (animation?.open) {
|
|
236
|
+
applyAnimation(element, animation.open);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
return () => {
|
|
243
|
+
const closeResponseElements = this.doc?.querySelectorAll(
|
|
244
|
+
`[${DATA_MOTION_POPUP}]`
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
//响应close事件
|
|
248
|
+
closeResponseElements?.forEach((element) => {
|
|
249
|
+
if (
|
|
250
|
+
this.doc?.defaultView &&
|
|
251
|
+
element instanceof this.doc?.defaultView.HTMLElement
|
|
252
|
+
) {
|
|
253
|
+
if (
|
|
254
|
+
element.dataset.motionPopup &&
|
|
255
|
+
containerElement?.contains(element)
|
|
256
|
+
) {
|
|
257
|
+
const animation = JSON.parse(element.dataset.motionPopup) as
|
|
258
|
+
| PopupAnimationConfig
|
|
259
|
+
| undefined;
|
|
260
|
+
if (animation?.close) {
|
|
261
|
+
// 首先终止该元素上可能正在进行的所有动画(包括open动画)
|
|
262
|
+
gsap.killTweensOf(element);
|
|
263
|
+
|
|
264
|
+
// 然后应用close动画
|
|
265
|
+
applyAnimation(element, animation.close);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
} else {
|
|
269
|
+
console.log("===Not an HTMLElement somehow?");
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
};
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
this.unmountHandlers.push(() => {
|
|
276
|
+
unsubscribe();
|
|
277
|
+
});
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
public onOpen = (
|
|
281
|
+
popupKey: string,
|
|
282
|
+
callback: (event: PopupEvent) => VoidFunction | void
|
|
283
|
+
): (() => void) => {
|
|
284
|
+
return this.onOpenAll((event) => {
|
|
285
|
+
if (event.key === popupKey) {
|
|
286
|
+
return callback(event);
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
public onClose = (
|
|
292
|
+
popupKey: string,
|
|
293
|
+
callback: (event: PopupEvent) => void
|
|
294
|
+
) => {
|
|
295
|
+
return this.onCloseAll((event) => {
|
|
296
|
+
if (event.key === popupKey) {
|
|
297
|
+
callback(event);
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
public onCloseAll = (callback: (event: PopupEvent) => void) => {
|
|
303
|
+
return this.eventBus.on(EVENT_CLOSE, callback);
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* 用于在脚本中监听弹出层的打开/关闭事件,并处理相关动画效果
|
|
308
|
+
* 注意:此函数主要用于脚本中的事件处理,组件内部已有相应实现,无需使用
|
|
309
|
+
*
|
|
310
|
+
* @param callback - 弹出层打开时的回调函数,可返回关闭时的清理函数
|
|
311
|
+
* @returns 取消监听的函数
|
|
312
|
+
*/
|
|
313
|
+
public onOpenAll = (
|
|
314
|
+
callback: (event: PopupEvent) => VoidFunction | void
|
|
315
|
+
): (() => void) => {
|
|
316
|
+
let unsubscribe: ((event: PopupEvent) => void) | void;
|
|
317
|
+
|
|
318
|
+
const handleOpen = (event: PopupEvent) => {
|
|
319
|
+
try {
|
|
320
|
+
unsubscribe = callback?.(event);
|
|
321
|
+
} catch (error) {
|
|
322
|
+
console.error("处理弹出层打开事件失败", error);
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
const unsubscribeOpen = this.eventBus.on(EVENT_OPEN, handleOpen);
|
|
327
|
+
|
|
328
|
+
const handleClose = (event: PopupEvent) => {
|
|
329
|
+
try {
|
|
330
|
+
unsubscribe?.(event);
|
|
331
|
+
} catch (error) {
|
|
332
|
+
console.error("处理弹出层关闭事件失败", error);
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
const unsubscribeClose = this.eventBus.on(EVENT_CLOSE, handleClose);
|
|
336
|
+
|
|
337
|
+
return () => {
|
|
338
|
+
unsubscribeOpen();
|
|
339
|
+
unsubscribeClose();
|
|
340
|
+
};
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
destroy = () => {
|
|
344
|
+
this.eventBus.destroy();
|
|
345
|
+
};
|
|
346
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SimpleAnimation,
|
|
3
|
+
ANIMATIONS,
|
|
4
|
+
DualAnimation,
|
|
5
|
+
AOS_ANIMATIONS,
|
|
6
|
+
} from "../motion";
|
|
7
|
+
|
|
8
|
+
export function normalizeSimpleAnimation(animation?: SimpleAnimation) {
|
|
9
|
+
if (animation?.preset) {
|
|
10
|
+
const preset = ANIMATIONS[animation?.preset];
|
|
11
|
+
return {
|
|
12
|
+
...preset,
|
|
13
|
+
keyframes: { ...preset.keyframes, ...animation.keyframes },
|
|
14
|
+
transition: { ...preset.transition, ...animation.transition },
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return animation;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
//处理预置动画
|
|
22
|
+
export function normalizeDualAnimation(
|
|
23
|
+
animation?: DualAnimation
|
|
24
|
+
): DualAnimation | undefined {
|
|
25
|
+
if (!animation) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 如果有预设,合并预设和自定义配置
|
|
30
|
+
if (animation.preset) {
|
|
31
|
+
const preset = AOS_ANIMATIONS[animation.preset];
|
|
32
|
+
return {
|
|
33
|
+
...animation,
|
|
34
|
+
in: animation.in
|
|
35
|
+
? normalizeSimpleAnimation({ ...preset.in, ...animation.in })
|
|
36
|
+
: preset.in,
|
|
37
|
+
out: animation.out
|
|
38
|
+
? normalizeSimpleAnimation({ ...preset.out, ...animation.out })
|
|
39
|
+
: preset.out,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 如果没有预设,但有 in/out 配置
|
|
44
|
+
return {
|
|
45
|
+
in: animation.in ? normalizeSimpleAnimation(animation.in) : undefined,
|
|
46
|
+
out: animation.out ? normalizeSimpleAnimation(animation.out) : undefined,
|
|
47
|
+
};
|
|
48
|
+
}
|