当你浏览小红书时,会发现其商品详情页的图片切换效果非常独特:前一张图片恰好露出右侧边缘,当前图片稳稳居中,后一张又露出左侧边缘,同时伴随着缩放和透明度的自然衰减。许多开发者会问:这个效果直接使用 swiper 组件能实现吗?坦白说,效果并不理想。

直接使用 swiper 虽能露出边缘,但无法实现层叠效果
swiper 的 previous-margin 和 next-margin 属性确实可以让两侧卡片露出来,但这种露出是静态的,无法动态控制每张图片的 z-index、scale 和 opacity,更无法响应用户手指拖拽过程中的实时偏移。在真机运行时,经常出现滑动卡顿、层级混乱的问题——并非配置有误,而是该组件的底层能力本身就有限。
必须通过手写 touch + transform 来控制五张卡片
解决方案其实很清晰:只渲染当前卡片及其前后各两张,总计五张卡片,全部使用 position: absolute 定位在同一个容器中,依靠 z-index 和 transform 来模拟层叠效果。关键要点如下:
- 每张卡片绑定独立的
style对象,动态计算transform: translateX() scale() rotateZ()和z-index的值。 translateX偏移量 =deltaX * weight * 0.3(其中weight值:当前卡片为 0,前一张为 -1,后一张为 +1,再前一张为 -2,再后一张为 +2)。scale=1 - Math.abs(weight) * 0.1(最小值为0.7),opacity=1 - Math.abs(weight) * 0.2(最小值为0.4)。- 务必使用
uni.upx2px()统一进行单位转换,避免 rpx 与 px 混用,否则偏移量计算会出错。
touchend 后需根据滑动距离决定是否切换
手指抬起后直接切换?这不够合理,需要判断用户拖动了多远。具体逻辑如下:
- 在
touchstart时记录clientX,每次touchmove计算deltaX = currentX - startX。 - 当
touchend触发时,如果Math.abs(deltaX) > 80(单位 rpx),才执行卡片切换;否则将卡片复位到原始位置。 - 切换后重置所有卡片的
z-index和transform,防止累积误差影响后续操作。 - 特别提醒:iOS 真机对
transform和opacity动画非常敏感,切勿使用left/top属性来触发重排,否则帧率会直接崩溃。
图片加载与内存优化不容忽视
商品图片通常体积大、数量多,如果直接使用 v-for 渲染所有图片,很容易导致卡顿甚至白屏。优化思路如下:
- 只对索引在
[currentIndex-2, currentIndex+2]范围内的图片进行渲染,其余图片使用占位符或不渲染。 - 设置
mode="aspectFill"防止图片拉伸变形,同时结合lazy-load属性实现非可视区图片的延迟加载。 - 在小程序端尽量避免使用
background-image加载商品图——这种做法容易触发 canvas 渲染瓶颈,建议优先使用标签。
