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

如何在Vaadin Flow中实现滑动手势并自定义行为

在Vaadin Flow中实现移动端滑动手势效果

要在Vaadin Flow中实现类似移动端的左滑显示操作按钮效果,核心是自己实现滑动手势的检测逻辑(浏览器无原生swipe事件),再配合Vaadin的事件绑定和UI状态控制完成。以下是针对需求的完整优化方案:

一、核心问题修正

你当前代码中使用的swipe事件并非浏览器原生事件,因此Vaadin无法捕获到。需要通过监听触摸/指针事件,手动计算滑动的方向和距离,再触发自定义的swipe事件供Java端处理。

二、完整实现代码

1. Java端组件代码

优化UI结构并正确绑定自定义事件:

import com.vaadin.flow.component.Unit;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.dom.Element;

@Route("swipe")
public class SwipeDiv extends VerticalLayout {

    public SwipeDiv() {
        // Header
        Div header = new Div();
        header.setText("HEADER: 左滑触发操作");
        add(header);

        // 可滑动的内容项
        Div content1 = createSwipeItem("左滑我显示删除按钮");
        add(content1);

        Div content2 = createSwipeItem("第二个可滑动项");
        add(content2);

        setWidthFull();
        setPadding(true);
        setSpacing(true);
    }

    private Div createSwipeItem(String text) {
        // 外层容器,作为滑动交互的载体
        Div swipeContainer = new Div();
        swipeContainer.addClassName("swipe-container");
        swipeContainer.setHeight(50, Unit.PIXELS);

        // 左侧文本区域
        Div textWrapper = new Div();
        textWrapper.addClassName("swipe-text");
        textWrapper.setText(text);

        // 右侧操作按钮区域
        Div actionWrapper = new Div();
        actionWrapper.addClassName("swipe-actions");

        Icon trashIcon = VaadinIcon.TRASH.create();
        trashIcon.addClickListener(e -> {
            // 这里添加删除逻辑
            swipeContainer.removeFromParent();
        });
        actionWrapper.add(trashIcon);

        swipeContainer.add(textWrapper, actionWrapper);

        // 注入滑动检测的JS逻辑
        addSwipeDetectionLogic(swipeContainer.getElement());

        // 监听自定义的swipe事件
        swipeContainer.getElement().addEventListener("swipe", event -> {
            String direction = event.getEventData().getString("event.detail.direction");
            if ("left".equals(direction)) {
                swipeContainer.addClassName("swiped-left");
            } else if ("right".equals(direction)) {
                swipeContainer.removeClassName("swiped-left");
            }
        }).addEventData("event.detail.direction");

        return swipeContainer;
    }

    private void addSwipeDetectionLogic(Element element) {
        // 注入JS代码,监听触摸事件并检测滑动
        element.executeJs("""
            let startX;
            const threshold = 50; // 触发滑动的最小距离
            const element = this;
            
            element.addEventListener('touchstart', (e) => {
                startX = e.touches[0].clientX;
            });
            
            element.addEventListener('touchend', (e) => {
                const endX = e.changedTouches[0].clientX;
                const diffX = startX - endX;
                
                if (Math.abs(diffX) > threshold) {
                    const direction = diffX > 0 ? 'left' : 'right';
                    // 触发自定义swipe事件
                    element.dispatchEvent(new CustomEvent('swipe', { detail: { direction } }));
                }
            });
            
            // 兼容桌面端的鼠标滑动(可选)
            element.addEventListener('mousedown', (e) => {
                startX = e.clientX;
            });
            
            element.addEventListener('mouseup', (e) => {
                const endX = e.clientX;
                const diffX = startX - endX;
                
                if (Math.abs(diffX) > threshold) {
                    const direction = diffX > 0 ? 'left' : 'right';
                    element.dispatchEvent(new CustomEvent('swipe', { detail: { direction } }));
                }
            });
        """);
    }
}

2. CSS样式(添加到项目styles.css中)

通过CSS实现平滑过渡和状态切换,让滑动效果更自然:

.swipe-container {
    display: flex;
    align-items: center;
    background: #fff;
    border: 1px solid #e0e0e0;
    border-radius: 4px;
    overflow: hidden;
    position: relative;
    transition: all 0.3s ease;
}

.swipe-text {
    flex: 1;
    padding: 0 16px;
    transition: opacity 0.3s ease;
}

.swipe-actions {
    width: 60px;
    height: 100%;
    background: #ff4444;
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
    transform: translateX(100%);
    transition: transform 0.3s ease;
    position: absolute;
    right: 0;
    top: 0;
}

.swipe-container.swiped-left .swipe-actions {
    transform: translateX(0);
}

.swipe-container.swiped-left .swipe-text {
    opacity: 0.7;
    padding-right: 80px; /* 给操作区域让出空间 */
}

三、关键实现说明

  1. 滑动检测逻辑:通过executeJs向组件注入JavaScript,监听touchstart/touchend(移动端)和mousedown/mouseup(桌面端兼容)事件,计算滑动距离,当超过阈值时触发自定义swipe事件。
  2. UI状态控制:Java端监听自定义swipe事件,根据方向添加/移除CSS类,通过CSS实现文本区域和操作区域的平滑过渡,避免生硬的视觉变化。
  3. 操作交互:给图标添加点击事件,实现删除等业务逻辑,符合移动端操作习惯。

四、额外优化建议

  • 调整threshold值(当前50px)可控制触发滑动的灵敏度。
  • 若需要滑动过程中的实时跟随效果,可在touchmove事件中动态修改元素位置,而非仅在touchend时切换状态。
  • 对于PWA项目,可添加逻辑判断滑动方向是否为水平,避免干扰页面垂直滚动。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 09:36:04