van-dialog 弹窗里 textarea 文字诡异跳行?罪魁是 scale 动画而非样式
在某个 C 端微信小程序里,我们有一处弹窗让用户填写备注——弹窗内嵌了一个 textarea,placeholder 是一段两行的提示文字。上线后用户陆续反馈:"弹窗一打开,文字闪了一下,然后才正常"。复现很稳定:弹窗弹出的瞬间,placeholder 先显示两行,紧接着 100ms 内自动折叠成一行,像是被"压缩"了一下。
起初我们把嫌疑人锁定在 placeholder-style、auto-height、字体加载……连续改了五轮,每次都以为找到了,结果症状依旧。
现象
van-dialog打开瞬间,内嵌textarea的 placeholder 按"多行"排版- 约 50~300 ms 后,文字自动重排为"更少行"(两行变一行、换行位置跳变)
- 仅在弹窗打开动画播放期间发生,动画结束后文字稳定
- 调整
placeholder-style、去掉auto-height、改字号 —— 均无效
根因
vant-weapp 的 van-dialog 有一个默认 prop:
// miniprogram_npm/@vant/weapp/dialog/index.js:64
transition: { type: String, value: 'scale' }这个值被原样透传给底层 van-popup:
<!-- miniprogram_npm/@vant/weapp/dialog/index.wxml -->
<van-popup transition="{{ transition }}" ... />scale 过渡的 CSS 是 transform: scale(0.7) → transform: scale(1)。问题就出在这里。
textarea、input、picker、map、canvas、video 都是微信的原生组件(native component),由客户端在独立的渲染层绘制,不完全跟随父容器的 CSS transform。
具体时序如下:
- 弹窗打开,父容器处于
scale(0.7)状态 - 原生组件
textarea按此时父容器的视觉宽度(缩小后的)计算内部文字换行位置 → 行数偏多 - scale 动画完成,父容器回到
scale(1),原生组件拿到真实宽度,重新换行 → 跳字
只要弹窗用的是任何 transform 类动画(scale、rotate、translate3d),内嵌的原生组件就会按动画中间状态的"错误宽度"计算文字布局,动画结束后触发重排,视觉上就是跳字。
这和 placeholder-style、auto-height、字体加载毫无关系,单改它们永远无效。
正解
把 van-dialog 的 transition 改为 "fade"。fade 只改变 opacity,没有 transform,原生组件从第一帧就能拿到最终宽度,文字不会发生重排。
<van-dialog
use-slot
show="{{ showDialog }}"
transition="fade"
title="填写备注"
confirm-button-text="确认"
bind:confirm="onConfirm"
bind:cancel="onCancel"
>
<textarea
value="{{ remark }}"
placeholder="请输入备注内容,最多 100 字"
placeholder-style="font-size: 28rpx; line-height: 1.6; color: #ccc;"
style="height: 180rpx;"
bindinput="onInput"
/>
</van-dialog>另外两个配套修复,消除其他次要跳字因素:
1. placeholder-style 与 .textarea wxss 精确对齐
/* page.wxss */
.textarea { font-size: 28rpx; line-height: 1.6; }placeholder-style="font-size: 28rpx; line-height: 1.6; color: #ccc;"两者 font-size / line-height 不一致时,placeholder 与实际输入文字的行高不同,聚焦瞬间仍会有微小跳动。
2. 移除 auto-height,改为固定 height
<!-- 不要 -->
<textarea auto-height ... />
<!-- 改为 -->
<textarea style="height: 180rpx;" ... />auto-height 依赖首帧内容决定高度,原生组件首帧宽度不稳时高度同样不稳。
opacity-only 动画不产生 transform,原生组件首帧宽度准确,文字不重排。所有"transform 类弹窗动画 + native 组件内容"场景通用此方案。
反模式警示
- 禁止用
wx:if+setTimeout延迟挂载textarea"跳过"动画期。根因未消除,只是把重排移到用户看不到的时刻;代价是弹窗打开与textarea出现时机错开,键盘弹起逻辑也会混乱。 - 禁止给
textarea套transform: translateZ(0)等硬件加速 hack。原生组件不受 WebView CSS 影响,毫无效果。
同类场景一并适用:van-popup、van-action-sheet、自定义弹窗只要用了 scale/rotate/translate3d 动画,内含 textarea/input/picker/map 均有此坑。
一句话外卖
弹窗内原生组件跳字,先查弹窗的
transition是否含transform,改成fade(opacity-only)是唯一根治;调文字样式五轮不如读一行 vendor 源码。