@ray-js/components 1.7.56-beta.1 → 1.7.56-beta.10
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/lib/Animated/index.d.ts +14 -0
- package/lib/Animated/index.js +232 -73
- package/lib/Animated/native/ty-animated/index.js +133 -29
- package/lib/Animated/native/ty-animated/index.tyml +12 -6
- package/lib/Animated/types.d.ts +104 -45
- package/lib/Animated-old/animation.d.ts +47 -0
- package/lib/Animated-old/index.d.ts +21 -0
- package/lib/Animated-old/index.js +359 -0
- package/lib/Animated-old/native/ty-animated/index.d.ts +5 -0
- package/lib/Animated-old/native/ty-animated/index.js +208 -0
- package/lib/Animated-old/native/ty-animated/index.json +4 -0
- package/lib/Animated-old/native/ty-animated/index.tyml +26 -0
- package/lib/Animated-old/native/ty-animated/index.tyss +3 -0
- package/lib/Animated-old/types.d.ts +312 -0
- package/lib/Animated-old/types.js +1 -0
- package/lib/ScrollView/ScrollView.d.ts +1 -1
- package/lib/ScrollView/ScrollView.js +6 -9
- package/lib/ScrollView/ScrollView.thing.d.ts +1 -1
- package/lib/ScrollView/ScrollView.thing.js +2 -5
- package/package.json +5 -5
package/lib/Animated/types.d.ts
CHANGED
|
@@ -2,13 +2,15 @@ import type React from 'react';
|
|
|
2
2
|
/**
|
|
3
3
|
* 触摸事件处理器(只保留核心事件)
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* 如需其他事件(如 onLongPress, onTouchMove 等),请在 Animated 的子元素上直接绑定
|
|
5
|
+
* 注:只提供最常用的事件 (onClick)
|
|
7
6
|
*/
|
|
8
7
|
export interface TouchEventHandler {
|
|
9
8
|
onClick?: (e: TouchEvent) => any;
|
|
10
9
|
onTouchStart?: (e: TouchEvent) => any;
|
|
11
10
|
onTouchEnd?: (e: TouchEvent) => any;
|
|
11
|
+
onTouchMove?: (e: TouchEvent) => any;
|
|
12
|
+
onTouchCancel?: (e: TouchEvent) => any;
|
|
13
|
+
onLongPress?: (e: TouchEvent) => any;
|
|
12
14
|
}
|
|
13
15
|
interface TargetType {
|
|
14
16
|
id: string;
|
|
@@ -96,66 +98,107 @@ export interface AnimatedViewRef {
|
|
|
96
98
|
/**
|
|
97
99
|
* 直接设置样式(无动画)
|
|
98
100
|
*
|
|
101
|
+
* 优势:不受 React 重渲染影响,可以在动画过程中随时调用 setState
|
|
102
|
+
* 而不用担心样式被覆盖或闪烁问题
|
|
103
|
+
*
|
|
104
|
+
* 注意:
|
|
105
|
+
* - 会清除动画数据并设置样式
|
|
106
|
+
* - 如果动画正在执行,会中断动画
|
|
107
|
+
* - 返回 Promise,可以等待样式应用完成
|
|
108
|
+
*
|
|
99
109
|
* @param style - 样式对象或 CSS 字符串
|
|
110
|
+
* @param callback - 可选的回调函数,样式应用完成后调用
|
|
111
|
+
* @returns Promise<void> - 样式应用完成后 resolve
|
|
100
112
|
*
|
|
101
113
|
* @example
|
|
102
114
|
* ```tsx
|
|
115
|
+
* // 基本使用
|
|
103
116
|
* ref.current?.setStyle({ opacity: 0.5, color: 'red' })
|
|
104
|
-
*
|
|
117
|
+
*
|
|
118
|
+
* // 等待样式应用完成
|
|
119
|
+
* await ref.current?.setStyle({ opacity: 0 })
|
|
120
|
+
*
|
|
121
|
+
* // 如果需要获取 rect,单独调用
|
|
122
|
+
* await ref.current?.setStyle({ opacity: 0 })
|
|
123
|
+
* const rect = await ref.current?.getBoundingClientRect()
|
|
124
|
+
*
|
|
125
|
+
* // 使用回调
|
|
126
|
+
* ref.current?.setStyle({ color: 'red' }, () => {
|
|
127
|
+
* console.log('样式已应用')
|
|
128
|
+
* })
|
|
105
129
|
* ```
|
|
106
130
|
*/
|
|
107
|
-
setStyle: (style: React.CSSProperties | string) => void
|
|
131
|
+
setStyle: (style: React.CSSProperties | string, callback?: () => void) => Promise<void>;
|
|
108
132
|
/**
|
|
109
|
-
*
|
|
110
|
-
*
|
|
133
|
+
* 创建动画对象
|
|
134
|
+
* 使用小程序原生动画 API,这是性能最优的动画方式
|
|
111
135
|
*
|
|
112
|
-
* @param
|
|
113
|
-
* @param options -
|
|
136
|
+
* @param options - 动画配置(可选)
|
|
137
|
+
* @param options.duration - 动画持续时间(毫秒),默认 400
|
|
138
|
+
* @param options.timingFunction - 缓动函数,默认 'linear'
|
|
139
|
+
* @param options.delay - 动画延迟时间(毫秒),默认 0
|
|
140
|
+
* @param options.transformOrigin - 变换原点,默认 '50% 50% 0'
|
|
141
|
+
* @returns Animation 实例,包含所有动画方法和 play() 方法
|
|
114
142
|
*
|
|
115
143
|
* @example
|
|
116
144
|
* ```tsx
|
|
117
|
-
* //
|
|
118
|
-
* ref.current
|
|
119
|
-
*
|
|
120
|
-
*
|
|
145
|
+
* // 基本使用
|
|
146
|
+
* const animation = ref.current.animate()
|
|
147
|
+
* animation.translateX(100).step({ duration: 300 })
|
|
148
|
+
* await animation.play()
|
|
149
|
+
* console.log('动画完成!')
|
|
121
150
|
*
|
|
122
|
-
* //
|
|
123
|
-
* ref.current
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
* })
|
|
151
|
+
* // 链式调用
|
|
152
|
+
* const animation = ref.current
|
|
153
|
+
* .animate({ transformOrigin: '50% 50%' })
|
|
154
|
+
* .translateX(100)
|
|
155
|
+
* .scale(1.2)
|
|
156
|
+
* .step({ duration: 300 })
|
|
157
|
+
* await animation.play()
|
|
158
|
+
*
|
|
159
|
+
* // 多段动画
|
|
160
|
+
* const animation = ref.current.animate()
|
|
161
|
+
* animation.translateX(100).step({ duration: 300 })
|
|
162
|
+
* animation.translateY(100).step({ duration: 300 })
|
|
163
|
+
* animation.scale(1.5).step({ duration: 300 })
|
|
164
|
+
* await animation.play()
|
|
128
165
|
*
|
|
129
|
-
* //
|
|
130
|
-
* ref.current
|
|
131
|
-
*
|
|
132
|
-
*
|
|
166
|
+
* // 复用动画
|
|
167
|
+
* const slideIn = ref.current.animate().translateX(100).step({ duration: 300 })
|
|
168
|
+
* await slideIn.play() // 第一次
|
|
169
|
+
* await slideIn.play() // 第二次
|
|
133
170
|
* ```
|
|
134
171
|
*/
|
|
135
|
-
animate: (
|
|
136
|
-
|
|
137
|
-
|
|
172
|
+
animate: (options?: TOptions) => IAnimation & {
|
|
173
|
+
play(): Promise<{
|
|
174
|
+
detail?: {
|
|
175
|
+
interruptedBySetStyle?: boolean;
|
|
176
|
+
interrupted?: boolean;
|
|
177
|
+
};
|
|
178
|
+
[key: string]: any;
|
|
179
|
+
}>;
|
|
180
|
+
};
|
|
138
181
|
/**
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
182
|
+
* 获取元素的布局位置和尺寸信息
|
|
183
|
+
* 基于小程序 SelectorQuery.boundingClientRect API
|
|
184
|
+
*
|
|
185
|
+
* @returns Promise<BoundingClientRect | null>
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```tsx
|
|
189
|
+
* const rect = await boxRef.current?.getBoundingClientRect()
|
|
190
|
+
* console.log(rect?.left, rect?.top, rect?.width, rect?.height)
|
|
191
|
+
*
|
|
192
|
+
* // 使用位置信息实现动画
|
|
193
|
+
* if (rect) {
|
|
194
|
+
* const centerX = window.innerWidth / 2 - rect.left - rect.width / 2
|
|
195
|
+
* const centerY = window.innerHeight / 2 - rect.top - rect.height / 2
|
|
196
|
+
* boxRef.current?.animate((anim) => {
|
|
197
|
+
* anim.translateX(centerX).translateY(centerY).scale(2).step({ duration: 300 })
|
|
198
|
+
* })
|
|
199
|
+
* }
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
159
202
|
getBoundingClientRect: () => Promise<BoundingClientRect | null>;
|
|
160
203
|
/**
|
|
161
204
|
* 获取元素的详细信息(包括样式、属性等)
|
|
@@ -273,5 +316,21 @@ export interface AnimatedProps extends TouchEventHandler {
|
|
|
273
316
|
onAnimationStart?: () => void;
|
|
274
317
|
/** 动画结束回调 */
|
|
275
318
|
onAnimationEnd?: () => void;
|
|
319
|
+
/**
|
|
320
|
+
* 点击事件自动加锁
|
|
321
|
+
* 防止快速点击导致的重复触发
|
|
322
|
+
*
|
|
323
|
+
* @example
|
|
324
|
+
* ```tsx
|
|
325
|
+
* <Animated.View
|
|
326
|
+
* ref={ref}
|
|
327
|
+
* onClick={async () => {
|
|
328
|
+
* await doSomething()
|
|
329
|
+
* }}
|
|
330
|
+
* lockOnClick // 自动加锁,回调执行完自动解锁
|
|
331
|
+
* />
|
|
332
|
+
* ```
|
|
333
|
+
*/
|
|
334
|
+
lockOnClick?: boolean;
|
|
276
335
|
}
|
|
277
336
|
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
interface IAnimation {
|
|
2
|
+
// transform
|
|
3
|
+
translate(x?: number, y?: number): this
|
|
4
|
+
translateX(x?: number): this
|
|
5
|
+
translateY(y?: number): this
|
|
6
|
+
translateZ(z?: number): this
|
|
7
|
+
|
|
8
|
+
rotate(angle: number): this
|
|
9
|
+
rotateX(angle: number): this
|
|
10
|
+
rotateY(angle: number): this
|
|
11
|
+
rotateZ(angle: number): this
|
|
12
|
+
rotate3d(x: number, y: number, z: number, angle: number): this
|
|
13
|
+
|
|
14
|
+
scale(x?: number, y?: number): this
|
|
15
|
+
scaleX(x?: number): this
|
|
16
|
+
scaleY(y?: number): this
|
|
17
|
+
scaleZ(z?: number): this
|
|
18
|
+
scale3d(x?: number, y?: number, z?: number): this
|
|
19
|
+
|
|
20
|
+
skew(x?: number, y?: number): this
|
|
21
|
+
skewX(x?: number): this
|
|
22
|
+
skewY(y?: number): this
|
|
23
|
+
|
|
24
|
+
matrix(...args: number[]): this
|
|
25
|
+
matrix3d(...args: number[]): this
|
|
26
|
+
|
|
27
|
+
// style
|
|
28
|
+
opacity(opacity: number): this
|
|
29
|
+
width(value: number | string): this
|
|
30
|
+
height(value: number | string): this
|
|
31
|
+
top(value: number | string): this
|
|
32
|
+
left(value: number | string): this
|
|
33
|
+
right(value: number | string): this
|
|
34
|
+
bottom(value: number | string): this
|
|
35
|
+
backgroundColor(value: string): this
|
|
36
|
+
|
|
37
|
+
// step / export
|
|
38
|
+
step(
|
|
39
|
+
options?: Partial<{
|
|
40
|
+
transformOrigin: string
|
|
41
|
+
duration: number
|
|
42
|
+
timingFunction: string
|
|
43
|
+
delay: number
|
|
44
|
+
}>
|
|
45
|
+
): this
|
|
46
|
+
export(): { actions: any[] }
|
|
47
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Animated - 纯净底层动画组件
|
|
3
|
+
*
|
|
4
|
+
* 只提供三个核心方法:
|
|
5
|
+
* - setProperty(property, value): 设置动画属性
|
|
6
|
+
* - setStyle(style): 设置样式
|
|
7
|
+
* - setText(text): 设置文本内容
|
|
8
|
+
*
|
|
9
|
+
* 可以配合任何动画库使用(GSAP、Anime.js、Framer Motion 等)
|
|
10
|
+
*/
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import type { AnimatedViewRef, AnimatedTextRef, AnimatedProps } from './types';
|
|
13
|
+
/**
|
|
14
|
+
* 默认导出
|
|
15
|
+
*/
|
|
16
|
+
declare const Animated: {
|
|
17
|
+
View: React.ForwardRefExoticComponent<Omit<AnimatedProps, "mode"> & React.RefAttributes<AnimatedViewRef>>;
|
|
18
|
+
Text: React.ForwardRefExoticComponent<Omit<AnimatedProps, "mode"> & React.RefAttributes<AnimatedTextRef>>;
|
|
19
|
+
};
|
|
20
|
+
export default Animated;
|
|
21
|
+
export type { AnimatedViewRef, AnimatedTextRef, AnimatedProps } from './types';
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
|
+
import "core-js/modules/es.regexp.exec.js";
|
|
3
|
+
import "core-js/modules/es.string.replace.js";
|
|
4
|
+
import "core-js/modules/esnext.iterator.constructor.js";
|
|
5
|
+
import "core-js/modules/esnext.iterator.for-each.js";
|
|
6
|
+
import "core-js/modules/esnext.iterator.map.js";
|
|
7
|
+
import "core-js/modules/web.dom-collections.iterator.js";
|
|
8
|
+
/**
|
|
9
|
+
* Animated - 纯净底层动画组件
|
|
10
|
+
*
|
|
11
|
+
* 只提供三个核心方法:
|
|
12
|
+
* - setProperty(property, value): 设置动画属性
|
|
13
|
+
* - setStyle(style): 设置样式
|
|
14
|
+
* - setText(text): 设置文本内容
|
|
15
|
+
*
|
|
16
|
+
* 可以配合任何动画库使用(GSAP、Anime.js、Framer Motion 等)
|
|
17
|
+
*/
|
|
18
|
+
import React, { forwardRef, useImperativeHandle, useRef, useMemo, useCallback } from 'react';
|
|
19
|
+
import TyAnimatedComponent from './native/ty-animated';
|
|
20
|
+
|
|
21
|
+
// 小程序 API 声明
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 样式对象转 CSS 字符串
|
|
25
|
+
*/
|
|
26
|
+
function styleToString(style) {
|
|
27
|
+
return Object.entries(style).map(_ref => {
|
|
28
|
+
let [key, value] = _ref;
|
|
29
|
+
const cssKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
30
|
+
return "".concat(cssKey, ":").concat(value);
|
|
31
|
+
}).join(';');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 基础 Animated 组件
|
|
36
|
+
*/
|
|
37
|
+
const AnimatedBase = /*#__PURE__*/forwardRef((props, ref) => {
|
|
38
|
+
const {
|
|
39
|
+
children,
|
|
40
|
+
className,
|
|
41
|
+
style,
|
|
42
|
+
id,
|
|
43
|
+
mode = 'view',
|
|
44
|
+
onAnimationStart,
|
|
45
|
+
onAnimationEnd,
|
|
46
|
+
// 核心触摸事件(只保留最常用的三个)
|
|
47
|
+
onClick,
|
|
48
|
+
onTouchStart,
|
|
49
|
+
onTouchEnd
|
|
50
|
+
} = props;
|
|
51
|
+
const callbackRef = useRef(onAnimationEnd);
|
|
52
|
+
const componentIdRef = useRef(id || "animated-".concat(Math.random().toString(36).slice(2, 11)));
|
|
53
|
+
|
|
54
|
+
// 提取文本内容(当 mode='text' 时)
|
|
55
|
+
const textContent = useMemo(() => {
|
|
56
|
+
if (mode !== 'text' || !children) return undefined;
|
|
57
|
+
if (typeof children === 'string' || typeof children === 'number') {
|
|
58
|
+
return String(children);
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
}, [mode, children]);
|
|
62
|
+
useImperativeHandle(ref, () => {
|
|
63
|
+
/**
|
|
64
|
+
* 获取 DOM 实例
|
|
65
|
+
*/
|
|
66
|
+
const getDomInstance = () => {
|
|
67
|
+
try {
|
|
68
|
+
var _getCurrentPages;
|
|
69
|
+
const pages = (_getCurrentPages = getCurrentPages) === null || _getCurrentPages === void 0 ? void 0 : _getCurrentPages();
|
|
70
|
+
if (!pages || pages.length === 0) return null;
|
|
71
|
+
const currentPage = pages[pages.length - 1];
|
|
72
|
+
if (!(currentPage !== null && currentPage !== void 0 && currentPage.selectComponent)) return null;
|
|
73
|
+
return currentPage.selectComponent("#".concat(componentIdRef.current));
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error('[Animated] 获取组件实例失败:', error);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
return {
|
|
80
|
+
/**
|
|
81
|
+
* 设置样式(直通通道)
|
|
82
|
+
* 用途:设置静态样式,绕过 React/Ray 层,提升性能
|
|
83
|
+
* @example
|
|
84
|
+
* ref.current.setStyle({ color: 'red', fontSize: '16px', border: '1px solid #ccc' })
|
|
85
|
+
*/
|
|
86
|
+
setStyle(styleValue, callback) {
|
|
87
|
+
return new Promise(resolve => {
|
|
88
|
+
var _inst$setCustomStyle;
|
|
89
|
+
const inst = getDomInstance();
|
|
90
|
+
if (!inst) {
|
|
91
|
+
console.warn('No DOM instance found');
|
|
92
|
+
resolve();
|
|
93
|
+
if (callback) callback();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 防止传入 undefined 或空值
|
|
98
|
+
if (styleValue === undefined || styleValue === null) {
|
|
99
|
+
console.warn('styleValue is undefined or null');
|
|
100
|
+
resolve();
|
|
101
|
+
if (callback) callback();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const styleString = typeof styleValue === 'string' ? styleValue : styleToString(styleValue);
|
|
105
|
+
if (!styleString) {
|
|
106
|
+
console.warn('styleString is empty');
|
|
107
|
+
resolve();
|
|
108
|
+
if (callback) callback();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// setData 完成后的回调
|
|
113
|
+
(_inst$setCustomStyle = inst.setCustomStyle) === null || _inst$setCustomStyle === void 0 || _inst$setCustomStyle.call(inst, styleString, () => {
|
|
114
|
+
// 调用用户回调
|
|
115
|
+
if (callback && typeof callback === 'function') {
|
|
116
|
+
try {
|
|
117
|
+
callback();
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.error('[Animated] setStyle callback error:', error);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// resolve Promise
|
|
123
|
+
resolve();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
/**
|
|
128
|
+
* 设置文本内容(仅 mode='text' 时可用)
|
|
129
|
+
* 不触发 React 重渲染
|
|
130
|
+
*/
|
|
131
|
+
setText(text) {
|
|
132
|
+
var _inst$__applyText;
|
|
133
|
+
// 运行时检查:只有 Text 组件才能调用 setText
|
|
134
|
+
if (mode !== 'text') {
|
|
135
|
+
console.error('[Animated] setText 只能在 Animated.Text 组件上使用');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const inst = getDomInstance();
|
|
139
|
+
if (!inst) {
|
|
140
|
+
console.warn('[Animated] setText: 无法获取组件实例');
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
(_inst$__applyText = inst.__applyText) === null || _inst$__applyText === void 0 || _inst$__applyText.call(inst, text);
|
|
144
|
+
},
|
|
145
|
+
/**
|
|
146
|
+
* 创建动画对象
|
|
147
|
+
* 使用小程序原生动画 API,这是性能最优的动画方式
|
|
148
|
+
*
|
|
149
|
+
* @param options - 动画配置(duration, timingFunction, delay, transformOrigin)
|
|
150
|
+
* @returns Animation 实例,包含 play() 方法
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* // 创建并执行动画
|
|
154
|
+
* const animation = ref.current.animate()
|
|
155
|
+
* animation.translateX(100).scale(1.2).step({ duration: 300 })
|
|
156
|
+
* animation.translateY(50).step({ duration: 200 })
|
|
157
|
+
* await animation.play()
|
|
158
|
+
* console.log('动画完成!')
|
|
159
|
+
*
|
|
160
|
+
* // 链式调用
|
|
161
|
+
* const animation = ref.current
|
|
162
|
+
* .animate({ transformOrigin: '50% 50%' })
|
|
163
|
+
* .translateX(100)
|
|
164
|
+
* .scale(1.2)
|
|
165
|
+
* .step({ duration: 300 })
|
|
166
|
+
* await animation.play()
|
|
167
|
+
*
|
|
168
|
+
* // 复用动画
|
|
169
|
+
* const slideIn = ref.current.animate().translateX(100).step({ duration: 300 })
|
|
170
|
+
* await slideIn.play() // 第一次
|
|
171
|
+
* await slideIn.play() // 第二次
|
|
172
|
+
*/
|
|
173
|
+
animate(options) {
|
|
174
|
+
var _inst$_getCreateAnima;
|
|
175
|
+
const inst = getDomInstance();
|
|
176
|
+
if (!inst) {
|
|
177
|
+
console.warn('[Animated] No DOM instance found');
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// 获取 createAnimation 函数
|
|
182
|
+
const createAnimation = (_inst$_getCreateAnima = inst._getCreateAnimation) === null || _inst$_getCreateAnima === void 0 ? void 0 : _inst$_getCreateAnima.call(inst);
|
|
183
|
+
if (!createAnimation) {
|
|
184
|
+
console.error('[Animated] createAnimation 不可用');
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 创建小程序原生 Animation 实例
|
|
189
|
+
const animation = createAnimation(options);
|
|
190
|
+
|
|
191
|
+
// 扩展 play 方法
|
|
192
|
+
animation.play = () => {
|
|
193
|
+
return new Promise(resolve => {
|
|
194
|
+
var _inst$__applyAnimData;
|
|
195
|
+
// 导出动画数据
|
|
196
|
+
const animationData = animation.export();
|
|
197
|
+
|
|
198
|
+
// 包装完成回调
|
|
199
|
+
|
|
200
|
+
callbackRef.current = event => {
|
|
201
|
+
resolve(event);
|
|
202
|
+
};
|
|
203
|
+
(_inst$__applyAnimData = inst.__applyAnimData) === null || _inst$__applyAnimData === void 0 || _inst$__applyAnimData.call(inst, animationData);
|
|
204
|
+
});
|
|
205
|
+
};
|
|
206
|
+
return animation;
|
|
207
|
+
},
|
|
208
|
+
/**
|
|
209
|
+
* 获取元素的布局位置和尺寸信息
|
|
210
|
+
* 基于小程序 SelectorQuery.boundingClientRect API
|
|
211
|
+
*/
|
|
212
|
+
getBoundingClientRect() {
|
|
213
|
+
return new Promise(resolve => {
|
|
214
|
+
try {
|
|
215
|
+
// 检查小程序 API 是否存在
|
|
216
|
+
if (typeof ty === 'undefined' || !ty.createSelectorQuery) {
|
|
217
|
+
console.error('[Animated] getBoundingClientRect: ty.createSelectorQuery 不存在');
|
|
218
|
+
resolve(null);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const query = ty.createSelectorQuery();
|
|
222
|
+
query.select("#".concat(componentIdRef.current)).boundingClientRect(rect => {
|
|
223
|
+
resolve(rect || null);
|
|
224
|
+
}).exec();
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.error('[Animated] getBoundingClientRect 执行失败:', error);
|
|
227
|
+
resolve(null);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
},
|
|
231
|
+
/**
|
|
232
|
+
* 获取元素的详细信息(包括样式、属性等)
|
|
233
|
+
* 基于小程序 SelectorQuery.fields API
|
|
234
|
+
*/
|
|
235
|
+
getFields(fields) {
|
|
236
|
+
return new Promise(resolve => {
|
|
237
|
+
try {
|
|
238
|
+
// 检查小程序 API 是否存在
|
|
239
|
+
if (typeof ty === 'undefined' || !ty.createSelectorQuery) {
|
|
240
|
+
console.error('[Animated] getFields: ty.createSelectorQuery 不存在');
|
|
241
|
+
resolve(null);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const query = ty.createSelectorQuery();
|
|
245
|
+
query.select("#".concat(componentIdRef.current)).fields(fields, res => {
|
|
246
|
+
resolve(res || null);
|
|
247
|
+
}).exec();
|
|
248
|
+
} catch (error) {
|
|
249
|
+
console.error('[Animated] getFields 执行失败:', error);
|
|
250
|
+
resolve(null);
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
},
|
|
254
|
+
/**
|
|
255
|
+
* 获取元素的计算后样式
|
|
256
|
+
* 便捷方法,专门用于查询样式
|
|
257
|
+
*/
|
|
258
|
+
getComputedStyle(styleNames) {
|
|
259
|
+
return new Promise(resolve => {
|
|
260
|
+
try {
|
|
261
|
+
// 检查小程序 API 是否存在
|
|
262
|
+
if (typeof ty === 'undefined' || !ty.createSelectorQuery) {
|
|
263
|
+
console.error('[Animated] getComputedStyle: ty.createSelectorQuery 不存在');
|
|
264
|
+
resolve(null);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const query = ty.createSelectorQuery();
|
|
268
|
+
query.select("#".concat(componentIdRef.current)).fields({
|
|
269
|
+
computedStyle: styleNames
|
|
270
|
+
}, res => {
|
|
271
|
+
if (!res) {
|
|
272
|
+
resolve(null);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// 提取样式信息,过滤掉非样式字段
|
|
277
|
+
const styles = {};
|
|
278
|
+
styleNames.forEach(styleName => {
|
|
279
|
+
if (res[styleName] !== undefined) {
|
|
280
|
+
styles[styleName] = res[styleName];
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
resolve(Object.keys(styles).length > 0 ? styles : null);
|
|
284
|
+
}).exec();
|
|
285
|
+
} catch (error) {
|
|
286
|
+
console.error('[Animated] getComputedStyle 执行失败:', error);
|
|
287
|
+
resolve(null);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
}, []);
|
|
293
|
+
|
|
294
|
+
// 处理 customStyle,避免传入 undefined
|
|
295
|
+
const customStyleValue = useMemo(() => {
|
|
296
|
+
if (!style) return undefined;
|
|
297
|
+
return typeof style === 'object' ? styleToString(style) : style;
|
|
298
|
+
}, []);
|
|
299
|
+
const handleAnimationEnd = useCallback(event => {
|
|
300
|
+
if (callbackRef.current && typeof callbackRef.current === 'function') {
|
|
301
|
+
const temp = callbackRef.current;
|
|
302
|
+
callbackRef.current = null;
|
|
303
|
+
temp(event);
|
|
304
|
+
}
|
|
305
|
+
if (onAnimationEnd) {
|
|
306
|
+
onAnimationEnd();
|
|
307
|
+
}
|
|
308
|
+
}, [onAnimationEnd, callbackRef.current]);
|
|
309
|
+
const eventBindings = {};
|
|
310
|
+
if (onClick) eventBindings['bind:click'] = onClick;
|
|
311
|
+
if (onTouchStart) eventBindings['bind:touchstart'] = onTouchStart;
|
|
312
|
+
if (onTouchEnd) eventBindings['bind:touchend'] = onTouchEnd;
|
|
313
|
+
if (onAnimationStart) eventBindings['bind:animationbegin'] = onAnimationStart;
|
|
314
|
+
eventBindings['bind:animationfinish'] = handleAnimationEnd;
|
|
315
|
+
return (
|
|
316
|
+
/*#__PURE__*/
|
|
317
|
+
// @ts-ignore - 小程序自定义组件
|
|
318
|
+
React.createElement(TyAnimatedComponent, _extends({
|
|
319
|
+
id: componentIdRef.current,
|
|
320
|
+
__mode: mode
|
|
321
|
+
}, className ? {
|
|
322
|
+
__className: className
|
|
323
|
+
} : {}, customStyleValue ? {
|
|
324
|
+
__style: customStyleValue
|
|
325
|
+
} : {}, mode === 'text' && textContent !== undefined ? {
|
|
326
|
+
__text: textContent
|
|
327
|
+
} : {}, eventBindings), mode === 'view' ? children : null)
|
|
328
|
+
);
|
|
329
|
+
});
|
|
330
|
+
AnimatedBase.displayName = 'AnimatedBase';
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Animated.View 组件(不包含 setText)
|
|
334
|
+
*/
|
|
335
|
+
const AnimatedView = /*#__PURE__*/forwardRef((props, ref) => /*#__PURE__*/React.createElement(AnimatedBase, _extends({}, props, {
|
|
336
|
+
mode: "view",
|
|
337
|
+
ref: ref
|
|
338
|
+
})));
|
|
339
|
+
AnimatedView.displayName = 'Animated.View';
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Animated.Text 组件(包含 setText)
|
|
343
|
+
*/
|
|
344
|
+
const AnimatedText = /*#__PURE__*/forwardRef((props, ref) => /*#__PURE__*/React.createElement(AnimatedBase, _extends({}, props, {
|
|
345
|
+
mode: "text",
|
|
346
|
+
ref: ref
|
|
347
|
+
})));
|
|
348
|
+
AnimatedText.displayName = 'Animated.Text';
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* 默认导出
|
|
352
|
+
*/
|
|
353
|
+
const Animated = {
|
|
354
|
+
View: AnimatedView,
|
|
355
|
+
Text: AnimatedText
|
|
356
|
+
};
|
|
357
|
+
export default Animated;
|
|
358
|
+
|
|
359
|
+
// 导出类型
|