前言本文的案例代码并非作者原创。本文主要讲解视差效果是如何实现的(原生三件套),本文并不涉及性能优化相关的知识点讲解(你就当我耍流氓吧)。本文会从原理讲起,然后结合多个案例由浅入深去实现最终效果。 本文案例如图所示 原理pc端的视差效果通常是根据鼠标、键盘、滚动条的变化和操作进行视觉上的差异化控制。移动端可能还会有重力陀螺仪之类的交互,本文不讲移动端。 举些例子:鼠标移到屏幕左上方:某元素就飞到屏幕右下方(跟鼠标反着来)。页面往下滑动:背景图不动,文本元素等其他元素往上移动。 实现 理解了实现的原理,那实现的关键就是事件监听addEventListener了。 简单例子 先来一个简单的例子玩玩 这个例子实现的效果是:鼠标往左移,元素就往右移;鼠标往上移,元素就往下移。 style。box{width:200px;height:300px;background:lightblue;position:absolute;绝对定位}style 当鼠标在页面左上方(加入x和y坐标分别是10和20),就设置元素在页面右下方(右:10,下:20)。 就是根据《原理》里讲的那样去实现。 如果不懂clientX和clientY,可以看看下面这篇文章: 《JS之clientX,clientY,screenX,screenY,offsetX,offsetY区别测试关耳佳的博客CSDN博客clientx》 注意: 本例使用了right和left移动元素。之所以这样做,是为了从最简单的方式讲解和实现。 实际开发中这会带来一定的布局问题和性能问题(会导致布局更改或重新绘制,并会导致动画不稳定。),推荐优先考虑使用transforms对元素进行移动等操作。 进阶版 好玩的交互除了移动元素外,还可以移动背景图位置、旋转元素等操作。 同时还需要考虑元素的动画幅度。像上面的例子就完全没控制元素移动幅度,所以当鼠标移动到屏幕最右侧或者最底部的时候,元素就会超出屏幕。这也许不是一种好的操作体验。 说到动画幅度,就要考虑参照物的事情。常见的参照物有浏览器宽高、容器宽高、容器位置等。 比如这个例子: 这个例子所操控的元素看上去很多,但其实逐层拆分,逐层控制起来就很简单。 要考虑的因素包括:容器旋转背景图轻微移动人物跟随鼠标移动 这里的参照物是鼠标位置与文档的宽高比例,并通过自己设置的公式来限制元素移动或旋转的范围。 1、容器旋转 创建一个p容器,设置了阴影。 stylehtml,body{width:100;height:100;margin:0;padding:0;}body{display:flex;justifycontent:center;alignitems:center;}容器。card{width:175px;height:250px;borderradius:8px;boxshadow:0px10px20px20pxrgba(0,0,0,0。17);}style 通过JS控制容器旋转 获取容器元素constcarddocument。querySelector(。card)计算函数functioncomputedTransform(num,doc){return(numdoc4020)。toFixed(1)}给文档添加一个鼠标移动的事件监听document。addEventListener(mousemove,e{旋转容器card。style。transformrotateX({computedTransform(e。clientX,window。innerWidth)}deg)rotateY({computedTransform(e。clientY,window。innerHeight)}deg)}) 2、移动背景图 添加背景图 !省略部分重复代码style。card{width:175px;height:250px;borderradius:8px;boxshadow:0px10px20px20pxrgba(0,0,0,0。17);backgroundimage:url(。img3drspirited。jpg);backgroundrepeat:norepeat;backgroundposition:5050;backgroundsize:110110;}style 这段css主要看最后添加的4行(background相关)。 直接在css里通过backgroundimage添加一个背景图,背景图不重复,起始位置在中心,背景图比容器稍微大一点点,但不会超出容器。 JS控制:背景图也跟随鼠标移动 录制的GIF有点小问题,最后出现了鼠标残影,先将就看着效果吧。 省略部分重复代码constcarddocument。querySelector(。card)计算functioncomputedBGPosition(num,doc){return(60Number((numdoc20)。toFixed(1)))}给文档添加鼠标移动的事件监听document。addEventListener(mousemove,e{移动背景card。style。backgroundPosition{computedBGPosition(e。clientX,window。innerWidth)}{computedBGPosition(e。clientY,window。innerHeight)}})复制代码 这部分的移动幅度我控制在一个比较小的范围内,使用backgroundposition来控制背景图起始位置。 再结合1、容器旋转的代码,就变成如下所示的效果: 3、移动图片(人物) 人物跟随鼠标移动 完整代码stylehtml,body{width:100;height:100;margin:0;padding:0;}body{display:flex;justifycontent:center;alignitems:center;}容器。card{width:175px;height:250px;overflow:hidden;backgroundimage:url(。img3drspirited。jpg);backgroundrepeat:norepeat;backgroundposition:5050;backgroundsize:110110;transformorigin:5050;perspective:1800px;transformstyle:preserve3d;borderradius:8px;boxshadow:0px10px20px20pxrgba(0,0,0,0。17);}图片样式(小千)。cardimg{height:100;position:relative;top:25px;left:25px;}styleimgsrca2020imgdataimg。jpgdatasrcimg03。bs178。comcb1j66d19d83c6cb1ab8。jpgalt 样式部分:容器:需要设置overflow:hidden;,图片在移动过程中超出的部分不展示人物图片:人物需要设置position:relative;,并且往下移动一点,这样可以隐藏下半身。 JS部分:constimgcard。querySelector(img)计算translatefunctioncomputedTransform(num,doc){return(numdoc4020)。toFixed(1)}img。style。transformtranslateX({computedTransform(e。clientX,window。innerWidth)}px)translateY({computedTransform(e。clientY,window。innerHeight)}px) 主要添加了这部分,通过鼠标当前位置和屏幕宽高来计算出图片移动的距离。 终极版 上面的进阶版讲解了实现视差效果的秘密。 平时见到更加复杂的效果,其实可以把元素逐一拆分,逐一控制。 比如本文的终极版效果: 这部分的讲解都放在代码注释里,建议自己建一个项目来运行。 有不懂的地方可以在评论区交流讨论。 完整代码如下所示:style。pagex{width:1000px;height:700px;居中布局display:flex;justifycontent:center;alignitems:center;overflow:hidden;设置元素被查看位置的视图perspective:1800px;背景色(兼容性写法)background:642b73;background:lineargradient(tobottom,c6426e,642b73);}Popularh1{底部外边距marginbottom:30px;z轴偏移transform:translateZ(35px);字母间距letterspacing:1px;字号fontsize:32px;字体粗细fontweight:800;字体颜色color:3e3e42;}Moviesh3{底部外边距marginbottom:6px;z轴偏移transform:translateZ(25px);字号fontsize:16px;字体颜色color:eb285d;}卡片主容器。cards{行内块元素display:inlineblock;最小宽度minwidth:595px;内边距padding:30px35px;设置元素被查看位置的视图perspective:1800px;旋转基点transformorigin:5050;使被转换的子元素保留其3D转换transformstyle:preserve3d;圆角borderradius:15px;文本左对齐textalign:left;背景色background:fff;投影boxshadow:0px10px20px20pxrgba(0,0,0,0。17);}卡片。card{行内块元素display:inlineblock;宽width:175px;高height:250px;相对定位position:relative;隐藏溢出部分overflow:hidden;设置元素被查看位置的视图perspective:1200px;使被转换的子元素保留其3D转换transformstyle:preserve3d;z轴偏移transform:translatez(35px);过渡transition:transform200mseaseout;文本居中对齐textalign:center;圆角borderradius:15px;投影boxshadow:5px5px20px5pxrgba(0,0,0,0。6);}除了最后一个卡片之外的卡片。card:not(:lastchild){右侧外边距marginright:30px;}卡片的图片。cardimg{相对定位position:relative;高度height:100;}卡片背景。cardbg{bottom:50px;left:50px;position:absolute;right:50px;top:50px;旋转基点transformorigin:5050;transform:translateZ(50px);zindex:0;}幽灵公主图片。princessmononoke。cardimg{top:14px;right:10px;height:110;}幽灵公主背景。princessmononoke。cardbg{background:url(img3drmonobg。jpg)centercovernorepeat;}千与千寻图片。spiritedaway。cardimg{top:25px;}千与千寻背景。spiritedaway。cardbg{background:url(img3drspirited。jpg)centercovernorepeat;}哈尔的移动城堡图片。howlsmovingcastle。cardimg{top:5px;left:4px;height:110;}哈尔的移动城堡背景。howlsmovingcastle。cardbg{background:url(img3drhowlbg。jpg)centercovernorepeat;}卡片的文本内容。cardtext{弹性布局display:flex;主轴为垂直方向flexdirection:column;主轴居中对齐justifycontent:center;交叉轴的中点对齐alignitems:center;宽width:100;高height:70px;绝对定位position:absolute;堆叠顺序zindex:2;离底部距离bottom:0;背景色:渐变background:lineargradient(tobottom,rgba(0,0,0,0)0,rgba(0,0,0,0。55)100);}卡片的标题。cardtitle{底部外边距marginbottom:3px;设置左右10px内边距padding:010px;字号fontsize:18px;字体的粗细fontweight:700;字体颜色color:fff;}styleh3Moviesh3h1Popularh1!幽灵公主imgclasscardimgsrca2020imgdataimg。jpgdatasrcimg03。bs178。comcb1j42b3f77505194f97。jpgpclasscardtitlePrincessMononoke!千与千寻imgclasscardimgsrca2020imgdataimg。jpgdatasrcimg03。bs178。comcb1j66d19d83c6cb1ab8。jpgpclasscardtitleSpiritedAway!哈尔的移动城堡imgclasscardimgsrca2020imgdataimg。jpgdatasrcimg03。bs178。comcb1jd4e5b804db5b3672。jpgpclasscardtitleHowlsMovingCastle 推荐 日常开发中很少直接用原生的方式去实现视差效果的。 这里推荐一个轻量JS动画库:anime。jsJavaScriptanimationengine 这个库的用法太简单了,直接看《anime。js官方文档》就知道怎么用了,本文不进行讲解。