效果展示
成品的效果如上所示,如果懒得看具体实现过程,可以点这里直接跳转到源码部分。今天也算是突发奇想,想手搓个太阳系出来,那就先从最具代表性的土星做起。
实现逻辑
主要的构造很简单:.ballContainer
用来储存球体,.ball
是球体本身。
<div class="ballContainer">
<div class="ball"></div>
</div>
先给球体容器一些初始属性,比如位置和颜色:
.ballContainer {
position: absolute;
width: 600px;
height: 600px;
top: 200px;
left: 500px;
background: radial-gradient(circle at 33% 33%, rgba(255, 255, 255, 0.8) 0%, rgba(231, 146, 61, 0.85) 80%);
}
球体目前看起来是这样的。
颜色看起来不错,接着让它变成一个圆形,添加border-radius
属性。
border-radius: 50%;
球体的容器到这一步已经实现完成,接下来我们要实现球体本身的作用 —— 旋转。我的思路是这样的,首先让球体容器的配色看起来像是一个球体,但它实质上只是一个正面对面观察者的圆形平面,该平面本身不会做任何运动。运动都交给球体容器内的.ball和.halo。
我们给球体添加一个子div —— .halo
,我们的光环。并为球体添加一个充满3d感的运动效果。
<div class="ballContainer">
<div class="ball">
<div class="halo"></div>
</div>
</div>
<style>
@keyframes ballRotate {
0% {
transform: rotateX(10deg) rotateY(0deg) rotateZ(10deg) ;
}
50% {
transform: rotateX(-15deg) rotateY(720deg) rotateZ(-10deg) ;
}
100% {
transform: rotateX(10deg) rotateY(0deg) rotateZ(10deg) ;
}
}
.ball {
transform-style: preserve-3d;
animation: ballRotate 20s infinite linear;
width: 100%;
height: 100%;
}
.halo {
transform-style: preserve-3d;
position: absolute;
top: -200px;
left: -200px;
width: 1000px;
height: 1000px;
transform: rotateX(90deg);
background: conic-gradient(#FF5800, #FF8100, #FEAC00, #FFCC00, #EDE604);
border-radius: 50%;
/*中间镂空*/
mask: radial-gradient(transparent 400px, #000 200px);
}
</style>
球体的旋转是真实存在的,它带动着光环做三个方向的转动。
从图中可以看到,光环遮挡了球体容器,这导致3d效果大幅降低。解决办法是在球体容器上添加 transform-style: preserve-3d
属性。至此,主要的效果已经实现,接下来是锦上添花环节。我们在.ballContainer
外侧套上一个.body div
,将背景设置为黑色;接着为.ballContainer
添加一个景深效果。
transform: translateZ(2000px);
perspective: 1000px;
这里添加这两行样式的原因是,之后可以通过修改perspective
属性,改变观察者与球体之间的距离,实现更好的3d效果。
源码
<template>
<div class="body">
<div class="ballContainer">
<div class="ball">
<div class="halo"></div>
</div>
</div>
<div class="visionControl">
<span>拖动我!</span>
<input type="range" class="rangeInput" min="250" max="1000" v-model="range">
</div>
</div>
</template>
<script>
export default {
name: "ZTest",
data() {
return {
range: 750
}
},
watch: {
range(input) {
let ballContainer = document.querySelector('.ballContainer')
ballContainer.style.perspective = input + 'px'
}
}
}
</script>
<style scoped>
@keyframes ballRotate {
0% {
transform: rotateX(10deg) rotateY(0deg) rotateZ(10deg) ;
}
50% {
transform: rotateX(-15deg) rotateY(720deg) rotateZ(-10deg) ;
}
100% {
transform: rotateX(10deg) rotateY(0deg) rotateZ(10deg) ;
}
}
* {
margin: 0;
padding: 0;
}
.body {
height: 100vh;
width: 100vw;
background-color: #000;
overflow: hidden;
}
.visionControl {
z-index: 1000;
background-color: white;
height: 75px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.ballContainer {
transform-style: preserve-3d;
position: absolute;
width: 600px;
height: 600px;
top: 200px;
left: 500px;
background: radial-gradient(circle at 33% 33%, rgba(255, 255, 255, 0.8) 0%, rgba(231, 146, 61, 0.85) 80%);
border-radius: 50%;
transform: translateZ(2000px);
perspective: 1000px;
}
.ball {
transform-style: preserve-3d;
animation: ballRotate 20s infinite linear;
width: 100%;
height: 100%;
}
.halo {
transform-style: preserve-3d;
position: absolute;
top: -200px;
left: -200px;
width: 1000px;
height: 1000px;
transform: rotateX(90deg);
background: conic-gradient(#FF5800, #FF8100, #FEAC00, #FFCC00, #EDE604);
border-radius: 50%;
/*中间镂空*/
mask: radial-gradient(transparent 400px, #000 200px);
}
</style>
总结
当时做这个模拟球体的时候,也是考虑了很多可能性和方法,无奈一直都没办法用纯CSS实现真正意义上的球体。所以如果想要绘制真正的球体,还是交给canvas吧!