jquery向左滑出,animate改left更稳
- 2026年11月9日
- 14 次
同一段几行的代码,复制到另一个页面就彻底不动:元素留在原地,控制台一行红字都没有,DevTools 里 left 的计算值写着 auto。两个页面用的jquery向左滑出代码一模一样,DOM 结构也一样,唯一的差别是被滑的那个盒子上少了一句 position: relative。卡住人的通常不是动画本身,而是位移落在哪个属性、left 又相对谁计算。
animate 改 left 的写法
结论放前面:横向滑出把位移放在 left 上,元素写 position: relative,父级写 overflow: hidden,动画里只动这一个值。

jQuery 的 animate 处理 left 的方式是把它当像素值,逐帧写进元素的内联 style,起始值从 computed style 读,读到 auto 就按 0 算。
$('.drawer').parent().css('overflow', 'hidden');
$('.drawer')
.css('position', 'relative')
.stop(true, true)
.animate({ left: -320 }, 300);
stop(true, true) 放在 animate 前面,含义是清空没跑完的动画并直接跳到当前这一段的终点,参数依次是 queue 和 jumpToEnd。不写也能跑,只有连点的时候看得出差别。
这套写法要挪 320px,left 就从 0 走到 -320,中间每一帧都是一个确定的数字,动画被打断也能读出当前位置。
选 left 不选 margin-left,理由只有一条:margin-left 参与文档流,负值会把同一行里的行内块一起带偏,块级兄弟不动,可整块内容跟着重排;left 只挪定位元素自己的位置,兄弟节点不动。代价有两个,left 只对定位元素生效,static 的盒子上写多少都没反应;元素自身仍在重排,所以动画期间别同时改 width 或 padding,两者叠在一起会跳。
三种做法摆开比
left 位移是最常见的做法,横向滑出大多落在它上面。前提是元素已经定位,relative 或 absolute 都行,父级能裁掉溢出。代价是 relative 时元素原位继续占空间,absolute 时要自己给父级留占位高度。适合固定距离的抽屉、侧栏、整块面板横移。
margin-left 位移。前提宽松,块级盒子就行,不用管定位。代价是它动的是盒模型外边距,每帧都牵动后面的布局,滑出过程中兄弟节点在变位置;元素自己还带 margin 的时候,实际位移是两者相加,距离容易算错。适合后面没有内容、或者本来就要把整行挤走的场景。
width 收到 0。前提是父级裁剪,再加 white-space: nowrap,不然文字会先换行挤成竖排再消失。代价是内容重排最重,长文本尤其明显。适合收起后要给别的元素让位的折叠区。

三种写法都只是改一个属性,差别全在前提和副作用上。jquery向左滑出选错属性,代码照样能跑,页面会抖。
position 没设,动画等于没写
代码跑了但元素没动,看两个地方就能定性:Elements 面板里元素的内联 style 有没有逐帧变化的 left,Computed 里 position 是不是 relative。前者有、后者是 static,就是定位没生效。
依据在 computed 值上,static 元素的 left 算出来是 auto,animate 读到 auto 会拿 0 当起点,从 0 走到 -320,值确实写进去了,渲染时被忽略。整个过程控制台不报错,看着像动画没触发,其实是属性不生效。所以动画写了却不生效,先查定位,别怀疑jquery向左滑出的代码本身。
改起来只有一行,给被滑元素加 position: relative;或者让它 absolute,由父级撑开尺寸。relative 成本低,元素原位继续占空间,滑出去之后那块留白交给父级 overflow: hidden 裁掉。
裁剪边界要和滑出距离对齐。抽屉宽 320px、left 走到 -320,父级窄了会先裁掉一半,看起来是滑到中途消失;父级宽了,元素滑出去还挂在可视区里,等于没滑。
连点两次,队列会叠加
animate 默认进的是 fx 队列,连点两次不是覆盖,是排队。
用相对值 '-=320' 的时候,第二次点击排在第一次后面,元素一路滑到 -640;用绝对值 -320 时,第二次点击从当前帧接着补到 -320,动画照样跑满 300ms,看上去是原地停一下再往前挪,不会有回弹。两种都不是想要的结果。
$('.drawer').on('click', function () {
var $d = $('.drawer');
if ($d.is(':animated')) return;
$d.animate({ left: -320 }, 300);
});
判断 :animated 比每次 stop 更稳。stop(true, true) 会把没跑完的动画直接跳到终点,快速连点时元素是「啪」一下到位,中间没有滑动过程;:animated 是干脆不接受第二次点击,第一次那 300ms 完整跑完。

要在连点下也保持顺滑,就把 stop 换成 stop(true, false),停在当前帧,再按当前位置算剩下的距离重新起动画。多两行代码,换中途改向不跳。
队列还有个副作用,动画没跑完时元素的 left 是中间值。这时候读它做判断,比如是否已经完全滑出,拿到的都是那一帧的数字,不是终点。
判到位用回调,或者直接比对目标值。拿定时器去猜 300ms 最不靠谱,机器慢一点就对不上。
slideUp 收起的方向和它不一样
slideUp 收的是高度,从下往上塌,方向固定在垂直轴上。核心库的 hide、show、toggle 也没有 direction 参数,写 .hide('slide', {direction: 'left'}) 想横向收,只引核心库时这个对象会被丢掉,元素按默认的上下收起。
要横着收,一条路是加 jQuery UI 的效果模块,代价是多一个依赖包;另一条路还是 left,配合 width 或 opacity 一起走,位移和淡出同步,视觉上就是横向收进去。前者代码短,后者不多加依赖。
选哪个看收起之后要什么。收起后位置要让给别的元素,用 slideUp,高度动画天然带着布局回收;收起只是视觉上移出可视区、原位保留,用 left。
两个混着用会打架。left 还没到位,高度已经开始塌,元素一边左移一边变矮,裁切边缘对不上,看着是斜着消失的。
回头看那次复制
开头那个页面不动,错不在漏了一行 position,而在于我把动画代码当成一段独立的片段复制过去。第一个页面里那条 position: relative 写在另一个 class 上,第二个页面的盒子没有这个 class,代码一样、前提不一样,结果就差在这里。
重来会先定容器:position: relative 加 overflow: hidden,位移统一走 left,滑出距离写成变量而不是散在几处。把jquery向左滑出的代码复制到新页面能直接跑,前提是容器那两句一起带过去。代价是每个滑出面板都要多一层定位父级,absolute 的时候还要手写占位高度;换来的是动画过程中兄弟节点不动,left 的值和实际位置一一对应,调试时看一眼数字就知道跑到哪一帧。
若非作者标注皆为李涛自留地原创文章,转载或复制请以超链接形式并注明出处。
- ZblogPHP纯代码实现文章索引目录锚点功能 2022-10-26
- ZblogPHP目录索引插件不生效问题及解决办法 2022-10-20
- 前端中jQuery学习的必要性 2021-03-01
- jQuery中如何给元素添加多个css样式 2021-03-15
- jquery选择器里拼接变量怎么写才不出错,我遇到两个坑 2026-11-08
- jquery tab选项卡,我用一个索引变量替掉三个 if 2026-11-10
