怎么样?正五边形简单吧?那么今天我们继续,把整个滚动的足球写出来吧。
一、五角星在足球上的位置
一个传统足球表面有 12 个正五边形。为了便于理解,我们使用地球模型来做讲解。
最简单的,我们在南极和北极各放一个正五边形。这两个五边形从正常的屏幕主视角(也叫做前视角)看上去,都是几乎处于水平位置。即:这两个正五边形围绕 X 轴分别向上或向下旋转 90deg。
然后我们需要在北半球和南半球的相同维度各放置五个正五边形。
维度方面: 北半球的五个应该放在北纬 30deg 的地方,同样南半球的五个也应该放在南纬 30deg 的地方。这样一来,北极到北纬、北纬到南纬、南纬到南极,角度差均为 60deg。
经度方面: 完整一圈一共有 360deg,一圈放置 5 个,那么就平均放在夹角 72deg 的平均分布。并确保南半球和北半球相对有一个 36deg 的错位。
二、构建创建正五边形的方法
我们从主视角出发,每一个正五边形都创建在主视角正中间。然后通过 transform 的 3D 属性完成位置旋转。因此需要控制五边形的样式为 3D 模式:
// html
<div class="ball"></div>
// css
.ball {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 1000px;
height: 1000px;
margin: auto;
transform-style: preserve-3d;
}
.five {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 200px;
height: 0;
margin: auto;
border-top: 190.35px solid black;
border-right: 61.8px solid transparent;
border-left: 61.8px solid transparent;
backface-visibility: visible;
}
.five::before {
position: absolute;
top: -285px;
left: -61.8px;
border-right: 161.8px solid transparent;
border-bottom: 95.1px solid black;
border-left: 161.8px solid transparent;
content: '';
}
我们通过 JavaScript 动态生成一个正五边形元素:
const ball = document.querySelector('.ball');
const five = document.createElement('div');
five.className = 'five';ball.appendChild(five);
若要把这个正五边形放到北极,只需要围绕 X 轴向北旋转 90deg:
five.style.transform = 'rotateY(0deg) rotateX(90deg) translateZ(500px)';
同理,放到南极:
five.style.transform = 'rotateY(0deg) rotateX(-90deg) translateZ(500px)';
同样的道理,放置北半球的五个,需要以 72deg 为跨度让其围绕 Y 轴旋转:
five.style.transform = 'rotateY(0deg) rotateX(30deg) translateZ(500px)';
five.style.transform = 'rotateY(72deg) rotateX(30deg) translateZ(500px)';
five.style.transform = 'rotateY(144deg) rotateX(30deg) translateZ(500px)';
five.style.transform = 'rotateY(216deg) rotateX(30deg) translateZ(500px)';
five.style.transform = 'rotateY(288deg) rotateX(30deg) translateZ(500px)';
南半球与北半球在经度方面存在一个 36deg 的错位:
five.style.transform = 'rotateY(36deg) rotateX(-30deg) translateZ(500px)';
five.style.transform = 'rotateY(108deg) rotateX(-30deg) translateZ(500px)';
five.style.transform = 'rotateY(180deg) rotateX(-30deg) translateZ(500px)';
five.style.transform = 'rotateY(252deg) rotateX(-30deg) translateZ(500px)';
five.style.transform = 'rotateY(324deg) rotateX(-30deg) translateZ(500px)';
聪明的你肯定已经找到规律了,咱们不妨将每一个正五角星的创建封装成一个函数:
/**
* @description 创建一个正五角星
* @param {Number} num 北半球或者南半球各有 num 个正五角星
* @param {Number} item 纬度
* @param {Number} index 北半球或者南半球的第 index 个正五角星
* @param {Number} translate 南半球与北半球之间在经度上存在一个 36deg 的错位,默认为 0,南半球传入 36
*/
function createFivePolygon(num, item, index, translate = 0) {
const radius = 500; // 足球半径
const gap = 360 / num; // 每个五边形之间的空隙
const ball = document.querySelector('.ball'); // 找到足球 DOM
const five = document.createElement('div'); // 创建一个正五边形
five.className = 'five'; // 设置这个正五边形的类名,让其与 CSS 样式关联
five.style.transform = `rotateY(${index * gap + translate}deg) rotateX(${item}deg) translateZ(${radius}px)`; // 位置控制
ball.appendChild(five); // 把这个正五边形放到足球 DOM 树上
}
这样,咱们就封装了一个创建正五边形的函数。
三、在对应位置创建正五边形
北极和南极各创建一个:
createFivePolygon(1, 90, 0); // 北极
createFivePolygon(1, -90, 0); // 南极
北半球和南半球各在对应位置创建五个:
createFivePolygon(5, 30, 0); // 北半球
createFivePolygon(5, 30, 1); // 北半球
createFivePolygon(5, 30, 2); // 北半球
createFivePolygon(5, 30, 3); // 北半球
createFivePolygon(5, 30, 4); // 北半球
createFivePolygon(5, -30, 0, 36); // 南半球
createFivePolygon(5, -30, 1, 36); // 南半球
createFivePolygon(5, -30, 2, 36); // 南半球
createFivePolygon(5, -30, 3, 36); // 南半球
createFivePolygon(5, -30, 4, 36); // 南半球
可以看到,createFivePolygon 方法调用了 12 次,摸清楚其中的规律,我们就可以通过循环调用:
function calcPosition() {
const rotateArr = [30, -30]; // 每一圈绕 X 轴旋转的度数
const num = 5; // 每一圈有多少个五边形
rotateArr.forEach(function(item) {
for (let i = 0; i < num; i++) {
createFivePolygon(num, item, i, item > 0 ? 0 : 36);
}
});
createFivePolygon(1, 90, 0); // 北极
createFivePolygon(1, -90, 0); // 南极
}
这样,我们就通过一个简单的函数封装,实现了 12 个正五边形的创建。
四、让足球滚动起来
咱们通过 CSS 动画,让足球围绕 Y 轴转动:
@keyframes rotate {
from { transform: rotateY(0deg); }
to { transform: rotateY(360deg); }
}
为了更加真实一些,我们控制一个“黄赤夹角”,让其滚动的更加真实一些:
.ball {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 1000px;
height: 1000px;
margin: auto;
transform: rotateX(-20deg) rotateY(0deg) rotateZ(10deg);
transform-style: preserve-3d;
animation: rotate 10s linear infinite;
}
@keyframes rotate {
from { transform: rotateX(-20deg) rotateY(0deg) rotateZ(10deg); }
to { transform: rotateX(-20deg) rotateY(360deg) rotateZ(10deg); }
}
这样,我们就实现了一个足球的滚动效果。
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>足球</title>
<style type="text/css">
.ball {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 1000px;
height: 1000px;
margin: auto;
/* transform: rotateX(-20deg) rotateZ(10deg) rotateY(0deg); */
transform: rotateX(-20deg) rotateY(0deg) rotateZ(10deg);
transform-style: preserve-3d;
animation: rotate 10s linear infinite;
}
.five {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 200px;
height: 0;
margin: auto;
border-top: 190.35px solid black;
border-right: 61.8px solid transparent;
border-left: 61.8px solid transparent;
backface-visibility: visible;
}
.five::before {
position: absolute;
top: -285px;
left: -61.8px;
border-right: 161.8px solid transparent;
border-bottom: 95.1px solid black;
border-left: 161.8px solid transparent;
content: '';
}
@keyframes rotate {
from { transform: rotateX(-20deg) rotateY(0deg) rotateZ(10deg); }
to { transform: rotateX(-20deg) rotateY(360deg) rotateZ(10deg); }
}
</style>
<script type="text/javascript">
function calcPosition() {
const rotateArr = [30, -30]; // 每一圈绕 X 轴旋转的度数
const num = 5; // 每一圈有多少个五边形
rotateArr.forEach(function(item) {
for (let i = 0; i < num; i++) {
createFivePolygon(num, item, i, item > 0 ? 0 : 36);
}
});
createFivePolygon(1, 90, 0); // 北极
createFivePolygon(1, -90, 0); // 南极
}
/**
* @description 创建一个正五角星
* @param {Number} num 北半球或者南半球各有 num 个正五角星
* @param {Number} item 纬度
* @param {Number} index 北半球或者南半球的第 index 个正五角星
* @param {Number} translate 南半球与北半球之间在经度上存在一个 36deg 的错位,默认为 0,南半球传入 36
*/
function createFivePolygon(num, item, index, translate = 0) {
const radius = 500; // 足球半径
const gap = 360 / num; // 每个五边形之间的空隙
const ball = document.querySelector('.ball'); // 找到足球 DOM
const five = document.createElement('div'); // 创建一个正五边形
five.className = 'five'; // 设置这个正五边形的类名,让其与 CSS 样式关联
five.style.transform = `rotateY(${index * gap + translate}deg) rotateX(${item}deg) translateZ(${radius}px)`; // 位置控制
ball.appendChild(five); // 把这个正五边形放到足球 DOM 树上
}
window.onload = calcPosition;
</script>
</head>
<body>
<div class="ball"></div>
</body>
</html>
↓
↓
↓
仅仅依赖文字讲解,对于这样 3D 模型来说,比较容易难以理解。不久的将来,我会补上视频讲解,以便更加透彻的理解。大家敬请期待......