略微有点标题党,轻拍。。
先看效果:
说到球首先想到的,就是地球经纬度那样的线框图。
- 18条经线,18个圆,绕Y轴方法依次旋转10度
- 17条纬线,从-80度开始一直到80度。
- 纬线半径为cos(R),纬线偏移为sin(R)
- 地球倾角为23度左右,加上一个旋转的动画效果
使用js来生成经纬线:
.container,.box {
position: relative;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
transform-style: preserve-3d;
}
.container {
perspective: 500px;
overflow: hidden;
}
.box {
animation: rotate 20s linear infinite;
}
.box>div {
position: absolute;
width: 320px;
height: 320px;
border: solid 1px #000; border-radius: 50%;
}
@keyframes rotate {
0% { transform: rotateZ(23deg) rotateY(0deg); }
100% { transform: rotateZ(23deg) rotateY(360deg); }}
<div class="container"> <div class="box"> </div></div>
function addLongitude() {
var count = 18
var stepDeg = 180 / count
var fragment = document.createDocumentFragment()
for (var i = 0; i < count; i ) {
var div = document.createElement('div')
div.style.transform = 'rotateY(' ~~(i * stepDeg) 'deg)'
fragment.appendChild(div)
}
document.querySelector('.box').appendChild(fragment)}
function addLatitude() {
var r = 160
var stepDeg = 10
var start = -80
var end = 80
var fragment = document.createDocumentFragment()
for (var i = start; i <= end; i = stepDeg) {
var div = document.createElement('div')
var _r = ~~(Math.cos(i / 180 * Math.PI) * r * 2) 'px'
div.style.width = _r
div.style.height = _r
div.style.transform = 'translateY(' ~~(Math.sin(i / 180 * Math.PI) * r) 'px) rotateX(90deg)'
fragment.appendChild(div)
}
document.querySelector('.box').appendChild(fragment)}
window.onload = function () {
addLongitude()
addLatitude()
}
线框图显然是不满足作为一个球的。
我们换个思路。把一个div填充了颜色,贴在球形的表面。
- 画一个有背景颜色的圆
- 在Y轴上选择经度的角度,在X轴上旋转纬度的角度,在Z轴上向外偏移半径R
- 然后纬度从-90度开始到90度依次铺满这个圆
- 由于相同大小的填充圆,在两极用到的数量比赤道要少很多。我们根据纬线半径来调整一下填充数量
上边代码修改内部div样式
.box>div {
position: absolute;
width: 50px;
height: 50px;
border-radius: 50%;
background:rgba(255,0,0,.6);
}
function draw(deg) {
var fragment = document.createDocumentFragment()
var r = 180 // 赤道半径
r *= Math.cos(deg * Math.PI * 2 / 360) // 对应纬度 圆半径
var size = 46 // 填充圆直径
var len = r * 2 * Math.PI / size
for (var i = 0; i < len; i ) {
var div = document.createElement('div')
div.style.transform = 'rotateY(' ~~(360 / len * i) 'deg) rotateX(' deg 'deg) translateZ(160px)'
fragment.appendChild(div)
}
document.querySelector('.box').appendChild(fragment)
}
window.onload = function () {
for (var i = -90; i <= 90; i = 20) {
draw(i)
}
}
这个时候,我们调整div的大小和密度。理论上是可以得到一个比较完美的3D圆的。就是渲染起来会特别卡。
突然。我想起来,我电脑上有这么一张图:
现在,我们将这个图作为div的背景,然后调整每个div的background-position
div.style.backgroundPosition = (100 * i / len) '% -' (deg 90) / 180 * 100 '%'
当然,div颗粒越小,渲染出来的越平滑,越圆润但是也越卡。
手机上无法渲染了,要看的话下载源码使用chrome查看吧。
代码仓库地址:
https://github.com/shb190802/html5
演示地址:
https://suohb.com/demo/win/globe.html
https://suohb.com/demo/win/globe2.html(PC查看)
,