|
|
@@ -233,6 +233,170 @@ export const vCardExpand = {
|
|
233
|
233
|
}
|
|
234
|
234
|
}
|
|
235
|
235
|
|
|
|
236
|
+
|
|
|
237
|
+
|
|
|
238
|
+// 自动滚动公共方法
|
|
|
239
|
+const autoScrollUtils = {
|
|
|
240
|
+ /**
|
|
|
241
|
+ * 初始化自动滚动
|
|
|
242
|
+ * @param {HTMLElement} scrollContainer - 滚动容器元素
|
|
|
243
|
+ * @param {Object} options - 配置选项
|
|
|
244
|
+ * @returns {Object} 控制方法
|
|
|
245
|
+ */
|
|
|
246
|
+ init (scrollContainer, options = {}) {
|
|
|
247
|
+ const defaultOptions = {
|
|
|
248
|
+ speed: 0.2,
|
|
|
249
|
+ pauseDuration: 3000,
|
|
|
250
|
+ autoStart: true
|
|
|
251
|
+ };
|
|
|
252
|
+
|
|
|
253
|
+ const config = { ...defaultOptions, ...options };
|
|
|
254
|
+
|
|
|
255
|
+ // 状态变量
|
|
|
256
|
+ let isScrolling = config.autoStart;
|
|
|
257
|
+ let animationId = null;
|
|
|
258
|
+ let direction = 'down';
|
|
|
259
|
+ let isAtEdge = false;
|
|
|
260
|
+ let curScrollTop = scrollContainer.scrollTop;
|
|
|
261
|
+
|
|
|
262
|
+ // 滚动函数
|
|
|
263
|
+ const scroll = () => {
|
|
|
264
|
+ if (!isScrolling) {
|
|
|
265
|
+ if (animationId) {
|
|
|
266
|
+ cancelAnimationFrame(animationId);
|
|
|
267
|
+ animationId = null;
|
|
|
268
|
+ }
|
|
|
269
|
+ return;
|
|
|
270
|
+ }
|
|
|
271
|
+
|
|
|
272
|
+ const scrollHeight = scrollContainer.scrollHeight;
|
|
|
273
|
+ const clientHeight = scrollContainer.clientHeight;
|
|
|
274
|
+
|
|
|
275
|
+ const scrollTop = scrollContainer.scrollTop;
|
|
|
276
|
+
|
|
|
277
|
+ // 检查边界
|
|
|
278
|
+ const isAtBottom = Math.abs(scrollTop + clientHeight - scrollHeight) < 1;
|
|
|
279
|
+ const isAtTop = scrollTop < 1;
|
|
|
280
|
+
|
|
|
281
|
+
|
|
|
282
|
+ if ((isAtBottom && direction === 'down') || (isAtTop && direction === 'up')) {
|
|
|
283
|
+ if (!isAtEdge) {
|
|
|
284
|
+ isAtEdge = true;
|
|
|
285
|
+ setTimeout(() => {
|
|
|
286
|
+ direction = direction === 'down' ? 'up' : 'down';
|
|
|
287
|
+ isAtEdge = false;
|
|
|
288
|
+ animationId = requestAnimationFrame(scroll);
|
|
|
289
|
+ }, config.pauseDuration);
|
|
|
290
|
+ return;
|
|
|
291
|
+ }
|
|
|
292
|
+ } else {
|
|
|
293
|
+ isAtEdge = false;
|
|
|
294
|
+ }
|
|
|
295
|
+
|
|
|
296
|
+ // 执行滚动
|
|
|
297
|
+ if (direction === 'down') {
|
|
|
298
|
+ curScrollTop += config.speed
|
|
|
299
|
+ } else {
|
|
|
300
|
+ curScrollTop -= config.speed
|
|
|
301
|
+ }
|
|
|
302
|
+ scrollContainer.scrollTop = Math.floor(curScrollTop)
|
|
|
303
|
+ animationId = requestAnimationFrame(scroll);
|
|
|
304
|
+ };
|
|
|
305
|
+
|
|
|
306
|
+ // 控制方法
|
|
|
307
|
+ const start = () => {
|
|
|
308
|
+ if (isScrolling) return;
|
|
|
309
|
+ isScrolling = true;
|
|
|
310
|
+ animationId = requestAnimationFrame(scroll);
|
|
|
311
|
+ };
|
|
|
312
|
+
|
|
|
313
|
+ const pause = () => {
|
|
|
314
|
+ isScrolling = false;
|
|
|
315
|
+ if (animationId) {
|
|
|
316
|
+ cancelAnimationFrame(animationId);
|
|
|
317
|
+ animationId = null;
|
|
|
318
|
+ }
|
|
|
319
|
+ };
|
|
|
320
|
+
|
|
|
321
|
+ const resume = () => {
|
|
|
322
|
+ if (isScrolling && animationId) return;
|
|
|
323
|
+ curScrollTop = scrollContainer.scrollTop
|
|
|
324
|
+ isScrolling = true;
|
|
|
325
|
+ animationId = requestAnimationFrame(scroll);
|
|
|
326
|
+ };
|
|
|
327
|
+
|
|
|
328
|
+ const stop = () => {
|
|
|
329
|
+ isScrolling = false;
|
|
|
330
|
+ scrollContainer.scrollTop = 0;
|
|
|
331
|
+ direction = 'down';
|
|
|
332
|
+ if (animationId) {
|
|
|
333
|
+ cancelAnimationFrame(animationId);
|
|
|
334
|
+ animationId = null;
|
|
|
335
|
+ }
|
|
|
336
|
+ };
|
|
|
337
|
+
|
|
|
338
|
+ return { start, pause, resume, stop };
|
|
|
339
|
+ }
|
|
|
340
|
+};
|
|
|
341
|
+
|
|
|
342
|
+export const autoScrollDirective = {
|
|
|
343
|
+ mounted (el, binding) {
|
|
|
344
|
+ const config = binding.value || {};
|
|
|
345
|
+
|
|
|
346
|
+ // 默认配置
|
|
|
347
|
+ const defaultConfig = {
|
|
|
348
|
+ speed: 0.2, // 滚动速度 px/帧
|
|
|
349
|
+ direction: 'down', // 初始方向
|
|
|
350
|
+ pauseDuration: 3000, // 到底部/顶部暂停时间
|
|
|
351
|
+ hoverPause: true, // 鼠标悬停暂停
|
|
|
352
|
+ autoStart: true // 是否自动开始
|
|
|
353
|
+ };
|
|
|
354
|
+
|
|
|
355
|
+ const options = { ...defaultConfig, ...config };
|
|
|
356
|
+
|
|
|
357
|
+ const autoScrollHandler = autoScrollUtils.init(el.querySelector('.el-scrollbar__wrap'), {
|
|
|
358
|
+ speed: options.speed,
|
|
|
359
|
+ pauseDuration: options.pauseDuration
|
|
|
360
|
+ })
|
|
|
361
|
+
|
|
|
362
|
+
|
|
|
363
|
+ // 鼠标悬停暂停功能
|
|
|
364
|
+ if (options.hoverPause) {
|
|
|
365
|
+ el.addEventListener('mouseenter', () => {
|
|
|
366
|
+ autoScrollHandler.pause()
|
|
|
367
|
+ });
|
|
|
368
|
+
|
|
|
369
|
+ el.addEventListener('mouseleave', () => {
|
|
|
370
|
+ autoScrollHandler.resume()
|
|
|
371
|
+ });
|
|
|
372
|
+ }
|
|
|
373
|
+
|
|
|
374
|
+ // 存储方法到元素上,供外部调用
|
|
|
375
|
+ el._autoScroll = {
|
|
|
376
|
+ start: autoScrollHandler.start,
|
|
|
377
|
+ pause: autoScrollHandler.pause,
|
|
|
378
|
+ resume: autoScrollHandler.resume,
|
|
|
379
|
+ stop: autoScrollHandler.stop
|
|
|
380
|
+ };
|
|
|
381
|
+ },
|
|
|
382
|
+ updated (el, binding) {
|
|
|
383
|
+ if (el._autoScroll) {
|
|
|
384
|
+ nextTick(() => {
|
|
|
385
|
+ el._autoScroll.resume();
|
|
|
386
|
+ })
|
|
|
387
|
+ }
|
|
|
388
|
+ },
|
|
|
389
|
+ unmounted (el) {
|
|
|
390
|
+ // 清理
|
|
|
391
|
+ if (el._autoScroll) {
|
|
|
392
|
+ el._autoScroll.stop();
|
|
|
393
|
+ delete el._autoScroll;
|
|
|
394
|
+ }
|
|
|
395
|
+ }
|
|
|
396
|
+};
|
|
|
397
|
+
|
|
|
398
|
+
|
|
|
399
|
+
|
|
236
|
400
|
// 导出工具函数供外部使用
|
|
237
|
401
|
export {
|
|
238
|
402
|
toggleCard,
|