浏览器发展至今,在网页上呈现 3D 效果已经非常简单了,只需要我们用上一个 css 属性,就可以让我们的页面元素拥有 3D 效果,今天我们就使用这个特性来做一个 3D 旋转盒子。
先来体验一下效果:
代码片段
transform-style
上面说到如果需要开启 3D 效果的话,就需要我们使用一个css
属性,那就是transform-style
,这个属性有两个值:
- flat:默认值,表示元素的子元素不会被视为 3D 对象,而是被平面化处理。
- preserve-3d:表示元素的子元素会被视为 3D 对象,保留 3D 效果。
transform-style: preserve-3d
属性定义嵌套元素如何在 3D 空间中呈现。它允许我们在 3D 空间中旋转、缩放和移动元素。
这个属性使用起来很简单,只需要给需要开启 3D 效果的元素添加这个属性即可,如下:
.box {
transform-style: preserve-3d;
}
这个属性是可以继承的,也就是说,如果我们给父元素添加了这个属性,那么子元素就会继承这个属性,也就是说子元素也会开启 3D 效果。
3D 盒子
既然我们已经知道了如何开启 3D 效果,那么我们就可以开始做一个 3D 盒子了。
首先我们分析一下 3D 盒子的结构,一个 3D 盒子由 6 个面组成,每个面都是一个div
元素,如下:
<div class="container">
<div class="side1"></div>
<div class="side2"></div>
<div class="side3"></div>
<div class="side4"></div>
<div class="side5"></div>
<div class="side6"></div>
</div>
我的命名不规范,不要学我,我是为了图方便。
然后我们给每个面添加样式,如下:
/* 设置盒子的样式,增加3d效果 */
.container {
position: relative;
width: 300px;
height: 300px;
transform-style: preserve-3d;
transform-origin: center center;
}
/* 设置每个面的样式 */
[class*="side"] {
position: absolute;
width: 300px;
height: 300px;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
box-sizing: border-box;
}
/* 设置每个面的背景图片和位置 */
.side1 {
transform: translate3d(0, 0, 150px);
background-image: url("https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5400559486c24cada8c01f13a72e875a~tplv-k3u1fbpfcp-watermark.image?");
}
.side2 {
transform: translate3d(0, 0, -150px) rotateY(180deg);
background-image: url("https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a02777827b2e4fdaa91107ae5a12c2ab~tplv-k3u1fbpfcp-watermark.image?");
}
.side3 {
transform: translate3d(0, -150px, 0) rotateX(90deg);
background-image: url("https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d6159b78ff704034a4ebf06b7093cadc~tplv-k3u1fbpfcp-watermark.image?");
}
.side4 {
transform: translate3d(0, 150px, 0) rotateX(90deg) rotateY(180deg);
background-image: url("https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c0d3cb14bc8648b7a14d7e795a628f02~tplv-k3u1fbpfcp-watermark.image?");
}
.side5 {
transform: translate3d(-150px, 0, 0) rotateY(90deg);
background-image: url("https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8cadd5ea9f59479996c20b3e5619c4fd~tplv-k3u1fbpfcp-watermark.image?");
}
.side6 {
transform: translate3d(150px, 0, 0) rotateY(90deg) rotateY(180deg);
background-image: url("https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/dc92dc8a2a0f42f187e108f803fa903d~tplv-k3u1fbpfcp-watermark.image?");
}
完成之后,上面的 6 个面就会呈现出 3D 效果,但是页面上并没有看到 3D
盒子,这是因为后面的其他面都被挡住了,我们旋转一下就好了,为了效果更明显,我们给container
添加一个动画,如下:
.container {
/* 省略其他代码 */
animation: rotate 10s linear infinite;
}
@keyframes rotate {
100% {
transform: rotate3d(1, 1, 1, 360deg);
}
}
效果如下:
感觉还不错,但是这个 3D 盒子还是有点单调,就简简单单加个边框吧,如下:
/* 省略其他代码 */
[class*="side"] {
border-left: 12px solid #ddd;
border-right: 12px solid #ddd;
border-top: 12px solid #eee;
border-bottom: 12px solid #eee;
}
加点交互
这个盒子好看是好看,但是我想看指定的面,怎么办呢?
那我们就让它可以拖动旋转吧,就这点小需求还不是简简单单,上代码:
const container = document.querySelector('.container');
// 监听鼠标按下事件
document.onmousedown = function (e) {
// 停止animation
container.style.animation = 'none';
// 记录鼠标按下时的坐标
const {
clientX, clientY} = e;
// 监听鼠标移动事件
document.onmousemove = function (e) {
// 计算鼠标移动的距离
const {
clientX: x, clientY: y} = e;
const dx = x - clientX;
const dy = y - clientY;
const rotateX = dy / 2;
const rotateY = dx * 2;
// 获取当前的旋转角度
const transform = getComputedStyle(container).transform;
const matrix = transform.match(/matrix(3d)?((.+))/)[2].split(', ') || [];
const matrixX = Number(matrix[8]);
const matrixY = Number(matrix[9]);
// 旋转
container.style.transform = `rotateX(${rotateX + matrixX}deg) rotateY(${rotateY + matrixY}deg)`;
}
// 监听鼠标抬起事件
document.onmouseup = function () {
// 移除鼠标移动事件
document.onmousemove = null
document.onmouseup = null
// 重新开始animation
container.style.animation = 'rotate 10s linear infinite';
}
}
完美,现在我们想看哪个面就拖动到哪个面,真不错呀!!!
但是感觉还是差点什么,既然是选秀盒子,那我就需要选呀,那就再加个选秀的交互吧!!!
选秀
选秀的交互其实很简单,就是点击某个面,就让它旋转到正面,这个交互其实就是点击事件,我们只需要监听点击事件,然后旋转到正面就好了,如下:
// 监听每个面的点击事件
document.querySelector('.side1').addEventListener('click', () => {
container.style.transform = 'rotateX(360deg) rotateY(360deg)';
container.style.animation = 'none';
})
document.querySelector('.side2').addEventListener('click', () => {
container.style.transform = 'rotateX(0deg) rotateY(180deg)';
container.style.animation = 'none';
})
document.querySelector('.side3').addEventListener('click', () => {
container.style.transform = 'rotateX(270deg) rotateY(0deg)';
container.style.animation = 'none';
})
document.querySelector('.side4').addEventListener('click', () => {
container.style.transform = 'rotateX(90deg) rotateY(180deg)';
container.style.animation = 'none';
})
document.querySelector('.side5').addEventListener('click', () => {
container.style.transform = 'rotateX(0) rotateY(90deg)';
container.style.animation = 'none';
})
document.querySelector('.side6').addEventListener('click', () => {
container.style.transform = 'rotateX(0deg) rotateY(270deg)';
container.style.animation = 'none';
})
由于每个面的旋转角度都不一样,所以我们需要分别设置,这里就不做优化了,直接写死了。
上面的代码已经可以让我们点击某个面,就让它旋转到正面了,固定到我们选中的妹子,但是既然是我们选中的妹子,那一定要很抢眼才行,要亮灯:
亮灯
亮灯就是直接加个边框闪烁的效果,这个效果其实很简单,就是通过animation来实现,如下:
@keyframes light {
0% {
box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.7);
}
70% {
box-shadow: 0 0 0 10px rgba(255, 255, 255, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(255, 255, 255, 0);
}
}
.light {
animation: light 1s infinite;
}
这里我们只需要给选中的面加个类名就好了,如下:
document.querySelector('.side1').addEventListener('click', () => {
// 亮灯
const light = document.querySelector('.light');
light && light.classList.remove('light');
e.target.classList.add('light');
// 省略其他代码
})
ok,大功告成,我们的选秀盒子就完成了,好像也没几行代码,但是这个效果还是很不错的,可以看看效果: