使用CSS3的景深与三维变换+JavaScript实现一个可旋转的立方体(x,y两轴旋转)
HTML部分:
首先先构建立方体的DOM结构:
将6个面使用cube包裹,目的很简单,为了给子元素开启3d景深,
使用wrap再次包裹,wrap的目的是为了包装整个立方体,可控制缩放
1 2 3 4 5 6 7 8 9 10 <div class ="wrap" > <div class ="cube" > <div class ="cube-face" > </div > <div class ="cube-face" > </div > <div class ="cube-face" > </div > <div class ="cube-face" > </div > <div class ="cube-face" > </div > <div class ="cube-face" > </div > </div > </div >
其对应样式如下:(使用less书写)
html与body的样式,使用 overflow: hidden; 隐藏垂直滚动条,防止移动端的滑动
1 2 3 4 5 6 7 8 9 html ,body { margin : 0 ; padding : 0 ; height : 100% ; overflow : hidden; background-color : #dfe6e9 ; }
.wrap:将其设置为100 x 100 px的初始大小,并水平垂直居中,默认将其放大两倍
1 2 3 4 5 6 7 8 .wrap { width : 100px ; height : 100px ; position : absolute; top : 50% ; left : 50% ; transform : translate(-50% , -50% ) scale(2 ); }
.cube : 将其设置为100 x 100 px 的初始大小,并使用另一种水平垂直居中的解决办法,且开启内部元素的3D变换
1 2 3 4 5 6 7 8 9 10 11 12 13 .cube { width : 100px ; height : 100px ; position : absolute; top : 0 ; bottom : 0 ; left : 0 ; right : 0 ; margin : auto; transform-style : preserve-3 d; transform-origin : center center; transform : rotateX(-30deg ) rotateY(30deg ); }
在编写6个面之前,我们需要了解浏览器页面的3D坐标系
从+Z 到 -Z 方向,为用户看向浏览器屏幕的方向,意味着如果不开启3D变换,那么一切元素都是按照Z轴进行选择
从+Y 到 -Y 方向,为屏幕从下到上的方向,意味着元素会按照Y轴左右旋转
从+X 到 -X 方向,为屏幕从左到右的方向,意味着元素可以按照X轴上下翻转
了解了坐标系,那么可以编写css代码了
首先我们需要将6个面的公共样式提取出,然后分别为每个面定制选择的角度,以及选择的原点,在这里所有的animation: first 1.5s .8s forwards;
可以暂时不编写,因为这是附加的入场动画效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 .cube-face { width : 100px ; height : 100px ; box-sizing : border-box; position : absolute; text-align : center; font-size : 40px ; line-height : 100px ; opacity : 0 ; user-select: none; & :nth-child(1 ) { top : -100% ; transform : rotateX(90deg ) translateY(50px ) translateZ(150px ); transform-origin : bottom; animation : first 1.5s .8s forwards; } & :nth-child (2 ) { bottom : -100% ; transform-origin : top; transform : rotateX(-90deg ) translateY(-50px ) translateZ(150px ); animation : second 1.5s 1s forwards; } & :nth-child (3 ) { left : -100% ; transform-origin : right; transform : rotateY(-90deg ) translateX(50px ) translateZ(150px ); animation : third 1.5s .4s forwards; } & :nth-child (4 ) { right : -100% ; transform-origin : left; transform : rotateY(90deg ) translateX(-50px ) translateZ(150px ); animation : fourth 1.5s .6s forwards; } & :nth-child (5 ) { transform : rotateY(180deg ) translateZ(150px ); animation : fifth 1.5s .2s forwards; } & :nth-child (6 ) { transform : translateZ(150px ); animation : sixth 1.5s forwards; } }
如果你按照我上方的代码编写,你就会发现结果是这样的一个效果:
这是因为我们最终的目的是要使用@keyframe来添加进场动画,动画设置forward为播放一次,且在结束时停止,接下来编写6个css动画:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @keyframes first { 100% { transform : rotateX(90deg ) translateY(50px ) ; opacity : 1 ; } } @keyframes second { 100% { transform : rotateX(-90deg ) translateY(-50px ); opacity : 1 ; } } @keyframes third { 100% { transform : rotateY(-90deg ) translateX(50px ) ; opacity : 1 ; } } @keyframes fourth { 100% { transform : rotateY(90deg ) translateX(-50px ); opacity : 1 ; } } @keyframes fifth { 100% { transform : rotateY(180deg ) translateZ(50px ); opacity : 1 ; } } @keyframes sixth { 100% { transform : translateZ(50px ); opacity : 1 ; } }
每个动画的最终位置就是每个面的正确位置,最后为每个面加上延迟动画即可,这时你可以看到每一个面交错入场,并搭建成一个立方体的动画效果
此时需要另外使用一个透明的覆盖层,改变z-index,覆盖整个wrap,用于旋转时的防止立方体元素获得鼠标焦点
现在布局以及样式都以及准备完毕了,接下来就进入到JavaScript的编写
首先获取需要DOM元素
1 2 3 4 5 let wrap = document .querySelector (".wrap" );let cube = document .querySelector (".cube" );let cover = document .querySelector (".cover" );let tip = document .querySelector (".tip > span" )let cubeFace = document .querySelectorAll (".cube-face" )
创建需要的变量
1 2 3 4 5 6 7 let keydownPosX; let keydownPosY; let currentDegX = 0 ; let currentDegY = 0 ; let stopDegX = 30 ; let stopDegY = -30 ; let timer;
设置一个定时器,延时旋转立方体
1 2 3 4 5 6 setTimeout (function ( ) { timer = setInterval (function ( ) { stopDegX += .2 ; cube.style .transform = `rotateX(${stopDegY} deg) rotateY(${stopDegX} deg)` ; }, 16 ) }, 800 )
监听鼠标在cover覆盖层上的事件:分别是按下时,移动时,抬起时
1 2 3 cover.addEventListener ("mousedown" , mousedown) cover.addEventListener ("mouseup" , mouseup) cover.addEventListener ("mouseout" , mouseup)
编写按下时的回调函数:立即清除定时器,记录坐标
1 2 3 4 5 6 function mousedown (ev ) { clearInterval (timer); keydownPosX = ev.offsetX ; keydownPosY = ev.offsetY ; cover.addEventListener ("mousemove" , drag); }
编写拖拽与鼠标抬起事件的回调函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function drag (ev ) { currentDegX = (ev.offsetX - keydownPosX) / 3 ; currentDegY = -(ev.offsetY - keydownPosY) / 3 ; cube.style .transform = `rotateX(${stopDegY + currentDegY} deg) rotateY(${stopDegX + currentDegX} deg)` ; } function mouseup (ev ) { stopDegX += currentDegX stopDegY += currentDegY currentDegX = 0 ; currentDegY = 0 ; cover.removeEventListener ("mousemove" , drag); }
到此,可旋转立方体基本功能已经完成,代码相对简单,只涉及到事件对象的信息获取以及对元素的样式操作
最后附上Demo地址: cube (使用PC端浏览器打开)