You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为函数调用设置延迟?解决Material UI风格按钮涟漪清理异常

解决Material UI按钮涟漪快速点击时的清理问题

问题核心是快速连续点击时,多个清理操作同时触发,各自的延迟逻辑互相干扰,没有按顺序等待前一个动画完成再处理下一个。解决思路是给清理操作加个任务队列,让它们排队执行。

具体实现步骤:

  1. 新增两个状态:一个存待清理的涟漪ID队列,一个标记是否正在处理队列
  2. 修改handleRippleRemoval:只触发涟漪淡出动画,把要清理的ID加入队列
  3. 写一个队列处理函数,确保前一个清理完成后再处理下一个

代码示例(React):

import { useState, useCallback, useEffect } from 'react';

const RippleButton = () => {
  const [ripples, setRipples] = useState([]);
  const [cleanupQueue, setCleanupQueue] = useState([]);
  const [isProcessing, setIsProcessing] = useState(false);
  // 用递增计数器生成唯一涟漪ID,比Date.now()更可靠
  const [rippleIdCounter, setRippleIdCounter] = useState(0);

  // 生成新涟漪的函数
  const addRipple = (e) => {
    const button = e.currentTarget;
    const rect = button.getBoundingClientRect();
    const size = Math.max(rect.width, rect.height);
    const x = e.clientX - rect.left - size / 2;
    const y = e.clientY - rect.top - size / 2;

    const newRipple = {
      id: rippleIdCounter,
      width: `${size}px`,
      height: `${size}px`,
      top: `${y}px`,
      left: `${x}px`,
      visible: true
    };

    setRipples(prev => [...prev, newRipple]);
    setRippleIdCounter(prev => prev + 1);
  };

  // 处理清理队列的核心函数
  const processCleanupQueue = useCallback(async () => {
    // 如果正在处理或队列为空,直接返回
    if (isProcessing || cleanupQueue.length === 0) return;

    setIsProcessing(true);
    const targetId = cleanupQueue[0];

    // 等待淡出动画完成(550ms)
    await new Promise(resolve => setTimeout(resolve, 550));

    // 从涟漪数组中移除目标涟漪
    setRipples(prev => prev.filter(ripple => ripple.id !== targetId));
    // 移除队列中的第一个任务
    setCleanupQueue(prev => prev.slice(1));
    // 标记处理完成,继续处理下一个任务
    setIsProcessing(false);
    processCleanupQueue();
  }, [cleanupQueue, isProcessing]);

  // 修改后的涟漪移除触发函数
  const handleRippleRemoval = () => {
    if (ripples.length === 0) return;
    const lastRipple = ripples[ripples.length - 1];

    // 触发淡出动画
    setRipples(prev => prev.map(ripple => 
      ripple.id === lastRipple.id ? {...ripple, visible: false} : ripple
    ));

    // 避免重复加入同一个ID到队列
    if (!cleanupQueue.includes(lastRipple.id)) {
      setCleanupQueue(prev => [...prev, lastRipple.id]);
    }
  };

  // 监听队列变化,自动启动处理
  useEffect(() => {
    processCleanupQueue();
  }, [cleanupQueue]);

  return (
    <button 
      onMouseDown={addRipple}
      onMouseUp={handleRippleRemoval}
      onMouseLeave={handleRippleRemoval} // 鼠标离开也要触发清理
      style={{
        position: 'relative',
        overflow: 'hidden',
        padding: '12px 24px',
        fontSize: '16px',
        border: 'none',
        borderRadius: '4px',
        backgroundColor: '#2196f3',
        color: 'white',
        cursor: 'pointer'
      }}
    >
      带涟漪的按钮
      {ripples.map(ripple => (
        <div
          key={ripple.id}
          style={{
            position: 'absolute',
            width: ripple.width,
            height: ripple.height,
            top: ripple.top,
            left: ripple.left,
            borderRadius: '50%',
            backgroundColor: 'rgba(255,255,255,0.3)',
            transform: 'scale(0)',
            opacity: ripple.visible ? 1 : 0,
            transition: 'transform 300ms ease-out, opacity 250ms ease-out',
            transformOrigin: 'center'
          }}
          className={ripple.visible ? 'ripple-expand' : 'ripple-fade'}
        />
      ))}
      <style jsx>{`
        .ripple-expand {
          animation: expand 300ms ease-out forwards;
        }
        .ripple-fade {
          animation: fade 250ms ease-out forwards;
        }
        @keyframes expand {
          from { transform: scale(0); }
          to { transform: scale(1); }
        }
        @keyframes fade {
          from { opacity: 1; }
          to { opacity: 0; }
        }
      `}</style>
    </button>
  );
};

export default RippleButton;

关键说明:

  • 用任务队列cleanupQueue存储需要清理的涟漪ID,确保每个清理操作按顺序执行
  • isProcessing标记避免同时处理多个任务,防止队列混乱
  • 给每个涟漪分配唯一ID,避免清理时误删其他涟漪
  • 加入了onMouseLeave事件处理,确保鼠标离开按钮时也能触发涟漪清理

内容的提问来源于stack exchange,提问作者Gabriel Ribeiro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 19:20:53