@ray-js/components 1.7.56-beta.5 → 1.7.56-beta.7
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 +0 -7
- package/lib/Animated/index.js +153 -51
- package/lib/Animated/native/ty-animated/index.js +78 -35
- package/lib/Animated/native/ty-animated/index.tyml +7 -3
- package/lib/Animated/types.d.ts +46 -23
- 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/index.d.ts +2 -2
- package/lib/index.js +1 -1
- package/package.json +5 -5
package/lib/Animated/index.d.ts
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Animated - 纯净底层动画组件
|
|
3
|
-
*
|
|
4
|
-
* 只提供三个核心方法:
|
|
5
|
-
* - setProperty(property, value): 设置动画属性
|
|
6
|
-
* - setStyle(style): 设置样式
|
|
7
|
-
* - setText(text): 设置文本内容
|
|
8
|
-
*
|
|
9
|
-
* 可以配合任何动画库使用(GSAP、Anime.js、Framer Motion 等)
|
|
10
3
|
*/
|
|
11
4
|
import React from 'react';
|
|
12
5
|
import type { AnimatedViewRef, AnimatedTextRef, AnimatedProps } from './types';
|
package/lib/Animated/index.js
CHANGED
|
@@ -5,17 +5,11 @@ import "core-js/modules/esnext.iterator.constructor.js";
|
|
|
5
5
|
import "core-js/modules/esnext.iterator.for-each.js";
|
|
6
6
|
import "core-js/modules/esnext.iterator.map.js";
|
|
7
7
|
import "core-js/modules/web.dom-collections.iterator.js";
|
|
8
|
+
/* eslint-disable import/no-named-as-default */
|
|
8
9
|
/**
|
|
9
10
|
* Animated - 纯净底层动画组件
|
|
10
|
-
*
|
|
11
|
-
* 只提供三个核心方法:
|
|
12
|
-
* - setProperty(property, value): 设置动画属性
|
|
13
|
-
* - setStyle(style): 设置样式
|
|
14
|
-
* - setText(text): 设置文本内容
|
|
15
|
-
*
|
|
16
|
-
* 可以配合任何动画库使用(GSAP、Anime.js、Framer Motion 等)
|
|
17
11
|
*/
|
|
18
|
-
import React, { forwardRef, useImperativeHandle, useRef, useMemo, useCallback } from 'react';
|
|
12
|
+
import React, { forwardRef, useImperativeHandle, useRef, useMemo, useCallback, useEffect } from 'react';
|
|
19
13
|
import TyAnimatedComponent from './native/ty-animated';
|
|
20
14
|
|
|
21
15
|
// 小程序 API 声明
|
|
@@ -43,14 +37,40 @@ const AnimatedBase = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
43
37
|
mode = 'view',
|
|
44
38
|
onAnimationStart,
|
|
45
39
|
onAnimationEnd,
|
|
46
|
-
//
|
|
40
|
+
// 核心触摸事件
|
|
47
41
|
onClick,
|
|
48
42
|
onTouchStart,
|
|
49
|
-
onTouchEnd
|
|
43
|
+
onTouchEnd,
|
|
44
|
+
onTouchMove,
|
|
45
|
+
onTouchCancel,
|
|
46
|
+
// onLongPress,
|
|
47
|
+
lockOnClick
|
|
48
|
+
// lockOnLongPress,
|
|
50
49
|
} = props;
|
|
51
|
-
|
|
50
|
+
|
|
51
|
+
// 使用队列管理多个 play() 的回调
|
|
52
|
+
// 因为基础库会触发多次 finish 事件(旧动画被打断 + 新动画完成)
|
|
53
|
+
// 当前动画的回调
|
|
54
|
+
// 同一时间只有一个动画在执行,新动画会打断旧动画
|
|
55
|
+
const currentCallbackRef = useRef(null);
|
|
52
56
|
const componentIdRef = useRef(id || "animated-".concat(Math.random().toString(36).slice(2, 11)));
|
|
53
57
|
|
|
58
|
+
// 组件卸载时清理未完成的动画 Promise
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
return () => {
|
|
61
|
+
const callback = currentCallbackRef.current;
|
|
62
|
+
if (callback) {
|
|
63
|
+
// console.log(`[Animated][${Date.now()}] 组件卸载,resolve 未完成的动画 Promise`)
|
|
64
|
+
callback({
|
|
65
|
+
detail: {
|
|
66
|
+
unmounted: true
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
currentCallbackRef.current = null;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}, []);
|
|
73
|
+
|
|
54
74
|
// 提取文本内容(当 mode='text' 时)
|
|
55
75
|
const textContent = useMemo(() => {
|
|
56
76
|
if (mode !== 'text' || !children) return undefined;
|
|
@@ -59,23 +79,24 @@ const AnimatedBase = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
59
79
|
}
|
|
60
80
|
return undefined;
|
|
61
81
|
}, [mode, children]);
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 获取 DOM 实例
|
|
85
|
+
*/
|
|
86
|
+
const getDomInstance = () => {
|
|
87
|
+
try {
|
|
88
|
+
var _getCurrentPages;
|
|
89
|
+
const pages = (_getCurrentPages = getCurrentPages) === null || _getCurrentPages === void 0 ? void 0 : _getCurrentPages();
|
|
90
|
+
if (!pages || pages.length === 0) return null;
|
|
91
|
+
const currentPage = pages[pages.length - 1];
|
|
92
|
+
if (!(currentPage !== null && currentPage !== void 0 && currentPage.selectComponent)) return null;
|
|
93
|
+
return currentPage.selectComponent("#".concat(componentIdRef.current));
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error('[Animated] 获取组件实例失败:', error);
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
62
99
|
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
100
|
return {
|
|
80
101
|
/**
|
|
81
102
|
* 设置样式(直通通道)
|
|
@@ -84,13 +105,12 @@ const AnimatedBase = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
84
105
|
* ref.current.setStyle({ color: 'red', fontSize: '16px', border: '1px solid #ccc' })
|
|
85
106
|
*/
|
|
86
107
|
setStyle(styleValue, callback) {
|
|
87
|
-
return new Promise(resolve => {
|
|
108
|
+
return new Promise((resolve, reject) => {
|
|
88
109
|
var _inst$setCustomStyle;
|
|
89
110
|
const inst = getDomInstance();
|
|
90
111
|
if (!inst) {
|
|
91
112
|
console.warn('No DOM instance found');
|
|
92
|
-
|
|
93
|
-
if (callback) callback();
|
|
113
|
+
reject();
|
|
94
114
|
return;
|
|
95
115
|
}
|
|
96
116
|
|
|
@@ -109,6 +129,19 @@ const AnimatedBase = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
109
129
|
return;
|
|
110
130
|
}
|
|
111
131
|
|
|
132
|
+
// 如果有动画在执行,setStyle 会清空 transition,打断动画
|
|
133
|
+
// 主动 resolve 当前 Promise
|
|
134
|
+
const animCallback = currentCallbackRef.current;
|
|
135
|
+
if (animCallback) {
|
|
136
|
+
console.log("[Animated][".concat(Date.now(), "] setStyle \u6253\u65AD\u52A8\u753B\uFF0Cresolve Promise"));
|
|
137
|
+
animCallback({
|
|
138
|
+
detail: {
|
|
139
|
+
interruptedBySetStyle: true
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
currentCallbackRef.current = null;
|
|
143
|
+
}
|
|
144
|
+
|
|
112
145
|
// setData 完成后的回调
|
|
113
146
|
(_inst$setCustomStyle = inst.setCustomStyle) === null || _inst$setCustomStyle === void 0 || _inst$setCustomStyle.call(inst, styleString, () => {
|
|
114
147
|
// 调用用户回调
|
|
@@ -192,14 +225,23 @@ const AnimatedBase = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
192
225
|
animation.play = () => {
|
|
193
226
|
return new Promise(resolve => {
|
|
194
227
|
var _inst$__applyAnimData;
|
|
195
|
-
//
|
|
196
|
-
const
|
|
228
|
+
// 如果有旧的 Promise,主动 resolve 它(被新动画打断)
|
|
229
|
+
const oldCallback = currentCallbackRef.current;
|
|
230
|
+
if (oldCallback) {
|
|
231
|
+
console.log("[Animated][".concat(Date.now(), "] \u65B0\u52A8\u753B\u5F00\u59CB\uFF0C\u628A\u4E4B\u524D\u7684\u52A8\u753B\u7ED9\u4E2D\u65AD\u6389"));
|
|
232
|
+
oldCallback({
|
|
233
|
+
detail: {
|
|
234
|
+
interrupted: true
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
197
238
|
|
|
198
|
-
//
|
|
239
|
+
// 保存新的 callback
|
|
240
|
+
currentCallbackRef.current = resolve;
|
|
241
|
+
console.log("[Animated][".concat(Date.now(), "] play() \u8C03\u7528"));
|
|
199
242
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
};
|
|
243
|
+
// 导出动画数据
|
|
244
|
+
const animationData = animation.export();
|
|
203
245
|
(_inst$__applyAnimData = inst.__applyAnimData) === null || _inst$__applyAnimData === void 0 || _inst$__applyAnimData.call(inst, animationData);
|
|
204
246
|
});
|
|
205
247
|
};
|
|
@@ -221,7 +263,9 @@ const AnimatedBase = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
221
263
|
const query = ty.createSelectorQuery();
|
|
222
264
|
query.select("#".concat(componentIdRef.current)).boundingClientRect(rect => {
|
|
223
265
|
resolve(rect || null);
|
|
224
|
-
}).exec()
|
|
266
|
+
}).exec(() => {
|
|
267
|
+
// exec callback
|
|
268
|
+
});
|
|
225
269
|
} catch (error) {
|
|
226
270
|
console.error('[Animated] getBoundingClientRect 执行失败:', error);
|
|
227
271
|
resolve(null);
|
|
@@ -244,7 +288,9 @@ const AnimatedBase = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
244
288
|
const query = ty.createSelectorQuery();
|
|
245
289
|
query.select("#".concat(componentIdRef.current)).fields(fields, res => {
|
|
246
290
|
resolve(res || null);
|
|
247
|
-
}).exec()
|
|
291
|
+
}).exec(() => {
|
|
292
|
+
// exec callback
|
|
293
|
+
});
|
|
248
294
|
} catch (error) {
|
|
249
295
|
console.error('[Animated] getFields 执行失败:', error);
|
|
250
296
|
resolve(null);
|
|
@@ -281,7 +327,9 @@ const AnimatedBase = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
281
327
|
}
|
|
282
328
|
});
|
|
283
329
|
resolve(Object.keys(styles).length > 0 ? styles : null);
|
|
284
|
-
}).exec()
|
|
330
|
+
}).exec(() => {
|
|
331
|
+
// exec callback
|
|
332
|
+
});
|
|
285
333
|
} catch (error) {
|
|
286
334
|
console.error('[Animated] getComputedStyle 执行失败:', error);
|
|
287
335
|
resolve(null);
|
|
@@ -294,22 +342,73 @@ const AnimatedBase = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
294
342
|
// 处理 customStyle,避免传入 undefined
|
|
295
343
|
const customStyleValue = useMemo(() => {
|
|
296
344
|
if (!style) return undefined;
|
|
345
|
+
console.log('customStyle', style, typeof style === 'object' ? styleToString(style) : style);
|
|
297
346
|
return typeof style === 'object' ? styleToString(style) : style;
|
|
298
|
-
}, []);
|
|
347
|
+
}, [style]);
|
|
299
348
|
const handleAnimationEnd = useCallback(event => {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
349
|
+
const callback = currentCallbackRef.current;
|
|
350
|
+
if (callback) {
|
|
351
|
+
callback(event);
|
|
352
|
+
currentCallbackRef.current = null;
|
|
304
353
|
}
|
|
305
354
|
if (onAnimationEnd) {
|
|
306
355
|
onAnimationEnd();
|
|
307
356
|
}
|
|
308
|
-
}, [onAnimationEnd
|
|
357
|
+
}, [onAnimationEnd]);
|
|
358
|
+
|
|
359
|
+
// 包装 onClick,如果启用了 lockOnClick
|
|
360
|
+
const wrappedOnClick = useMemo(() => {
|
|
361
|
+
console.log('wrappedOnClick', !!onClick);
|
|
362
|
+
if (!onClick) return undefined;
|
|
363
|
+
if (lockOnClick) {
|
|
364
|
+
// 声明式锁:包装 onClick,完成后通过 setData 触发解锁
|
|
365
|
+
return async event => {
|
|
366
|
+
const startTime = Date.now();
|
|
367
|
+
console.log("[Animated.React][".concat(startTime, "] \uD83C\uDFAF onClick \u5F00\u59CB\u6267\u884C"));
|
|
368
|
+
try {
|
|
369
|
+
await onClick(event);
|
|
370
|
+
// console.log(
|
|
371
|
+
// `[Animated.React][${Date.now()}] onClick 执行完成,耗时: ${Date.now() - startTime}ms`
|
|
372
|
+
// )
|
|
373
|
+
} catch (error) {
|
|
374
|
+
console.error("[Animated.React][".concat(Date.now(), "] onClick \u6267\u884C\u5931\u8D25:"), error);
|
|
375
|
+
throw error;
|
|
376
|
+
} finally {
|
|
377
|
+
// 等待事件执行完成,自动解锁
|
|
378
|
+
await new Promise(resolve => {
|
|
379
|
+
const inst = getDomInstance();
|
|
380
|
+
inst.__unlockEvent('click', resolve);
|
|
381
|
+
});
|
|
382
|
+
// console.log('click 已解锁')
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
return onClick;
|
|
387
|
+
}, [onClick, lockOnClick]);
|
|
388
|
+
|
|
389
|
+
// 自动检测哪些事件需要加锁
|
|
390
|
+
const autoLockEvents = useMemo(() => {
|
|
391
|
+
const events = [];
|
|
392
|
+
|
|
393
|
+
// 通过 prop 声明
|
|
394
|
+
if (lockOnClick) {
|
|
395
|
+
events.push('click');
|
|
396
|
+
}
|
|
397
|
+
// if (lockOnLongPress) {
|
|
398
|
+
// events.push('longPress')
|
|
399
|
+
// }
|
|
400
|
+
|
|
401
|
+
if (events.length > 0) {
|
|
402
|
+
console.log("[Animated][".concat(Date.now(), "] \u81EA\u52A8\u52A0\u9501\u4E8B\u4EF6:"), events);
|
|
403
|
+
}
|
|
404
|
+
return events;
|
|
405
|
+
}, [onClick, lockOnClick]);
|
|
309
406
|
const eventBindings = {};
|
|
310
|
-
if (
|
|
407
|
+
if (wrappedOnClick) eventBindings['bind:click'] = wrappedOnClick;
|
|
311
408
|
if (onTouchStart) eventBindings['bind:touchstart'] = onTouchStart;
|
|
312
409
|
if (onTouchEnd) eventBindings['bind:touchend'] = onTouchEnd;
|
|
410
|
+
if (onTouchMove) eventBindings['bind:touchmove'] = onTouchMove;
|
|
411
|
+
if (onTouchCancel) eventBindings['bind:touchcancel'] = onTouchCancel;
|
|
313
412
|
if (onAnimationStart) eventBindings['bind:animationbegin'] = onAnimationStart;
|
|
314
413
|
eventBindings['bind:animationfinish'] = handleAnimationEnd;
|
|
315
414
|
return (
|
|
@@ -317,14 +416,17 @@ const AnimatedBase = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
317
416
|
// @ts-ignore - 小程序自定义组件
|
|
318
417
|
React.createElement(TyAnimatedComponent, _extends({
|
|
319
418
|
id: componentIdRef.current,
|
|
320
|
-
__mode: mode
|
|
419
|
+
__mode: mode,
|
|
420
|
+
__componentId: componentIdRef.current
|
|
321
421
|
}, className ? {
|
|
322
|
-
|
|
422
|
+
__className: className
|
|
323
423
|
} : {}, customStyleValue ? {
|
|
324
|
-
|
|
424
|
+
__style: customStyleValue
|
|
325
425
|
} : {}, mode === 'text' && textContent !== undefined ? {
|
|
326
426
|
__text: textContent
|
|
327
|
-
} : {},
|
|
427
|
+
} : {}, autoLockEvents ? {
|
|
428
|
+
__autoLockEvents: autoLockEvents
|
|
429
|
+
} : [], eventBindings), mode === 'view' ? children : null)
|
|
328
430
|
);
|
|
329
431
|
});
|
|
330
432
|
AnimatedBase.displayName = 'AnimatedBase';
|
|
@@ -356,4 +458,4 @@ const Animated = {
|
|
|
356
458
|
};
|
|
357
459
|
export default Animated;
|
|
358
460
|
|
|
359
|
-
//
|
|
461
|
+
// 导出类型和工具函数
|
|
@@ -2,6 +2,7 @@ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
|
2
2
|
import "core-js/modules/es.string.trim.js";
|
|
3
3
|
import "core-js/modules/esnext.iterator.constructor.js";
|
|
4
4
|
import "core-js/modules/esnext.iterator.filter.js";
|
|
5
|
+
import "core-js/modules/esnext.iterator.for-each.js";
|
|
5
6
|
import "core-js/modules/esnext.iterator.map.js";
|
|
6
7
|
import "core-js/modules/web.dom-collections.iterator.js";
|
|
7
8
|
// @ts-nocheck
|
|
@@ -28,20 +29,6 @@ Component({
|
|
|
28
29
|
type: String,
|
|
29
30
|
value: ''
|
|
30
31
|
},
|
|
31
|
-
// 兼容 FastView 接口
|
|
32
|
-
customStyle: {
|
|
33
|
-
type: String,
|
|
34
|
-
value: ''
|
|
35
|
-
},
|
|
36
|
-
customClass: {
|
|
37
|
-
type: String,
|
|
38
|
-
value: '',
|
|
39
|
-
observer: function (v) {
|
|
40
|
-
this.setData({
|
|
41
|
-
__className: v
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
32
|
onAnimationStart: {
|
|
46
33
|
type: null,
|
|
47
34
|
value: null
|
|
@@ -62,26 +49,40 @@ Component({
|
|
|
62
49
|
},
|
|
63
50
|
lifetimes: {
|
|
64
51
|
attached() {
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
});
|
|
71
|
-
}
|
|
52
|
+
// 初始化
|
|
53
|
+
// 为每个事件类型维护独立的锁
|
|
54
|
+
this._locks = {};
|
|
55
|
+
this._componentId = this.data.__componentId || this.id;
|
|
56
|
+
this._excludedTouchStartIdentifier = new Set();
|
|
72
57
|
},
|
|
73
58
|
detached() {
|
|
74
|
-
//
|
|
59
|
+
//
|
|
75
60
|
}
|
|
76
61
|
},
|
|
77
62
|
methods: {
|
|
63
|
+
/**
|
|
64
|
+
* 原生层事件锁:解锁(支持指定事件类型)
|
|
65
|
+
* @param eventType - 事件类型,如 'click', 'longPress'
|
|
66
|
+
* @param resolve - Promise resolve 函数
|
|
67
|
+
*/
|
|
68
|
+
__unlockEvent(eventType, resolve) {
|
|
69
|
+
if (!eventType) {
|
|
70
|
+
console.warn('[TyAnimated] __unlockEvent: eventType is required');
|
|
71
|
+
resolve();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 清除内部事件锁状态
|
|
76
|
+
this._locks[eventType] = false;
|
|
77
|
+
resolve();
|
|
78
|
+
},
|
|
78
79
|
/**
|
|
79
80
|
* 事件处理器
|
|
80
81
|
*/
|
|
81
82
|
__handleTiggerEvent(eventName, e) {
|
|
82
83
|
// 触发自定义事件,让外部可以监听
|
|
83
84
|
this.triggerEvent(eventName, e, {
|
|
84
|
-
|
|
85
|
+
bubbles: false,
|
|
85
86
|
composed: true
|
|
86
87
|
});
|
|
87
88
|
},
|
|
@@ -120,7 +121,7 @@ Component({
|
|
|
120
121
|
if (!styleString) return {};
|
|
121
122
|
const result = {};
|
|
122
123
|
const styles = styleString.split(';').filter(Boolean);
|
|
123
|
-
|
|
124
|
+
styles.forEach(style => {
|
|
124
125
|
const colonIndex = style.indexOf(':');
|
|
125
126
|
if (colonIndex > 0) {
|
|
126
127
|
const key = style.substring(0, colonIndex).trim();
|
|
@@ -129,7 +130,7 @@ Component({
|
|
|
129
130
|
result[key] = value;
|
|
130
131
|
}
|
|
131
132
|
}
|
|
132
|
-
}
|
|
133
|
+
});
|
|
133
134
|
return result;
|
|
134
135
|
},
|
|
135
136
|
/**
|
|
@@ -151,20 +152,20 @@ Component({
|
|
|
151
152
|
|
|
152
153
|
// 合并样式
|
|
153
154
|
const currentStyle = this._parseStyle(this.data.__style || '');
|
|
155
|
+
console.log('_style', this.data.__style);
|
|
156
|
+
console.log('currentStyle', currentStyle);
|
|
154
157
|
const newStyle = this._parseStyle(value);
|
|
155
158
|
const mergedStyle = _objectSpread(_objectSpread({}, currentStyle), newStyle);
|
|
156
159
|
let mergedStyleString = this._styleToString(mergedStyle);
|
|
157
160
|
if (mergedStyleString === this.data.__style && this.data.__animData !== null) {
|
|
158
|
-
console.log('[TyAnimated] 样式值相同但有动画数据,添加时间戳强制更新');
|
|
159
161
|
// 添加一个不影响渲染的注释,使样式字符串不同
|
|
160
162
|
mergedStyleString = "".concat(mergedStyleString, ";/* ").concat(Date.now(), " */");
|
|
161
163
|
}
|
|
162
|
-
|
|
163
|
-
// 一次性 setData,不嵌套
|
|
164
164
|
this.setData({
|
|
165
|
-
__style: mergedStyleString
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
__style: mergedStyleString
|
|
166
|
+
}, () => {
|
|
167
|
+
if (callback) callback();
|
|
168
|
+
});
|
|
168
169
|
},
|
|
169
170
|
/**
|
|
170
171
|
* 设置文本内容(__mode='text' 时使用)
|
|
@@ -213,15 +214,57 @@ Component({
|
|
|
213
214
|
__animData: animData
|
|
214
215
|
});
|
|
215
216
|
},
|
|
216
|
-
// 核心触摸事件处理(只保留最常用的)
|
|
217
|
-
__onClick(e) {
|
|
218
|
-
this.__handleTiggerEvent('click', e);
|
|
219
|
-
},
|
|
220
217
|
__onTouchStart(e) {
|
|
218
|
+
/**
|
|
219
|
+
* 因为end事件会合成tap事件的原因,可能会出现
|
|
220
|
+
* click -> lock -> 执行内部逻辑 ---- 过程中可能会有touchstart事件(identifier: 1) -> unlock -> click完成 -> touchend(identifier: 1) -> 合成了tap -> click(额外的异常点击)
|
|
221
|
+
* 逻辑层的锁 锁不住视图层的事件监听
|
|
222
|
+
*
|
|
223
|
+
* 方案:将在click事件锁住后的所有touchStart保存起来,在tap的时候对比set中是否有相同的序列id,有的话就直接return,并重置set
|
|
224
|
+
*/
|
|
225
|
+
|
|
226
|
+
// 只有在锁住的状态下存储 touchStart 的序列值
|
|
227
|
+
if (this._locks['click']) {
|
|
228
|
+
const touchId = e.touches[0].identifier; // 每个触摸序列的唯一ID
|
|
229
|
+
// console.log('------1-1--1-1-1-1-:::', touchId)
|
|
230
|
+
this._excludedTouchStartIdentifier.add(touchId);
|
|
231
|
+
}
|
|
221
232
|
this.__handleTiggerEvent('touchstart', e);
|
|
222
233
|
},
|
|
234
|
+
__onTouchMove(e) {
|
|
235
|
+
this.__handleTiggerEvent('touchmove', e);
|
|
236
|
+
},
|
|
223
237
|
__onTouchEnd(e) {
|
|
238
|
+
// console.log('=====2---22-2-2-::::', e.changedTouches[0].identifier)
|
|
239
|
+
|
|
224
240
|
this.__handleTiggerEvent('touchend', e);
|
|
241
|
+
},
|
|
242
|
+
__onTouchCancel(e) {
|
|
243
|
+
this.__handleTiggerEvent('touchcancel', e);
|
|
244
|
+
},
|
|
245
|
+
// 核心触摸事件处理(只保留最常用的)
|
|
246
|
+
__onClick(e) {
|
|
247
|
+
Date.now();
|
|
248
|
+
const eventType = 'click';
|
|
249
|
+
// console.log('---3----3-3--:::', e.changedTouches[0].identifier)
|
|
250
|
+
// 对比set中是否有相同的序列id,有的话就直接return,并重置set
|
|
251
|
+
e.changedTouches[0].identifier;
|
|
252
|
+
const isExceptiontrigger = this._excludedTouchStartIdentifier.has(e.changedTouches[0].identifier);
|
|
253
|
+
if (isExceptiontrigger) {
|
|
254
|
+
this._excludedTouchStartIdentifier.clear();
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// 检查该事件类型是否已加锁,锁住直接return
|
|
259
|
+
if (this._locks[eventType]) {
|
|
260
|
+
// console.log(`[TyAnimated][${now}] ⚠️ 事件被锁拦截: ${eventType}`)
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
// 如果发现click事件需要自动开解锁,在第一次click的时候就自动加上锁
|
|
264
|
+
if (this.data.__autoLockEvents.includes(eventType)) {
|
|
265
|
+
this._locks[eventType] = true;
|
|
266
|
+
}
|
|
267
|
+
this.__handleTiggerEvent(eventType, e);
|
|
225
268
|
}
|
|
226
269
|
}
|
|
227
270
|
});
|
|
@@ -5,21 +5,25 @@
|
|
|
5
5
|
class="{{__className}}"
|
|
6
6
|
style="{{__style}}"
|
|
7
7
|
animation="{{__animData}}"
|
|
8
|
-
bindtap="__onClick"
|
|
9
8
|
bindtouchstart="__onTouchStart"
|
|
10
9
|
bindtouchend="__onTouchEnd"
|
|
10
|
+
bindtouchmove="__onTouchMove"
|
|
11
|
+
bindtouchcancel="__onTouchCancel"
|
|
12
|
+
bindtap="__onClick"
|
|
11
13
|
>
|
|
12
14
|
<slot></slot>
|
|
13
15
|
</view>
|
|
14
16
|
<text
|
|
15
17
|
wx:else
|
|
16
18
|
id="rootText"
|
|
17
|
-
class="{{__className}}"
|
|
19
|
+
class="container {{__className}}"
|
|
18
20
|
style="{{__style}}"
|
|
19
21
|
animation="{{__animData}}"
|
|
20
|
-
bindtap="__onClick"
|
|
21
22
|
bindtouchstart="__onTouchStart"
|
|
22
23
|
bindtouchend="__onTouchEnd"
|
|
24
|
+
bindtouchmove="__onTouchMove"
|
|
25
|
+
bindtouchcancel="__onTouchCancel"
|
|
26
|
+
bindtap="__onClick"
|
|
23
27
|
>{{__text}}</text>
|
|
24
28
|
</block>
|
|
25
29
|
|
package/lib/Animated/types.d.ts
CHANGED
|
@@ -2,13 +2,14 @@ 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;
|
|
12
13
|
}
|
|
13
14
|
interface TargetType {
|
|
14
15
|
id: string;
|
|
@@ -168,29 +169,35 @@ export interface AnimatedViewRef {
|
|
|
168
169
|
* ```
|
|
169
170
|
*/
|
|
170
171
|
animate: (options?: TOptions) => IAnimation & {
|
|
171
|
-
play(): Promise<
|
|
172
|
+
play(): Promise<{
|
|
173
|
+
detail?: {
|
|
174
|
+
interruptedBySetStyle?: boolean;
|
|
175
|
+
interrupted?: boolean;
|
|
176
|
+
};
|
|
177
|
+
[key: string]: any;
|
|
178
|
+
}>;
|
|
172
179
|
};
|
|
173
180
|
/**
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
181
|
+
* 获取元素的布局位置和尺寸信息
|
|
182
|
+
* 基于小程序 SelectorQuery.boundingClientRect API
|
|
183
|
+
*
|
|
184
|
+
* @returns Promise<BoundingClientRect | null>
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```tsx
|
|
188
|
+
* const rect = await boxRef.current?.getBoundingClientRect()
|
|
189
|
+
* console.log(rect?.left, rect?.top, rect?.width, rect?.height)
|
|
190
|
+
*
|
|
191
|
+
* // 使用位置信息实现动画
|
|
192
|
+
* if (rect) {
|
|
193
|
+
* const centerX = window.innerWidth / 2 - rect.left - rect.width / 2
|
|
194
|
+
* const centerY = window.innerHeight / 2 - rect.top - rect.height / 2
|
|
195
|
+
* boxRef.current?.animate((anim) => {
|
|
196
|
+
* anim.translateX(centerX).translateY(centerY).scale(2).step({ duration: 300 })
|
|
197
|
+
* })
|
|
198
|
+
* }
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
194
201
|
getBoundingClientRect: () => Promise<BoundingClientRect | null>;
|
|
195
202
|
/**
|
|
196
203
|
* 获取元素的详细信息(包括样式、属性等)
|
|
@@ -308,5 +315,21 @@ export interface AnimatedProps extends TouchEventHandler {
|
|
|
308
315
|
onAnimationStart?: () => void;
|
|
309
316
|
/** 动画结束回调 */
|
|
310
317
|
onAnimationEnd?: () => void;
|
|
318
|
+
/**
|
|
319
|
+
* 点击事件自动加锁
|
|
320
|
+
* 防止快速点击导致的重复触发
|
|
321
|
+
*
|
|
322
|
+
* @example
|
|
323
|
+
* ```tsx
|
|
324
|
+
* <Animated.View
|
|
325
|
+
* ref={ref}
|
|
326
|
+
* onClick={async () => {
|
|
327
|
+
* await doSomething()
|
|
328
|
+
* }}
|
|
329
|
+
* lockOnClick // 自动加锁,回调执行完自动解锁
|
|
330
|
+
* />
|
|
331
|
+
* ```
|
|
332
|
+
*/
|
|
333
|
+
lockOnClick?: boolean;
|
|
311
334
|
}
|
|
312
335
|
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
|
+
}
|