You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在悬停行号显示面板时同步两个容器的滚动

如何在悬停行号显示面板时同步两个容器的滚动

首先,问题的核心矛盾在于:当我们给#line-numbers设置overflow: visible时,虽然能让悬停面板完整显示,但该容器不再支持通过scrollTop进行滚动同步(因为overflow: visible下容器本身不会产生滚动行为,scrollTop的设置无法影响内容位置);而overflow-y: hidden虽然能保证滚动同步,但会裁剪掉超出容器范围的面板内容。

解决思路是将悬停面板从行号容器中移出,改为通过绝对定位动态显示在父容器内,这样既保留行号容器的overflow-y: hidden以维持滚动同步,又能让面板完整显示。下面是具体实现步骤:

步骤1:修改Svelte组件模板

移除行号内部的panel元素,添加一个全局的面板到text-area-container中:

<div id="text-area-container">
  <!-- 全局悬停面板 -->
  {#if hoveredLineData}
    <div class="panel" style="top: {hoveredLineData.top}px; left: {hoveredLineData.left}px;">
      <div>Min: {hoveredLineData.exeTime?.minExeTime || 0} ms</div>
      <div>Avg: {hoveredLineData.exeTime?.avgExeTime || 0} ms</div>
      <div>Max: {hoveredLineData.exeTime?.maxExeTime || 0} ms</div>
      <div class="panel-icons">
        <button on:click={handleToggleDebug}></button>
        <button on:click={handleShowLogs}></button>
      </div>
    </div>
  {/if}

  <div id="line-numbers" bind:this={lineNumbersContainer} on:scroll={preventLineNumberScroll}>
    {#each lineNumbers as line, index}
      {#if line !== null}
        <div 
          class:highlighted={highlightedLines.includes(String(line.lineNumber))} 
          class={line.className}
          on:mouseenter={(e) => showPanel(e, line)}
          on:mouseleave={() => hidePanel()}
        >
          {line.lineNumber}
        </div>
      {:else}
        <div>&nbsp;</div>
      {/if}
    {/each}
  </div>

  <!-- 其他元素保持不变 -->
  <div id="highlighted-text" bind:this={highlightedTextContainer}></div>
  <textarea
    id="text-area"
    bind:this={textarea}
    bind:value={text}
    {placeholder}
    {disabled}
    on:keyup={handleKeyUp}
    on:keydown={handleKeyDown}
    on:mouseup={handleClick}
    on:scroll={syncScroll}
  />
</div>

步骤2:添加面板控制逻辑

在script部分新增变量和函数,用于控制面板的显示、隐藏和位置计算:

// 新增变量:存储悬停行的数据和位置
let hoveredLineData = null;

// 显示面板:计算行号元素位置并填充数据
function showPanel(event, line) {
  const lineElement = event.target;
  const containerRect = lineNumbersContainer.getBoundingClientRect();
  const lineRect = lineElement.getBoundingClientRect();
  
  hoveredLineData = {
    ...line,
    top: lineRect.top - containerRect.top, // 相对于行号容器的顶部偏移
    left: containerRect.width // 面板显示在行号容器的右侧
  };
}

// 隐藏面板
function hidePanel() {
  hoveredLineData = null;
}

步骤3:调整CSS样式

恢复行号容器的overflow-y: hidden设置,并更新全局面板的样式:

/* 恢复行号容器的overflow设置,保证滚动同步 */
#line-numbers {
  position: relative;
  width: 40px;
  padding: 10px 5px;
  text-align: right;
  border-right: 1px solid #ccc;
  background-color: #f0f0f0;
  user-select: none;
  overflow-y: hidden; /* 关键:维持滚动同步能力 */
  height: 100%;
  z-index: 3;
}

/* 全局面板样式 */
.panel {
  position: absolute;
  background-color: white;
  border: 1px solid #ccc;
  padding: 10px;
  border-radius: 4px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  z-index: 10; /* 确保面板在最上层不被遮挡 */
  white-space: nowrap; /* 避免内容换行导致面板变形 */
}

方案生效原因

  • 行号容器保持overflow-y: hidden,因此我们仍然可以通过设置scrollTop来同步滚动位置,解决了滚动不同步的问题。
  • 悬停面板不再是行号容器的子元素,而是通过绝对定位直接放在父容器中,不会被行号容器的overflow属性裁剪,同时可以根据行号的位置动态调整自身位置。
  • 面板的z-index设置为10,确保它显示在所有其他元素之上,不会被遮挡。

备注:内容来源于stack exchange,提问作者Amit Kumar Gupta

火山引擎 最新活动