弹窗动画停在半像素位置会让文章图片顶线消失

文章弹窗里的图片出现过一个渲染异常:图片顶部的 1px 线框会消失,看起来像图片内容向上压住了外部线框。鼠标框选图片后,线框又恢复正常。

直接改图片样式很难稳定复现和修复。异常状态和恢复后的 computed style 对比后,图片自身规则没有实质差异。真正触发风险的是弹窗动画把内容层长期留在半像素 transform 合成层里,1px 顶线在取整时被吃掉。

有效线索

异常状态下,文章图片继承的 CSS 变量里有这样的入场位移:

--modal-enter-x: -192.5px;
--modal-enter-y: -786.5px;

这两个 .5px 值来自弹窗入场动画。原结构把弹窗外层固定在视口中心:

.modal-outer {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

入场动画也直接作用在同一个外层元素上。视口尺寸、卡片点击位置和弹窗尺寸组合后,最终坐标容易落在半像素。浏览器在 transform 合成层中绘制图片、圆角和 1px 线框时,会出现取整差异。鼠标框选触发重绘,线框临时恢复,所以看起来像“选一下就好了”。

修复方式

修复点放在弹窗层级,避免继续堆叠图片规则。

  1. modal-outer 改成全屏 fixed 容器,用 flex 居中,不再使用 top: 50%translate(-50%, -50%)
  2. 新增 modal-shell 包住目录栏和文章主体,入场/离场动画只作用在这个内部壳层。
  3. 给 Vue <Transition> 显式声明 enter/leave 时长,避免外层没有 transition 后过渡类无法按预期清理。
  4. 动画结束态让 modal-shell 回到 transform: none,文章内容不再长期处在 transform 合成层里。
  5. 图片仍保留 display: block,线框改用 box-shadow: 0 0 0 1px black,降低 inline 布局和边框绘制带来的额外变量。

核心结构变成:

<div class="modal-outer">
  <div class="modal-shell">
    <aside class="chapter-panel"></aside>
    <div class="modal-wrapper"></div>
  </div>
</div>

关键样式变成:

.modal-outer {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.modal-shell {
  display: flex;
  transform: none;
  pointer-events: auto;
}

.modal-enter-from .modal-shell {
  transform: translate(
    var(--modal-enter-x, 0px),
    var(--modal-enter-y, 0px)
  ) scale(0.004);
}

.modal-enter-to .modal-shell,
.modal-leave-from .modal-shell {
  transform: none;
}

验证方式

这类问题不能只看 computed style。异常和正常状态的 computed style 可能完全一样,差异存在于合成层、动画类和实际像素坐标。

验证时检查三个点:

  1. 动画结束后 .modal-outertransformnone
  2. 动画结束后 .modal-shelltransformnone
  3. .article-body img 保持 display: block,线框规则为 box-shadow: 0 0 0 1px black

对应的回归测试可以直接检查源码约束,防止后续又把 transform 居中加回外层弹窗。

结论

图片线框丢失不一定是图片样式问题。弹窗、浮层、缩放动画、半像素居中会改变子元素的绘制上下文。遇到“鼠标框选后恢复”的 UI 异常,应优先检查是否存在长期保留的 transform、filter、backdrop-filter 或动画类。

这次修复的原则是:外层负责固定定位和居中,内层负责短暂动画,动画结束后内容层回到普通布局。这样能保留打开动画,同时减少图片、文字和 1px 线框的合成层取整风险。

本文由 AI 辅助生成,可能存在错误或遗漏,请以实际资料和官方文档为准。