tencent.jquery.pix.component 1.0.88-beta3 → 1.0.90

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.
@@ -18,6 +18,7 @@ let $ = null;
18
18
  * @param {function} [options.click] 点击轮播图时触发,第一个参数为触发元素的jq,第二个参数为 options.list 中对应的项,第三个参数为当前页码
19
19
  * @param {function} [options.pageChanged] 翻页时触发,第一个参数为新生效页面元素的jq,第二个参数为 options.list 中对应的项,第三个参数为当前页码
20
20
  * @param {function} [options.renderCallback] 自定义渲染回调,第一个参数为 options.list 中对应的项,第二个参数为当前页码,返回值为渲染后的元素
21
+ * @param {boolean} [options.singNoSwitch=false] 当只有单张图片时是否禁用切换功能(隐藏分页和切换),默认为 false 即单图也展示分页和切换
21
22
  */
22
23
  export function Banner(options = {}) {
23
24
  $ = getEnv().$;
@@ -27,6 +28,7 @@ export function Banner(options = {}) {
27
28
  this.options.autoResize ??= true;
28
29
  this.options.durationMs = Number(options.durationMs || 0) || 0;
29
30
  this.options.dragThreshold = Number(options.dragThreshold || 0.2) || 0.2;
31
+ this.options.singNoSwitch ??= false;
30
32
  this.index = Number(options.index || 0) || 0;
31
33
  // 用于保存 options.list 中每一项对应的 imgObj(按 list 索引存储),避免依赖 jQuery 的 data 功能
32
34
  this.imgObjMap = {};
@@ -57,6 +59,9 @@ Banner.prototype.fitWidth = function () {
57
59
  * 跳转到上一页
58
60
  */
59
61
  Banner.prototype.prevPage = async function () {
62
+ if (this.options.singNoSwitch && this.options.list.length <= 1) {
63
+ return;
64
+ }
60
65
  this.pauseAutoPlay();
61
66
  await this.gotoPageUnchecked(this.index - 1);
62
67
  this.startAutoPlay();
@@ -66,6 +71,9 @@ Banner.prototype.prevPage = async function () {
66
71
  * 跳转到下一页
67
72
  */
68
73
  Banner.prototype.nextPage = async function () {
74
+ if (this.options.singNoSwitch && this.options.list.length <= 1) {
75
+ return;
76
+ }
69
77
  this.pauseAutoPlay();
70
78
  await this.gotoPageUnchecked(this.index + 1);
71
79
  this.startAutoPlay();
@@ -149,6 +157,10 @@ Banner.prototype.init = function () {
149
157
  }
150
158
 
151
159
  Banner.prototype.startAutoPlay = function () {
160
+ // 单图且 singNoSwitch 为 true 时,不启动自动播放
161
+ if (this.options.singNoSwitch && this.options.list.length === 1) {
162
+ return;
163
+ }
152
164
  if (this.options.durationMs > 0 && this.timer == null) {
153
165
  this.timer = setInterval(() => {
154
166
  this.nextPage()
@@ -173,9 +185,12 @@ Banner.prototype.createHtml = function () {
173
185
  $t.empty();
174
186
  const signWidth = this.signWidth = $t.width()
175
187
 
176
- const allWidth = this.allWidth = signWidth * (len + 2)
188
+ // 单图且 singNoSwitch true 时,不创建循环占位,直接展示单张图片
189
+ const isSingleNoSwitch = this.options.singNoSwitch && len === 1;
190
+
191
+ const allWidth = this.allWidth = isSingleNoSwitch ? signWidth : signWidth * (len + 2)
177
192
  const $inner = this.$inner = $(`<div class="banner-inner-transform"></div>`)
178
- this.currentTranslate = -signWidth * (this.index + 1)
193
+ this.currentTranslate = isSingleNoSwitch ? 0 : -signWidth * (this.index + 1)
179
194
  $inner.width(allWidth).css('transform', `translateX(${this.currentTranslate}px)`)
180
195
 
181
196
  // 重置 imgObj 映射表,按 list 的索引保存 imgObj 引用
@@ -186,10 +201,12 @@ Banner.prototype.createHtml = function () {
186
201
  }
187
202
  }
188
203
 
189
- // 加第0个位置
190
- $inner.append(`<div class="banner-inner-li" data-background-url="${this.options.list[len - 1].url}" style="width:${this.signWidth}px;">
191
- </div>
192
- `)
204
+ if (!isSingleNoSwitch) {
205
+ // 加第0个位置
206
+ $inner.append(`<div class="banner-inner-li" data-background-url="${this.options.list[len - 1].url}" style="width:${this.signWidth}px;">
207
+ </div>
208
+ `)
209
+ }
193
210
  for (let i = 0; i < len; i++) {
194
211
  const item = this.options.list[i]
195
212
  const $li = $(`<div class="banner-inner-li" data-background-url="${item.url}" style="width:${this.signWidth}px;">
@@ -197,19 +214,23 @@ Banner.prototype.createHtml = function () {
197
214
  `)
198
215
  $inner.append($li)
199
216
  }
200
- // 加第N+1
201
- $inner.append(`<div class="banner-inner-li" data-background-url="${this.options.list[0].url}" style="width:${this.signWidth}px;">
202
- </div>
203
- `)
217
+ if (!isSingleNoSwitch) {
218
+ // 加第N+1
219
+ $inner.append(`<div class="banner-inner-li" data-background-url="${this.options.list[0].url}" style="width:${this.signWidth}px;">
220
+ </div>
221
+ `)
222
+ }
204
223
 
205
224
  const preloadIdx = this.index;
206
225
  this.loadBackground(preloadIdx);
207
- this.loadBackground((preloadIdx + 1) % len); // prev
208
- this.loadBackground((preloadIdx - 1 + len) % len); // next
226
+ if (!isSingleNoSwitch) {
227
+ this.loadBackground((preloadIdx + 1) % len); // prev
228
+ this.loadBackground((preloadIdx - 1 + len) % len); // next
229
+ }
209
230
 
210
231
  $t.append($inner)
211
232
 
212
- if (this.options.isTitleEnabled) {
233
+ if (this.options.isTitleEnabled && !isSingleNoSwitch) {
213
234
  // 创建 titlebox
214
235
  const $titleBox = $(`<div class="banner-title-box">${this.options.list[this.index].title}</div>`)
215
236
 
@@ -246,24 +267,31 @@ Banner.prototype.createCustomHtml = function () {
246
267
  $t.empty();
247
268
  const signWidth = this.signWidth = $t.width()
248
269
 
249
- const allWidth = this.allWidth = signWidth * (len + 2)
270
+ // 单图且 singNoSwitch true 时,不创建循环占位
271
+ const isSingleNoSwitch = this.options.singNoSwitch && len === 1;
272
+
273
+ const allWidth = this.allWidth = isSingleNoSwitch ? signWidth : signWidth * (len + 2)
250
274
  const $inner = this.$inner = $(`<div class="banner-inner-transform"></div>`)
251
- this.currentTranslate = -signWidth * (this.index + 1)
275
+ this.currentTranslate = isSingleNoSwitch ? 0 : -signWidth * (this.index + 1)
252
276
  $inner.width(allWidth).css('transform', `translateX(${this.currentTranslate}px)`)
253
277
 
254
- let $li = $(this.options.renderCallback(this.options.list[len - 1], len - 1));
255
- $li.width(signWidth);
256
- $inner.append($li);
278
+ if (!isSingleNoSwitch) {
279
+ let $li = $(this.options.renderCallback(this.options.list[len - 1], len - 1));
280
+ $li.width(signWidth);
281
+ $inner.append($li);
282
+ }
257
283
 
258
284
  for (let i = 0; i < len; i++) {
259
- $li = $(this.options.renderCallback(this.options.list[i], i));
285
+ let $li = $(this.options.renderCallback(this.options.list[i], i));
260
286
  $li.width(signWidth);
261
287
  $inner.append($li);
262
288
  }
263
289
 
264
- $li = $(this.options.renderCallback(this.options.list[0], 0));
265
- $li.width(signWidth);
266
- $inner.append($li);
290
+ if (!isSingleNoSwitch) {
291
+ let $li = $(this.options.renderCallback(this.options.list[0], 0));
292
+ $li.width(signWidth);
293
+ $inner.append($li);
294
+ }
267
295
 
268
296
  $t.append($inner);
269
297
  }
@@ -273,6 +301,19 @@ Banner.prototype.bindEvent = function () {
273
301
  const len = this.options.list.length;
274
302
  const $inner = this.$inner;
275
303
 
304
+ // 单图且 singNoSwitch 为 true 时,不绑定拖拽和切换事件
305
+ if (this.options.singNoSwitch && len === 1) {
306
+ // 仅绑定点击事件
307
+ $inner.on('click', function (e) {
308
+ if (!self.options.click) {
309
+ return
310
+ }
311
+ const $t = self.$inner.children().eq(0);
312
+ self.options.click($t, self.options.list[0], 0);
313
+ });
314
+ return;
315
+ }
316
+
276
317
  // pStart 时的 clientX
277
318
  let originalX = 0;
278
319
 
@@ -467,8 +508,9 @@ function childIdxToListIdx(childIdx, len) {
467
508
  Banner.prototype.loadAllBackgrounds = function () {
468
509
  const len = this.options.list.length;
469
510
  const imgObjMap = this.imgObjMap || {};
511
+ const isSingleNoSwitch = this.options.singNoSwitch && len === 1;
470
512
  this.$inner.children().each(function (childIdx) {
471
- const listIdx = childIdxToListIdx(childIdx, len);
513
+ const listIdx = isSingleNoSwitch ? childIdx : childIdxToListIdx(childIdx, len);
472
514
  applyBackgroundImage($(this), imgObjMap[listIdx]);
473
515
  });
474
516
  // console.log('banner images loaded');
@@ -481,7 +523,8 @@ Banner.prototype.loadAllBackgrounds = function () {
481
523
  Banner.prototype.loadBackground = function (pos) {
482
524
  console.log('loadBackground pos === ', pos);
483
525
  const imgObjMap = this.imgObjMap || {};
484
- let $item = this.$inner.children().eq(pos + 1);
526
+ const isSingleNoSwitch = this.options.singNoSwitch && this.options.list.length === 1;
527
+ let $item = this.$inner.children().eq(isSingleNoSwitch ? pos : pos + 1);
485
528
  applyBackgroundImage($item, imgObjMap[pos]);
486
529
 
487
530
  // 加载滚动占位的图片
@@ -48,6 +48,7 @@ const DEFAULTS = {
48
48
  showLoading: null, // 展示loading的回调函数 params:$node
49
49
  hideLoading: null, // 隐藏loading的回调函数
50
50
  createLoading: null, // 创建loading的回调函数
51
+ useDropDown: false, // 是否启用下拉刷新功能(会在 touch 设备上启用下拉手势,触发 dropDownCall 回调)
51
52
  createDropDown: null, // 创建下拉时展现出来的元素的回调函数 params:$node
52
53
  dropDownCall: null, // 当下拉位置超过50%时触发的有效下拉回调函数
53
54
  };
@@ -178,7 +179,6 @@ Waterfallv2.prototype.init = function (optionsMain = null) {
178
179
 
179
180
 
180
181
  // 如果有定义loading函数 那么创建一个loading节点元素
181
- console.log('options.createLoading', options.createLoading)
182
182
  if (options.createLoading) {
183
183
  this.$loadingNode = $(
184
184
  `<div class="waterfallv2-loading" style="position:absolute;top:0;left:0;width:100%;transform: translate(0px, -99999px)"></div>`
@@ -201,222 +201,223 @@ Waterfallv2.prototype.init = function (optionsMain = null) {
201
201
  }
202
202
  });
203
203
 
204
- const $child = $container.children();
205
- // child容器绑定一个下拉事件
206
- // 仅当 $scrollDom.scrollTop() === 0 时,才会跟随手指下拉产生 translateY 位移
207
- // 松手后以过渡动画自然恢复到 translateY(0)
208
- let pullStartY = 0; // 触摸起始位置
209
- let pullCurrentY = 0; // 当前位移
210
- let isPulling = false; // 是否处于下拉中
211
- let pullReachThreshold = false; // 本次下拉是否已达到有效阈值(超过50%)
212
- const PULL_DAMPING = 0.7; // 下拉阻尼系数(手指滑动距离 * 系数 = 实际位移),产生橡皮筋效果
213
- const PULL_DEFAULT_HEIGHT = 200; // 下拉元素默认高度(当 $dropDownNode 不存在或高度为 0 时的兜底值)
214
- const PULL_RESET_DURATION = 300; // 松手回弹动画时长(ms)
215
- // 以下 3 个变量会在每次 touchstart 时基于 $dropDownNode 的实际高度动态计算:
216
- let pullMax = PULL_DEFAULT_HEIGHT * 1.5; // 下拉最大位移(px)
217
- let pullThreshold = PULL_DEFAULT_HEIGHT; // 有效下拉阈值(超过此距离则视为一次有效下拉)
218
- let pullHoldOffset = PULL_DEFAULT_HEIGHT; // 触发有效下拉后,松手悬停保持的位移量(保持下拉元素可见)
219
-
220
- // 下拉保持状态标志(挂在实例上,方便 hideDropDown 等原型方法访问)
221
- this.isPullHolding = false;
204
+ if(options.useDropDown) {
205
+ const $child = $container.children();
206
+ // 给child容器绑定一个下拉事件
207
+ // 仅当 $scrollDom.scrollTop() === 0 时,才会跟随手指下拉产生 translateY 位移
208
+ // 松手后以过渡动画自然恢复到 translateY(0)
209
+ let pullStartY = 0; // 触摸起始位置
210
+ let pullCurrentY = 0; // 当前位移
211
+ let isPulling = false; // 是否处于下拉中
212
+ let pullReachThreshold = false; // 本次下拉是否已达到有效阈值(超过50%)
213
+ const PULL_DAMPING = 0.7; // 下拉阻尼系数(手指滑动距离 * 系数 = 实际位移),产生橡皮筋效果
214
+ const PULL_DEFAULT_HEIGHT = 200; // 下拉元素默认高度(当 $dropDownNode 不存在或高度为 0 时的兜底值)
215
+ const PULL_RESET_DURATION = 300; // 松手回弹动画时长(ms)
216
+ // 以下 3 个变量会在每次 touchstart 时基于 $dropDownNode 的实际高度动态计算:
217
+ let pullMax = PULL_DEFAULT_HEIGHT * 1.5; // 下拉最大位移(px)
218
+ let pullThreshold = PULL_DEFAULT_HEIGHT; // 有效下拉阈值(超过此距离则视为一次有效下拉)
219
+ let pullHoldOffset = PULL_DEFAULT_HEIGHT; // 触发有效下拉后,松手悬停保持的位移量(保持下拉元素可见)
220
+
221
+ // 下拉保持状态标志(挂在实例上,方便 hideDropDown 等原型方法访问)
222
+ this.isPullHolding = false;
222
223
 
223
- // 根据 $dropDownNode 的实际高度动态计算下拉相关的 3 个关键位移值
224
- // 每次触摸开始时调用,保证拿到最新高度(适应运行时 $dropDownNode 内容变化)
225
- const recalcPullMetrics = function () {
226
- let dropH = 0;
227
- if (self.$dropDownNode && self.$dropDownNode.length) {
228
- dropH = self.$dropDownNode.height() || 0;
229
- }
230
- if (dropH <= 0) {
231
- dropH = PULL_DEFAULT_HEIGHT;
232
- }
233
- pullThreshold = dropH; // 下拉超过 dropDown 完整高度即视为有效
234
- pullHoldOffset = dropH; // 保持位置刚好让 dropDown 完全可见
235
- pullMax = dropH * 1.5; // 下拉最大位移为 1.5 倍高度,提供橡皮筋余量
236
- };
237
-
238
- const pullDomNode = $child.get(0);
239
- console.log('pullDomNode',pullDomNode);
240
-
241
- // 通用的 clientY 提取函数:同时兼容触摸事件和鼠标事件
242
- // - 触摸事件:从 e.touches[0] 或 e.originalEvent.touches[0] 读取
243
- // - 鼠标事件:直接从 e.clientY 读取
244
- const getClientY = function (e) {
245
- if (e.touches && e.touches.length) return e.touches[0].clientY;
246
- if (e.originalEvent && e.originalEvent.touches && e.originalEvent.touches.length) {
247
- return e.originalEvent.touches[0].clientY;
224
+ // 根据 $dropDownNode 的实际高度动态计算下拉相关的 3 个关键位移值
225
+ // 每次触摸开始时调用,保证拿到最新高度(适应运行时 $dropDownNode 内容变化)
226
+ const recalcPullMetrics = function () {
227
+ let dropH = 0;
228
+ if (self.$dropDownNode && self.$dropDownNode.length) {
229
+ dropH = self.$dropDownNode.height() || 0;
230
+ }
231
+ if (dropH <= 0) {
232
+ dropH = PULL_DEFAULT_HEIGHT;
233
+ }
234
+ pullThreshold = dropH; // 下拉超过 dropDown 完整高度即视为有效
235
+ pullHoldOffset = dropH; // 保持位置刚好让 dropDown 完全可见
236
+ pullMax = dropH * 1.5; // 下拉最大位移为 1.5 倍高度,提供橡皮筋余量
237
+ };
238
+
239
+ const pullDomNode = $child.get(0);
240
+
241
+ // 通用的 clientY 提取函数:同时兼容触摸事件和鼠标事件
242
+ // - 触摸事件:从 e.touches[0] 或 e.originalEvent.touches[0] 读取
243
+ // - 鼠标事件:直接从 e.clientY 读取
244
+ const getClientY = function (e) {
245
+ if (e.touches && e.touches.length) return e.touches[0].clientY;
246
+ if (e.originalEvent && e.originalEvent.touches && e.originalEvent.touches.length) {
247
+ return e.originalEvent.touches[0].clientY;
248
+ }
249
+ if (typeof e.clientY === 'number') return e.clientY;
250
+ return null;
251
+ };
252
+
253
+ // 创建下拉时展现出来的元素(类似 loading 节点的机制)
254
+ // 节点放置在 $child 内部最顶部,通过 top: -Hpx 自身负偏移隐藏在列表可视区之上;
255
+ // 当 $child 执行 translateY(offset) 向下位移时,该元素会跟随一起下移并自然露出。
256
+ this.$dropDownNode = null;
257
+ if (options.createDropDown && options.createDropDown.constructor === Function) {
258
+ this.$dropDownNode = $(
259
+ `<div class="waterfallv2-dropdown" style="position:absolute;left:0;right:0;bottom:100%;width:100%;"></div>`
260
+ );
261
+ // 插入到 $child 的最前面,使其定位参考 $child($child 为定位上下文时最佳;
262
+ // 若 $child 不是定位元素,也可使用 bottom:100% 让其处于 $child 上方)
263
+ $child.css('position', $child.css('position') === 'static' ? 'relative' : $child.css('position'));
264
+ $child.prepend(this.$dropDownNode);
265
+ options.createDropDown(this.$dropDownNode);
248
266
  }
249
- if (typeof e.clientY === 'number') return e.clientY;
250
- return null;
251
- };
252
-
253
- // 创建下拉时展现出来的元素(类似 loading 节点的机制)
254
- // 节点放置在 $child 内部最顶部,通过 top: -Hpx 自身负偏移隐藏在列表可视区之上;
255
- // 当 $child 执行 translateY(offset) 向下位移时,该元素会跟随一起下移并自然露出。
256
- this.$dropDownNode = null;
257
- if (options.createDropDown && options.createDropDown.constructor === Function) {
258
- this.$dropDownNode = $(
259
- `<div class="waterfallv2-dropdown" style="position:absolute;left:0;right:0;bottom:100%;width:100%;"></div>`
260
- );
261
- // 插入到 $child 的最前面,使其定位参考 $child($child 为定位上下文时最佳;
262
- // 若 $child 不是定位元素,也可使用 bottom:100% 让其处于 $child 上方)
263
- $child.css('position', $child.css('position') === 'static' ? 'relative' : $child.css('position'));
264
- $child.prepend(this.$dropDownNode);
265
- options.createDropDown(this.$dropDownNode);
266
- }
267
267
 
268
- const onTouchStart = function (e) {
269
- // 若处于下拉保持状态(等待开发者手动收回),则忽略新的下拉手势
270
- if (self.isPullHolding) {
271
- isPulling = false;
272
- return;
273
- }
274
- // 仅当滚动容器已在顶部时才进入下拉模式
275
- if ($scrollDom.scrollTop() !== 0) {
276
- isPulling = false;
277
- return;
278
- }
279
- const clientY = getClientY(e);
280
- if (clientY === null) return;
281
- // 在每次下拉开始时动态获取 $dropDownNode 的实际高度,重新计算下拉关键位移值
282
- recalcPullMetrics();
283
- pullStartY = clientY;
284
- pullCurrentY = 0;
285
- isPulling = true;
286
- // 移除过渡,确保跟手
287
- $child.css('transition', 'none');
288
- };
289
-
290
- const onTouchMove = function (e) {
291
- if (!isPulling) return;
292
- const clientY = getClientY(e);
293
- if (clientY === null) return;
294
-
295
- const dy = clientY - pullStartY;
296
-
297
- // 仅响应下拉方向
298
- if (dy <= 0) {
268
+ const onTouchStart = function (e) {
269
+ // 若处于下拉保持状态(等待开发者手动收回),则忽略新的下拉手势
270
+ if (self.isPullHolding) {
271
+ isPulling = false;
272
+ return;
273
+ }
274
+ // 仅当滚动容器已在顶部时才进入下拉模式
275
+ if ($scrollDom.scrollTop() !== 0) {
276
+ isPulling = false;
277
+ return;
278
+ }
279
+ const clientY = getClientY(e);
280
+ if (clientY === null) return;
281
+ // 在每次下拉开始时动态获取 $dropDownNode 的实际高度,重新计算下拉关键位移值
282
+ recalcPullMetrics();
283
+ pullStartY = clientY;
299
284
  pullCurrentY = 0;
300
- $child.css('transform', 'translateY(0px)');
301
- return;
302
- }
285
+ isPulling = true;
286
+ // 移除过渡,确保跟手
287
+ $child.css('transition', 'none');
288
+ };
303
289
 
304
- // 若在下拉过程中滚动位置不为 0(例如系统反弹),则中断下拉
305
- if ($scrollDom.scrollTop() > 0) {
306
- return;
307
- }
290
+ const onTouchMove = function (e) {
291
+ if (!isPulling) return;
292
+ const clientY = getClientY(e);
293
+ if (clientY === null) return;
308
294
 
309
- // 阻尼衰减 + 最大位移限制
310
- let offset = dy * PULL_DAMPING;
295
+ const dy = clientY - pullStartY;
311
296
 
312
- if (offset > pullMax) offset = pullMax;
313
- pullCurrentY = offset;
297
+ // 仅响应下拉方向
298
+ if (dy <= 0) {
299
+ pullCurrentY = 0;
300
+ $child.css('transform', 'translateY(0px)');
301
+ return;
302
+ }
314
303
 
315
- // 判断是否达到有效下拉阈值(只要过程中曾经超过阈值即视为达到)
304
+ // 若在下拉过程中滚动位置不为 0(例如系统反弹),则中断下拉
305
+ if ($scrollDom.scrollTop() > 0) {
306
+ return;
307
+ }
316
308
 
317
- if (offset >= pullThreshold) {
309
+ // 阻尼衰减 + 最大位移限制
310
+ let offset = dy * PULL_DAMPING;
318
311
 
319
- pullReachThreshold = true;
320
- }
312
+ if (offset > pullMax) offset = pullMax;
313
+ pullCurrentY = offset;
321
314
 
322
- // 在模拟器下 下拉元素还有个默认下滑,这里相对再减去一点值
323
- $child.css('transform', 'translateY(' + offset * 0.8 + 'px)');
315
+ // 判断是否达到有效下拉阈值(只要过程中曾经超过阈值即视为达到)
324
316
 
325
- // 处于下拉偏移过程中,阻止原生滚动与橡皮筋效果
326
- if (e.cancelable) {
327
- if(e.preventDefault) e.preventDefault();
328
- }
329
- };
317
+ if (offset >= pullThreshold) {
318
+
319
+ pullReachThreshold = true;
320
+ }
330
321
 
331
- const onTouchEnd = function () {
332
- if (!isPulling) return;
333
- isPulling = false;
322
+ // 在模拟器下 下拉元素还有个默认下滑,这里相对再减去一点值
323
+ $child.css('transform', 'translateY(' + offset * 0.8 + 'px)');
334
324
 
335
- // 本次是否达到有效阈值
336
- const reached = pullReachThreshold;
337
- pullReachThreshold = false;
325
+ // 处于下拉偏移过程中,阻止原生滚动与橡皮筋效果
326
+ if (e.cancelable) {
327
+ if(e.preventDefault) e.preventDefault();
328
+ }
329
+ };
338
330
 
339
- if (reached) {
340
- // === 有效下拉:悬停保持,等待开发者手动调用 hideDropDown() 收回 ===
341
- // 悬停保持位移:使用 touchstart 时动态计算出的 pullHoldOffset(基于 $dropDownNode 实际高度)
342
- const holdOffset = pullHoldOffset;
331
+ const onTouchEnd = function () {
332
+ if (!isPulling) return;
333
+ isPulling = false;
343
334
 
344
- // 标记进入保持状态
345
- self.isPullHolding = true;
335
+ // 本次是否达到有效阈值
336
+ const reached = pullReachThreshold;
337
+ pullReachThreshold = false;
338
+
339
+ if (reached) {
340
+ // === 有效下拉:悬停保持,等待开发者手动调用 hideDropDown() 收回 ===
341
+ // 悬停保持位移:使用 touchstart 时动态计算出的 pullHoldOffset(基于 $dropDownNode 实际高度)
342
+ const holdOffset = pullHoldOffset;
343
+
344
+ // 标记进入保持状态
345
+ self.isPullHolding = true;
346
+
347
+ // 以过渡动画平滑过渡到保持位置
348
+ $child.css('transition', 'transform ' + PULL_RESET_DURATION + 'ms ease-out');
349
+ $child.css('transform', 'translateY(' + holdOffset + 'px)');
350
+
351
+ // 动画结束后清理 transition
352
+ setTimeout(function () {
353
+ $child.css('transition', '');
354
+ pullCurrentY = holdOffset;
355
+ }, PULL_RESET_DURATION);
356
+
357
+ // 触发 dropDownCall 回调(在进入保持状态后回调,便于开发者在回调内部发起请求,
358
+ // 完成后调用 self.hideDropDown() 收回下拉)
359
+ if (options.dropDownCall && options.dropDownCall.constructor === Function) {
360
+ try {
361
+ options.dropDownCall(self.$dropDownNode);
362
+ } catch (err) {
363
+ console.error('Waterfallv2: dropDownCall error', err);
364
+ }
365
+ }
366
+ return;
367
+ }
346
368
 
347
- // 以过渡动画平滑过渡到保持位置
369
+ // === 未达到阈值:按原逻辑自然回弹到 0 ===
348
370
  $child.css('transition', 'transform ' + PULL_RESET_DURATION + 'ms ease-out');
349
- $child.css('transform', 'translateY(' + holdOffset + 'px)');
371
+ $child.css('transform', 'translateY(0px)');
350
372
 
351
- // 动画结束后清理 transition
373
+ // 动画结束后清理 transition,避免影响后续其他 transform 修改
352
374
  setTimeout(function () {
353
375
  $child.css('transition', '');
354
- pullCurrentY = holdOffset;
376
+ pullCurrentY = 0;
355
377
  }, PULL_RESET_DURATION);
356
-
357
- // 触发 dropDownCall 回调(在进入保持状态后回调,便于开发者在回调内部发起请求,
358
- // 完成后调用 self.hideDropDown() 收回下拉)
359
- if (options.dropDownCall && options.dropDownCall.constructor === Function) {
360
- try {
361
- options.dropDownCall(self.$dropDownNode);
362
- } catch (err) {
363
- console.error('Waterfallv2: dropDownCall error', err);
364
- }
365
- }
366
- return;
367
- }
368
-
369
- // === 未达到阈值:按原逻辑自然回弹到 0 ===
370
- $child.css('transition', 'transform ' + PULL_RESET_DURATION + 'ms ease-out');
371
- $child.css('transform', 'translateY(0px)');
372
-
373
- // 动画结束后清理 transition,避免影响后续其他 transform 修改
374
- setTimeout(function () {
375
- $child.css('transition', '');
376
- pullCurrentY = 0;
377
- }, PULL_RESET_DURATION);
378
- };
379
-
380
- // 桌面端鼠标事件支持:按照触摸事件的同样规律实现鼠标拖拽下拉
381
- // mousedown / mousemove / mouseup 均绑定在 pullDomNode 上
382
- // 由于鼠标可能拖出 pullDomNode 边界,额外绑定 mouseleave 作为兼底中断(视同松手)
383
- const onMouseMove = function (e) {
384
- onTouchMove(e);
385
- };
386
- const onMouseUp = function (e) {
387
- onTouchEnd(e);
388
- };
389
- const onMouseLeave = function (e) {
390
- // 拖拽过程中移出 pullDomNode 时视为松手,触发回弹或保持逻辑
391
- if (isPulling) {
378
+ };
379
+
380
+ // 桌面端鼠标事件支持:按照触摸事件的同样规律实现鼠标拖拽下拉
381
+ // mousedown / mousemove / mouseup 均绑定在 pullDomNode 上
382
+ // 由于鼠标可能拖出 pullDomNode 边界,额外绑定 mouseleave 作为兼底中断(视同松手)
383
+ const onMouseMove = function (e) {
384
+ onTouchMove(e);
385
+ };
386
+ const onMouseUp = function (e) {
392
387
  onTouchEnd(e);
388
+ };
389
+ const onMouseLeave = function (e) {
390
+ // 拖拽过程中移出 pullDomNode 时视为松手,触发回弹或保持逻辑
391
+ if (isPulling) {
392
+ onTouchEnd(e);
393
+ }
394
+ };
395
+ const onMouseDown = function (e) {
396
+ onTouchStart(e);
397
+ };
398
+ // 小应用环境只支持mousedown的鼠标点击类型事件,不支持touch事件
399
+ if (pullDomNode) {
400
+ pullDomNode.addEventListener('mousedown', onMouseDown);
401
+ pullDomNode.addEventListener('mousemove', onMouseMove);
402
+ pullDomNode.addEventListener('mouseup', onMouseUp);
403
+ pullDomNode.addEventListener('mouseleave', onMouseLeave);
393
404
  }
394
- };
395
- const onMouseDown = function (e) {
396
- onTouchStart(e);
397
- };
398
- // 小应用环境只支持mousedown的鼠标点击类型事件,不支持touch事件
399
- if (pullDomNode) {
400
- pullDomNode.addEventListener('mousedown', onMouseDown);
401
- pullDomNode.addEventListener('mousemove', onMouseMove);
402
- pullDomNode.addEventListener('mouseup', onMouseUp);
403
- pullDomNode.addEventListener('mouseleave', onMouseLeave);
404
- }
405
-
406
- // 保存引用,便于 destroy 时解绑;同时供 hideDropDown 原型方法使用
407
- this._pullHandlers = {
408
- $child: $child,
409
- node: pullDomNode,
410
- onTouchStart: onTouchStart,
411
- onTouchMove: onTouchMove,
412
- onTouchEnd: onTouchEnd,
413
- onMouseDown: onMouseDown,
414
- onMouseMove: onMouseMove,
415
- onMouseUp: onMouseUp,
416
- onMouseLeave: onMouseLeave,
417
- resetDuration: PULL_RESET_DURATION,
418
- };
419
405
 
406
+ // 保存引用,便于 destroy 时解绑;同时供 hideDropDown 原型方法使用
407
+ this._pullHandlers = {
408
+ $child: $child,
409
+ node: pullDomNode,
410
+ onTouchStart: onTouchStart,
411
+ onTouchMove: onTouchMove,
412
+ onTouchEnd: onTouchEnd,
413
+ onMouseDown: onMouseDown,
414
+ onMouseMove: onMouseMove,
415
+ onMouseUp: onMouseUp,
416
+ onMouseLeave: onMouseLeave,
417
+ resetDuration: PULL_RESET_DURATION,
418
+ };
419
+ }
420
+
420
421
  this.scrollTop = $scrollDom.scrollTop(); // 当前滚动位置
421
422
 
422
423
  // 首次渲染
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tencent.jquery.pix.component",
3
- "version": "1.0.88-beta3",
3
+ "version": "1.0.90",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "files": [