@ray-js/components 1.7.56-beta.2 → 1.7.56-beta.4

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/LICENSE.md ADDED
@@ -0,0 +1,9 @@
1
+ Copyright © 2014-2022 Tuya.inc
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,6 +1,4 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
- import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
3
- const _excluded = ["finish"];
4
2
  import "core-js/modules/es.regexp.exec.js";
5
3
  import "core-js/modules/es.string.replace.js";
6
4
  import "core-js/modules/esnext.iterator.constructor.js";
@@ -85,16 +83,46 @@ const AnimatedBase = /*#__PURE__*/forwardRef((props, ref) => {
85
83
  * @example
86
84
  * ref.current.setStyle({ color: 'red', fontSize: '16px', border: '1px solid #ccc' })
87
85
  */
88
- setStyle(styleValue) {
89
- var _inst$setCustomStyle;
90
- const inst = getDomInstance();
91
- if (!inst) return;
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
+ }
92
96
 
93
- // 防止传入 undefined 或空值
94
- if (styleValue === undefined || styleValue === null) return;
95
- const styleString = typeof styleValue === 'string' ? styleValue : styleToString(styleValue);
96
- if (!styleString) return;
97
- (_inst$setCustomStyle = inst.setCustomStyle) === null || _inst$setCustomStyle === void 0 || _inst$setCustomStyle.call(inst, styleString);
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
+ });
98
126
  },
99
127
  /**
100
128
  * 设置文本内容(仅 mode='text' 时可用)
@@ -115,34 +143,67 @@ const AnimatedBase = /*#__PURE__*/forwardRef((props, ref) => {
115
143
  (_inst$__applyText = inst.__applyText) === null || _inst$__applyText === void 0 || _inst$__applyText.call(inst, text);
116
144
  },
117
145
  /**
118
- * 使用小程序原生动画 API
119
- * 这是性能最优的动画方式
146
+ * 创建动画对象
147
+ * 使用小程序原生动画 API,这是性能最优的动画方式
148
+ *
149
+ * @param options - 动画配置(duration, timingFunction, delay, transformOrigin)
150
+ * @returns Animation 实例,包含 play() 方法
120
151
  *
121
152
  * @example
122
- * ref.current?.animate((anim) => {
123
- * anim.translateX(100).scale(1.2).step({ duration: 300 })
124
- * }, { transformOrigin: 'center center' })
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() // 第二次
125
172
  */
126
- animate(callback, options) {
127
- var _inst$__apply;
173
+ animate(options) {
174
+ var _inst$_getCreateAnima;
128
175
  const inst = getDomInstance();
129
176
  if (!inst) {
130
- console.warn('[Animated] animate: 无法获取组件实例');
131
- return;
177
+ console.warn('[Animated] No DOM instance found');
178
+ return null;
132
179
  }
133
- const _ref2 = options || {},
134
- {
135
- finish
136
- } = _ref2,
137
- option = _objectWithoutProperties(_ref2, _excluded);
138
- if (finish && typeof finish === 'function') {
139
- callbackRef.current = finish;
140
- } else {
141
- // 如果没有传 finish 参数,清空之前的回调
142
- // 避免 duration: 0 的重置动画再次触发之前的 finish
143
- callbackRef.current = null;
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;
144
186
  }
145
- (_inst$__apply = inst.__apply) === null || _inst$__apply === void 0 || _inst$__apply.call(inst, callback, option);
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;
146
207
  },
147
208
  /**
148
209
  * 获取元素的布局位置和尺寸信息
@@ -141,37 +141,30 @@ Component({
141
141
  return "".concat(key, ":").concat(value);
142
142
  }).join(';');
143
143
  },
144
- setCustomStyle(value) {
144
+ setCustomStyle(value, callback) {
145
145
  // 防止传入 undefined 或 null 导致小程序报错
146
146
  if (value === undefined || value === null) {
147
147
  console.warn('[TyAnimatedComponent] setCustomStyle: value is undefined or null, ignored');
148
+ if (callback) callback();
148
149
  return;
149
150
  }
150
151
 
151
- // 1. 合并样式
152
+ // 合并样式
152
153
  const currentStyle = this._parseStyle(this.data.__style || '');
153
154
  const newStyle = this._parseStyle(value);
154
155
  const mergedStyle = _objectSpread(_objectSpread({}, currentStyle), newStyle);
155
- const mergedStyleString = this._styleToString(mergedStyle);
156
+ let mergedStyleString = this._styleToString(mergedStyle);
156
157
  if (mergedStyleString === this.data.__style && this.data.__animData !== null) {
157
- console.log('[TyAnimated] 值相同且有动画数据,强制刷新');
158
-
159
- // 先清空样式和动画数据
160
- this.setData({
161
- __style: '',
162
- __animData: null
163
- }, () => {
164
- // 再设置目标样式
165
- this.setData({
166
- __style: mergedStyleString
167
- });
168
- });
169
- return;
158
+ console.log('[TyAnimated] 样式值相同但有动画数据,添加时间戳强制更新');
159
+ // 添加一个不影响渲染的注释,使样式字符串不同
160
+ mergedStyleString = "".concat(mergedStyleString, ";/* ").concat(Date.now(), " */");
170
161
  }
162
+
163
+ // 一次性 setData,不嵌套
171
164
  this.setData({
172
165
  __style: mergedStyleString,
173
- __animData: null
174
- });
166
+ __animData: null // 总是清空动画数据
167
+ }, callback);
175
168
  },
176
169
  /**
177
170
  * 设置文本内容(__mode='text' 时使用)
@@ -183,6 +176,15 @@ Component({
183
176
  });
184
177
  }
185
178
  },
179
+ __applyAnimData(animData) {
180
+ if (!animData) {
181
+ console.warn('[TyAnimatedComponent] __applyAnimData: animData is null or undefined');
182
+ return;
183
+ }
184
+ this.setData({
185
+ __animData: animData
186
+ });
187
+ },
186
188
  /**
187
189
  * 核心方法:直接应用 Animation 对象
188
190
  * @param {Function} fn - 接收 Animation 实例的回调函数
@@ -96,45 +96,80 @@ export interface AnimatedViewRef {
96
96
  /**
97
97
  * 直接设置样式(无动画)
98
98
  *
99
+ * 优势:不受 React 重渲染影响,可以在动画过程中随时调用 setState
100
+ * 而不用担心样式被覆盖或闪烁问题
101
+ *
102
+ * 注意:
103
+ * - 会清除动画数据并设置样式
104
+ * - 如果动画正在执行,会中断动画
105
+ * - 返回 Promise,可以等待样式应用完成
106
+ *
99
107
  * @param style - 样式对象或 CSS 字符串
108
+ * @param callback - 可选的回调函数,样式应用完成后调用
109
+ * @returns Promise<void> - 样式应用完成后 resolve
100
110
  *
101
111
  * @example
102
112
  * ```tsx
113
+ * // 基本使用
103
114
  * ref.current?.setStyle({ opacity: 0.5, color: 'red' })
104
- * ref.current?.setStyle('transform: translateX(100px);')
115
+ *
116
+ * // 等待样式应用完成
117
+ * await ref.current?.setStyle({ opacity: 0 })
118
+ *
119
+ * // 如果需要获取 rect,单独调用
120
+ * await ref.current?.setStyle({ opacity: 0 })
121
+ * const rect = await ref.current?.getBoundingClientRect()
122
+ *
123
+ * // 使用回调
124
+ * ref.current?.setStyle({ color: 'red' }, () => {
125
+ * console.log('样式已应用')
126
+ * })
105
127
  * ```
106
128
  */
107
- setStyle: (style: React.CSSProperties | string) => void;
129
+ setStyle: (style: React.CSSProperties | string, callback?: () => void) => Promise<void>;
108
130
  /**
109
- * 使用小程序原生动画 API
110
- * 这是性能最优的动画方式
131
+ * 创建动画对象
132
+ * 使用小程序原生动画 API,这是性能最优的动画方式
111
133
  *
112
- * @param callback - 动画配置回调,接收小程序动画对象
113
- * @param options - 动画选项
134
+ * @param options - 动画配置(可选)
135
+ * @param options.duration - 动画持续时间(毫秒),默认 400
136
+ * @param options.timingFunction - 缓动函数,默认 'linear'
137
+ * @param options.delay - 动画延迟时间(毫秒),默认 0
138
+ * @param options.transformOrigin - 变换原点,默认 '50% 50% 0'
139
+ * @returns Animation 实例,包含所有动画方法和 play() 方法
114
140
  *
115
141
  * @example
116
142
  * ```tsx
117
- * // 单个动画
118
- * ref.current?.animate((anim) => {
119
- * anim.translateX(100).scale(1.2).step({ duration: 300, timingFunction: 'ease-out' })
120
- * })
143
+ * // 基本使用
144
+ * const animation = ref.current.animate()
145
+ * animation.translateX(100).step({ duration: 300 })
146
+ * await animation.play()
147
+ * console.log('动画完成!')
121
148
  *
122
- * // 序列动画
123
- * ref.current?.animate((anim) => {
124
- * anim.translateX(100).step({ duration: 300 })
125
- * anim.scale(1.2).step({ duration: 200 })
126
- * anim.rotate(360).step({ duration: 500 })
127
- * })
149
+ * // 链式调用
150
+ * const animation = ref.current
151
+ * .animate({ transformOrigin: '50% 50%' })
152
+ * .translateX(100)
153
+ * .scale(1.2)
154
+ * .step({ duration: 300 })
155
+ * await animation.play()
128
156
  *
129
- * // 设置 transformOrigin
130
- * ref.current?.animate((anim) => {
131
- * anim.scaleX(0.5).step({ duration: 300 })
132
- * }, { transformOrigin: 'left center' })
157
+ * // 多段动画
158
+ * const animation = ref.current.animate()
159
+ * animation.translateX(100).step({ duration: 300 })
160
+ * animation.translateY(100).step({ duration: 300 })
161
+ * animation.scale(1.5).step({ duration: 300 })
162
+ * await animation.play()
163
+ *
164
+ * // 复用动画
165
+ * const slideIn = ref.current.animate().translateX(100).step({ duration: 300 })
166
+ * await slideIn.play() // 第一次
167
+ * await slideIn.play() // 第二次
133
168
  * ```
134
169
  */
135
- animate: (callback: (anim: IAnimation) => any, options?: TOptions & {
136
- finish: (event: EventType) => void;
137
- }) => void;
170
+ animate: (options?: TOptions) => IAnimation & {
171
+ play(): Promise<void>;
172
+ };
138
173
  /**
139
174
  * 获取元素的布局位置和尺寸信息
140
175
  * 基于小程序 SelectorQuery.boundingClientRect API
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ray-js/components",
3
- "version": "1.7.56-beta.2",
3
+ "version": "1.7.56-beta.4",
4
4
  "description": "Ray basic components",
5
5
  "keywords": [
6
6
  "ray"
@@ -29,8 +29,8 @@
29
29
  "dependencies": {
30
30
  "@ray-core/macro": "^0.4.9",
31
31
  "@ray-core/wechat": "^0.4.9",
32
- "@ray-js/adapter": "1.7.56-beta.2",
33
- "@ray-js/framework-shared": "1.7.56-beta.2",
32
+ "@ray-js/adapter": "1.7.56-beta.4",
33
+ "@ray-js/framework-shared": "1.7.56-beta.4",
34
34
  "ahooks": "^3.8.5",
35
35
  "clsx": "^1.2.1",
36
36
  "core-js": "^3.43.0",
@@ -40,11 +40,11 @@
40
40
  "style-to-object": "^0.3.0"
41
41
  },
42
42
  "devDependencies": {
43
- "@ray-js/cli": "1.7.56-beta.2"
43
+ "@ray-js/cli": "1.7.56-beta.4"
44
44
  },
45
45
  "publishConfig": {
46
46
  "access": "public",
47
47
  "registry": "https://registry.npmjs.org"
48
48
  },
49
- "gitHead": "511cd3f8d57c2888411700526781e65959037c5b"
49
+ "gitHead": "76b53482b255b74de7bfbeebc5a102a89031eb82"
50
50
  }