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

Wavesurfer.js多区域拖拽/缩放防重叠问题求助

Wavesurfer.js 字幕区域防重叠拖拽/缩放实现问题修复

问题背景

使用Wavesurfer.js开发视频编辑器,为每条字幕创建可拖拽/缩放的区域,需要实现拖拽或缩放区域时自动防止区域重叠,但现有处理函数未生效。

现有代码

创建区域的代码

let waveSurfer = useRef(null);
let wsRegions = useRef(null);

const createRegion = () => {
    if (wsRegions.current) {
      wsRegions.current.clearRegions();
      segments.forEach((segment, index) => {
        const region = {
          start: segment.startTime,
          end: segment.endTime,
          data: segment,
          id: index.toString(),
          content: segment.styledText ? segment.styledText : segment[textType]
        }
        wsRegions.current.addRegion(region);
      });
    }
  }

未生效的重叠处理函数

const handleRegionDrag = async () => {
    if (!wsRegions.current) {
      return;
    }

    const regions = await wsRegions.current.regions;
    console.log(regions)

    if (regions.length === 0) {
      return;
    }
    regions.sort((a, b) => a.start - b.start);

    for (let i = 0; i < regions.length; i++) {
      const currentRegion = regions[i];
      const previousRegion = regions[i - 1];
      const nextRegion = regions[i + 1];

      currentRegion.on("click", () => {
        console.log("Clicked on region with ID:", currentRegion.id);
        console.log(previousRegion, nextRegion)
      });
      if (nextRegion && currentRegion.end > nextRegion.start) {
        currentRegion.end = nextRegion.start;
      }

      if (previousRegion && currentRegion.start < previousRegion.end) {
        currentRegion.start = previousRegion.end;
      }
    }
  };

问题分析

  1. 事件绑定缺失:未将防重叠逻辑绑定到区域的drag、resize事件上,函数仅会在主动调用时执行,而非拖拽/缩放过程中实时触发。
  2. 区域更新未触发:直接修改currentRegion.start或end后,未调用Wavesurfer区域的update()方法,无法同步更新界面显示。
  3. 逻辑执行时机错误:循环中绑定click事件会导致重复绑定,且排序后的边界修正逻辑未在每次拖拽/缩放后重新计算。

修复方案

步骤1:在创建区域时绑定事件

修改createRegion函数,为每个区域添加drag和resize事件监听,触发防重叠检查:

const createRegion = () => {
    if (wsRegions.current) {
      wsRegions.current.clearRegions();
      segments.forEach((segment, index) => {
        const region = {
          start: segment.startTime,
          end: segment.endTime,
          data: segment,
          id: index.toString(),
          content: segment.styledText ? segment.styledText : segment[textType]
        }
        const newRegion = wsRegions.current.addRegion(region);
        // 绑定拖拽和缩放事件
        newRegion.on('drag', handleRegionBoundaryCheck);
        newRegion.on('resize', handleRegionBoundaryCheck);
      });
    }
  }

步骤2:实现实时防重叠检查函数

重写防重叠逻辑,确保每次拖拽/缩放时实时修正区域边界,并触发界面更新:

const handleRegionBoundaryCheck = () => {
  if (!wsRegions.current) return;

  // 获取所有区域并按开始时间排序
  const regions = Array.from(wsRegions.current.regions).sort((a, b) => a.start - b.start);

  regions.forEach((currentRegion, index) => {
    const prevRegion = regions[index - 1];
    const nextRegion = regions[index + 1];
    let updated = false;

    // 处理与前一个区域的重叠
    if (prevRegion && currentRegion.start < prevRegion.end) {
      currentRegion.start = prevRegion.end;
      updated = true;
    }

    // 处理与后一个区域的重叠(确保结束时间不小于开始时间)
    if (nextRegion && currentRegion.end > nextRegion.start) {
      currentRegion.end = nextRegion.start;
      // 防止区域被压缩到0时长
      if (currentRegion.end < currentRegion.start) {
        currentRegion.end = currentRegion.start + 0.1;
      }
      updated = true;
    }

    // 如果有修改,更新区域
    if (updated) {
      currentRegion.update({
        start: currentRegion.start,
        end: currentRegion.end
      });
    }
  });
};

额外优化:精准控制拖拽方向

如果需要更精准的控制(比如拖拽区域开始时不覆盖前一个区域,拖拽结束时不覆盖后一个区域),可以在事件回调中获取拖拽的方向参数:

const handleRegionBoundaryCheck = (e, region, update) => {
  // update参数包含拖拽的是start还是end
  if (!wsRegions.current) return;

  const regions = Array.from(wsRegions.current.regions).sort((a, b) => a.start - b.start);
  const index = regions.findIndex(r => r.id === region.id);
  const prevRegion = regions[index - 1];
  const nextRegion = regions[index + 1];

  if (update.start && prevRegion && region.start < prevRegion.end) {
    region.update({ start: prevRegion.end });
  }

  if (update.end && nextRegion && region.end > nextRegion.start) {
    region.update({ end: nextRegion.start });
    // 保证区域最小时长
    if (region.end <= region.start) {
      region.update({ end: region.start + 0.1 });
    }
  }
};

说明

  • 每次拖拽或缩放区域时,会实时对所有区域按时间排序,然后逐个检查当前区域与前后区域的边界,修正重叠部分。
  • 使用region.update()方法确保界面同步更新,这是Wavesurfer区域修改后必须调用的方法。
  • 添加了最小时长保护,避免区域被压缩到无法识别的状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 04:55:56