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

解决日历事件顶部调整大小时底部抖动问题

解决日历事件顶部调整大小的抖动问题

问题根源

你遇到的抖动问题,核心原因是同时使用transform: translateY()修改位置和直接调整height,再加上父组件同时设置了top和bottom的绝对定位,导致浏览器在重排布局时出现计算冲突——浏览器既要根据top/bottom自动计算高度,又要处理你手动设置的height和transform,两种逻辑互相干扰,就出现了底部抖动。而底部调整时只修改height,没有触发位置计算冲突,所以正常。

修复方案

1. 简化父组件定位逻辑

父组件去掉bottom样式绑定,只通过top和height来控制事件的位置和大小,避免浏览器自动计算高度的逻辑干扰:

<user-calendar-event
  class="absolute px-1"
  [style.top]="event.top"
  <!-- 移除[style.bottom]绑定 -->
  [style.height]="event.height" <!-- 新增height绑定,替代bottom逻辑 -->
  [style.opacity]="eventOpacityChange(event)"
  [style.z-index]="hoveredEvent === event.id ? 10 : 1"
  [event]="event"
  [color]="event ? hoverColorChange(event.id, event.calendar_id) : ''"
  [weeklyEvent]="true"
  (newHeightAndTop)="onEventResizeTop($event, event)" <!-- 新增传递top+height的事件 -->
  (newHeight)="onEventResizeBottom($event, event)"
  (resizingEvent)="isResizing = $event"
  (click)="onClickedEvent($event)"
  (mouseover)="hoveredEventId(event.id)"
  (mouseleave)="onMouseLeave()"
>
</user-calendar-event>

2. 重写顶部调整逻辑,移除transform

不再用transform偏移位置,而是直接修改父组件的top值和当前组件的height,让布局计算完全基于标准定位属性,避免冲突:

startResizing(event: MouseEvent, position: string) {
  this.resizingEvent.emit(true);

  const startY = event.clientY;
  const startHeight = this.container.nativeElement.offsetHeight;
  const viewportHeight = window.innerHeight;
  const heightPercentage = 100 / viewportHeight;
  const pixelsPerTimeSlot = 3 / heightPercentage;
  // 顶部调整时记录父元素初始top值
  const parentElement = this.container.nativeElement.parentElement;
  const startTop = position === 'top' ? parseInt(parentElement.style.top, 10) : 0;

  const mouseMove = (moveEvent: MouseEvent) => {
    const currentY = moveEvent.clientY;
    const diffY = currentY - startY;
    const timeSlotsMoved = Math.abs(diffY) / pixelsPerTimeSlot;
    const slotPixels = Math.round(timeSlotsMoved) * pixelsPerTimeSlot;
    let newHeight = startHeight;
    let newTop = startTop;

    if (position === 'top') {
      // 向上拖动:top减小,height增加;向下拖动:top增加,height减小
      newTop = diffY > 0 ? startTop + slotPixels : startTop - slotPixels;
      newHeight = diffY > 0 ? startHeight - slotPixels : startHeight + slotPixels;
      // 更新父元素top,限制最小top为0
      parentElement.style.top = `${Math.max(newTop, 0)}px`;
    } else {
      // 底部调整逻辑保持不变
      newHeight = diffY > 0 ? startHeight + slotPixels : startHeight - slotPixels;
    }

    // 限制事件最小高度
    const finalHeight = Math.max(newHeight, 20);
    this.container.nativeElement.style.height = `${finalHeight}px`;
  };

  const mouseUp = () => {
    if (position === 'top') {
      const parentElement = this.container.nativeElement.parentElement;
      const finalTop = parseInt(parentElement.style.top, 10);
      const finalHeight = this.container.nativeElement.offsetHeight;
      // 向父组件传递top和height的百分比值
      this.newHeightAndTop.emit({
        topPercent: finalTop * heightPercentage,
        heightPercent: finalHeight * heightPercentage
      });
    } else {
      const finalHeight = this.container.nativeElement.offsetHeight;
      const heightInPercent = finalHeight * heightPercentage;
      this.newHeight.emit(heightInPercent);
    }

    window.removeEventListener('mousemove', mouseMove);
    window.removeEventListener('mouseup', mouseUp);
    this.resizingEvent.emit(false);
  };

  window.addEventListener('mousemove', mouseMove);
  window.addEventListener('mouseup', mouseUp);
}

3. 更新父组件事件处理

新增onEventResizeTop方法,同步更新事件的top和height值:

onEventResizeTop(data: {topPercent: number, heightPercent: number}, event: any) {
  event.top = `${data.topPercent}%`;
  event.height = `${data.heightPercent}%`;
  // 后续保存/刷新逻辑...
}

onEventResizeBottom(heightPercent: number, event: any) {
  event.height = `${heightPercent}%`;
  // 后续保存/刷新逻辑...
}

方案有效性说明

  • 去掉父组件bottom绑定后,浏览器不再自动计算事件高度,完全由我们通过top和height控制,避免了布局计算冲突。
  • 移除transform后,所有位置和大小修改都基于标准绝对定位属性,浏览器重排逻辑更稳定,不会出现抖动。
  • 顶部调整时同步修改top和height,逻辑和底部调整保持一致,保证了布局计算的统一性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 18:33:21