打造真实魔方教程

老姚
老姚 发布于 2016-11-10 20:55:13 浏览:2622 类型:原创 - 随笔 分类:JavaScript - 我也来说说系列 二维码: 作者原创 版权保护
这是一篇讲解如何打造真实魔方(可以转着玩的)的教程。

先说说本文的行文风格。
每一步都不改原先的代码,基本都是新增标签,添加新的代码。
这样步步迭代,大家看得也很清晰,一股脑的贴代码,觉得很难有信心坚持看完。
每次代码中都标出了,跟上一次相比哪块是新增的。
友情提示一下,本文关心的是实现原理,因此没有考虑兼容性问题,chrome是没问题的。

正文开始

魔方,众所周知,1阶魔方就是一个立方块(体),当然也不能算是魔方。
2阶魔方是8个立方块(体)。本文以打造2阶魔方为例讲,讲解如何画任意阶魔方。
因为从1到2,发生了质变,而2到3,再到4,只是量变。
7阶魔方的原理与2阶魔方,并没什么区别

第一步,画一个立方体

首先我们知道坐标系是这样的,
x轴,从屏幕的左到右;
y轴,从屏幕的上到下;
z轴,从屏幕的里到外。
因此对于平移来说,是很好理解的,
比如translateZ(100px),就是向屏幕外面平移100px。

那么3D变换的旋转方向规律是什么呢?
左手定则

比如rotateZ(90deg),
根据左手定则,左手握住z轴,
拇指向外,其余四指弯曲的方向就是旋转方向,即顺时针旋转。

再比如rotateX(-90deg),
左手握住x轴,拇指向左(因为是负数),四指弯曲的方向是从里到外的。
因此画一个立方体,就是由6个面平移加旋转。
<style>
div{
	margin:150px 200px;
	width:100px;
	height:100px;
	transform: rotateX(-45deg) rotateY(30deg);
	transform-style:preserve-3d;
}
ul{
	width:100px;
	height:100px;
	padding:0;
	list-style:none;
	background:black;
	position:relative;
	transform-style:preserve-3d;
}
li{
	width:100px;
	height:100px;
	background:#00ffff;
	opacity:0.5;
	border:1px solid red;
	position:absolute;
	left:0;
	top:0;
}
li:nth-child(1){
	transform:translateX(-50px) rotateY(-90deg);
}
li:nth-child(2){
	transform:translateY(-50px) rotateX(90deg);
}
li:nth-child(3){
	transform:translateZ(50px) rotateY(0deg);
}
li:nth-child(4){
	transform:translateZ(-50px) rotateY(180deg);
}
li:nth-child(5){
	transform:translateY(50px) rotateX(90deg);
}
li:nth-child(6){
	transform:translateX(50px) rotateY(90deg);
}
</style>
<div>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
</div>

ul的父元素样式属性定义了
transform-style:preserve-3d;
表示变换风格为保持三维,默认是平面(flat)。
也许你还分不清哪个是哪个,请看:
<style>
/*原来的样式*/
div{
	margin:150px 200px;
	width:100px;
	height:100px;
	transform: rotateX(-45deg) rotateY(30deg);
	transform-style:preserve-3d;
}
ul{
	width:100px;
	height:100px;
	padding:0;
	list-style:none;
	background:black;
	position:relative;
	transform-style:preserve-3d;
}
li{
	width:100px;
	height:100px;
	background:#00ffff;
	opacity:0.5;
	border:1px solid red;
	position:absolute;
	left:0;
	top:0;
}
li:nth-child(1){
	transform:translateX(-50px) rotateY(-90deg);
}
li:nth-child(2){
	transform:translateY(-50px) rotateX(90deg);
}
li:nth-child(3){
	transform:translateZ(50px) rotateY(0deg);
}
li:nth-child(4){
	transform:translateZ(-50px) rotateY(180deg);
}
li:nth-child(5){
	transform:translateY(50px) rotateX(90deg);
}
li:nth-child(6){
	transform:translateX(50px) rotateY(90deg);
}
</style>
<style>
/*新增加的样式*/
ul, li{
	text-align:center;
	line-height:100px;
	opacity:1;
}
ul:before, li:before{
	color:black;
	font-size:30px;
	font-weight:bold;
}
ul:before{
	content:'ul';
}
li:nth-child(1):before{
	content:'left';
}
li:nth-child(2):before{
	content:'top';
}
li:nth-child(3):before{
	content:'front';
}
li:nth-child(4):before{
	content:'back';
}
li:nth-child(5):before{
	content:'bottom';
}
li:nth-child(6):before{
	content:'right';
}
</style>
<div>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
</div>


第二步,画个2阶魔方


2阶魔方就是8个立方体,
因此也是8个ul,那么通过平移便能做到:
<style>
/*原来的样式*/
div{
	margin:150px 200px;
	width:100px;
	height:100px;
	transform: rotateX(-45deg) rotateY(30deg);
	transform-style:preserve-3d;
}
ul{
	width:100px;
	height:100px;
	padding:0;
	list-style:none;
	background:black;
	position:relative;
	transform-style:preserve-3d;
}
li{
	width:100px;
	height:100px;
	background:#00ffff;
	opacity:0.5;
	border:1px solid red;
	position:absolute;
	left:0;
	top:0;
}
li:nth-child(1){
	transform:translateX(-50px) rotateY(-90deg);
}
li:nth-child(2){
	transform:translateY(-50px) rotateX(90deg);
}
li:nth-child(3){
	transform:translateZ(50px) rotateY(0deg);
}
li:nth-child(4){
	transform:translateZ(-50px) rotateY(180deg);
}
li:nth-child(5){
	transform:translateY(50px) rotateX(90deg);
}
li:nth-child(6){
	transform:translateX(50px) rotateY(90deg);
}
</style>
<style>
/*新增加的样式*/
div{
	position:absolute;
}
ul{
	position:absolute;
	top:0;
	left:0;
}
ul:nth-child(1){
}
ul:nth-child(2){
	transform:translateX(110px);
}
ul:nth-child(3){
	transform:translateY(110px);
}
ul:nth-child(4){
	transform:translateX(110px) translateY(110px);
}
ul:nth-child(5){
	transform:translateZ(110px);
}
ul:nth-child(6){
	transform:translateX(110px) translateZ(110px);
}
ul:nth-child(7){
	transform:translateY(110px) translateZ(110px);
}
ul:nth-child(8){
	transform:translateX(110px) translateY(110px) translateZ(110px);
}
</style>
<div>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
</div>


第三步,给立方体表面添加颜色

这里使用的配色方案是
左面是紫色;
上面是蓝色;
前面是白色;
右面是红色;
下面是绿色;
后面是黄色。
2阶魔方的左面是第1、3、5、7个ul的第一个li,新增代码如下:
<style>
/*原来的样式*/
div{
	margin:150px 200px;
	width:100px;
	height:100px;
	transform: rotateX(-45deg) rotateY(30deg);
	transform-style:preserve-3d;
}
ul{
	width:100px;
	height:100px;
	padding:0;
	list-style:none;
	background:black;
	position:relative;
	transform-style:preserve-3d;
}
li{
	width:100px;
	height:100px;
	background:#00ffff;
	opacity:0.5;
	border:1px solid red;
	position:absolute;
	left:0;
	top:0;
}
li:nth-child(1){
	transform:translateX(-50px) rotateY(-90deg);
}
li:nth-child(2){
	transform:translateY(-50px) rotateX(90deg);
}
li:nth-child(3){
	transform:translateZ(50px) rotateY(0deg);
}
li:nth-child(4){
	transform:translateZ(-50px) rotateY(180deg);
}
li:nth-child(5){
	transform:translateY(50px) rotateX(90deg);
}
li:nth-child(6){
	transform:translateX(50px) rotateY(90deg);
}
</style>
<style>
/*原来的样式*/
div{
	position:absolute;
}
ul{
	position:absolute;
	top:0;
	left:0;
}
ul:nth-child(1){
}
ul:nth-child(2){
	transform:translateX(110px);
}
ul:nth-child(3){
	transform:translateY(110px);
}
ul:nth-child(4){
	transform:translateX(110px) translateY(110px);
}
ul:nth-child(5){
	transform:translateZ(110px);
}
ul:nth-child(6){
	transform:translateX(110px) translateZ(110px);
}
ul:nth-child(7){
	transform:translateY(110px) translateZ(110px);
}
ul:nth-child(8){
	transform:translateX(110px) translateY(110px) translateZ(110px);
}
</style>
<style>
/*新增加的样式*/
body{
	background:black;
}
ul{
	background:none;
}
li{
	opacity:1;
	border:none;
}
/*left*/
ul:nth-child(1) li:nth-child(1),
ul:nth-child(3) li:nth-child(1),
ul:nth-child(5) li:nth-child(1),
ul:nth-child(7) li:nth-child(1){
	background:purple;
}
/*top*/
ul:nth-child(1) li:nth-child(2),
ul:nth-child(2) li:nth-child(2),
ul:nth-child(5) li:nth-child(2),
ul:nth-child(6) li:nth-child(2){
	background:blue;
}
/*front*/
ul:nth-child(5) li:nth-child(3),
ul:nth-child(6) li:nth-child(3),
ul:nth-child(7) li:nth-child(3),
ul:nth-child(8) li:nth-child(3){
	background:white;
}
/*right*/
ul:nth-child(2) li:nth-child(6),
ul:nth-child(4) li:nth-child(6),
ul:nth-child(6) li:nth-child(6),
ul:nth-child(8) li:nth-child(6){
	background:red;
}
/*bottom*/
ul:nth-child(3) li:nth-child(5),
ul:nth-child(4) li:nth-child(5),
ul:nth-child(7) li:nth-child(5),
ul:nth-child(8) li:nth-child(5){
	background:green;
}
/*back*/
ul:nth-child(1) li:nth-child(4),
ul:nth-child(2) li:nth-child(4),
ul:nth-child(3) li:nth-child(4),
ul:nth-child(4) li:nth-child(4){
	background:yellow;
}
</style>
<div>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
</div>

代码中的6大面颜色赋值我直接用的笨方法来做的,
如果元素是js动态生成的话,弄个坐标系统,
那么通过坐标系统很容易取到的。

第四步,实现整体拖拽转到效果

目前还只能看到前3个面,我们这里支持一下整体拖拽,方便看到6个面。
<style>
/*原来的样式*/
div{
	margin:150px 200px;
	width:100px;
	height:100px;
	transform: rotateX(-45deg) rotateY(30deg);
	transform-style:preserve-3d;
}
ul{
	width:100px;
	height:100px;
	padding:0;
	list-style:none;
	background:black;
	position:relative;
	transform-style:preserve-3d;
}
li{
	width:100px;
	height:100px;
	background:#00ffff;
	opacity:0.5;
	border:1px solid red;
	position:absolute;
	left:0;
	top:0;
}
li:nth-child(1){
	transform:translateX(-50px) rotateY(-90deg);
}
li:nth-child(2){
	transform:translateY(-50px) rotateX(90deg);
}
li:nth-child(3){
	transform:translateZ(50px) rotateY(0deg);
}
li:nth-child(4){
	transform:translateZ(-50px) rotateY(180deg);
}
li:nth-child(5){
	transform:translateY(50px) rotateX(90deg);
}
li:nth-child(6){
	transform:translateX(50px) rotateY(90deg);
}
</style>
<style>
/*原来的样式*/
div{
	position:absolute;
}
ul{
	position:absolute;
	top:0;
	left:0;
}
ul:nth-child(1){
}
ul:nth-child(2){
	transform:translateX(110px);
}
ul:nth-child(3){
	transform:translateY(110px);
}
ul:nth-child(4){
	transform:translateX(110px) translateY(110px);
}
ul:nth-child(5){
	transform:translateZ(110px);
}
ul:nth-child(6){
	transform:translateX(110px) translateZ(110px);
}
ul:nth-child(7){
	transform:translateY(110px) translateZ(110px);
}
ul:nth-child(8){
	transform:translateX(110px) translateY(110px) translateZ(110px);
}
</style>
<style>
/*原来的样式*/
body{
	background:black;
}
ul{
	background:none;
}
li{
	opacity:1;
	border:none;
}
/*left*/
ul:nth-child(1) li:nth-child(1),
ul:nth-child(3) li:nth-child(1),
ul:nth-child(5) li:nth-child(1),
ul:nth-child(7) li:nth-child(1){
	background:purple;
}
/*top*/
ul:nth-child(1) li:nth-child(2),
ul:nth-child(2) li:nth-child(2),
ul:nth-child(5) li:nth-child(2),
ul:nth-child(6) li:nth-child(2){
	background:blue;
}
/*front*/
ul:nth-child(5) li:nth-child(3),
ul:nth-child(6) li:nth-child(3),
ul:nth-child(7) li:nth-child(3),
ul:nth-child(8) li:nth-child(3){
	background:white;
}
/*right*/
ul:nth-child(2) li:nth-child(6),
ul:nth-child(4) li:nth-child(6),
ul:nth-child(6) li:nth-child(6),
ul:nth-child(8) li:nth-child(6){
	background:red;
}
/*bottom*/
ul:nth-child(3) li:nth-child(5),
ul:nth-child(4) li:nth-child(5),
ul:nth-child(7) li:nth-child(5),
ul:nth-child(8) li:nth-child(5){
	background:green;
}
/*back*/
ul:nth-child(1) li:nth-child(4),
ul:nth-child(2) li:nth-child(4),
ul:nth-child(3) li:nth-child(4),
ul:nth-child(4) li:nth-child(4){
	background:yellow;
}
</style>
<div>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
	<ul>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
		<li></li>
	</ul>
</div>
<style>
/*新增加的样式*/
div{
	transform-origin: 105px 105px 55px;
}
ul{
	margin:0;
}
</style>
<script>
/*新增加的代码*/
var flag = false;// 是否启动拖拽
document.addEventListener('mousedown', function() {
	flag = true;
}, false);
document.addEventListener('mouseup', function() {
	flag = false;
}, false);
var div = document.querySelector('div');
document.addEventListener('mousemove', function move(e) {
	if (!flag) {
		move.lastX = e.x;
		move.lastY = e.y;
		return;
	}
	var transform = getComputedStyle(div).getPropertyValue('transform');
	div.style.setProperty('transform', transform + 'rotateX(' + (move.lastY - e.y) / 5 + 'deg) rotateY(' + (e.x - move.lastX) / 5 + 'deg)');
	move.lastX = e.x;
	move.lastY = e.y;
}, false);
</script>

新增拖拽代码还是很简单的,再原来的变换基础上再变。
应该能看懂,看不懂也无关紧要,因为不属于魔方原理的核心。

第五步,给6个面附上方向箭头

上箭头是⇑(&#8657加分号);
下箭头是⇓(&#8659加分号);
左箭头是⇐(&#8656加分号);
右箭头是⇒(&#8658加分号);

我以添加一个箭头为例:
<style>
/*原来的样式*/
div{
	margin:150px 200px;
	width:100px;
	height:100px;
	transform: rotateX(-45deg) rotateY(30deg);
	transform-style:preserve-3d;
}
ul{
	width:100px;
	height:100px;
	padding:0;
	list-style:none;
	background:black;
	position:relative;
	transform-style:preserve-3d;
}
li{
	width:100px;
	height:100px;
	background:#00ffff;
	opacity:0.5;
	border:1px solid red;
	position:absolute;
	left:0;
	top:0;
}
li:nth-child(1){
	transform:translateX(-50px) rotateY(-90deg);
}
li:nth-child(2){
	transform:translateY(-50px) rotateX(90deg);
}
li:nth-child(3){
	transform:translateZ(50px) rotateY(0deg);
}
li:nth-child(4){
	transform:translateZ(-50px) rotateY(180deg);
}
li:nth-child(5){
	transform:translateY(50px) rotateX(90deg);
}
li:nth-child(6){
	transform:translateX(50px) rotateY(90deg);
}
</style>
<style>
/*原来的样式*/
div{
	position:absolute;
}
ul{
	position:absolute;
	top:0;
	left:0;
}
ul:nth-child(1){
}
ul:nth-child(2){
	transform:translateX(110px);
}
ul:nth-child(3){
	transform:translateY(110px);
}
ul:nth-child(4){
	transform:translateX(110px) translateY(110px);
}
ul:nth-child(5){
	transform:translateZ(110px);
}
ul:nth-child(6){
	transform:translateX(110px) translateZ(110px);
}
ul:nth-child(7){
	transform:translateY(110px) translateZ(110px);
}
ul:nth-child(8){
	transform:translateX(110px) translateY(110px) translateZ(110px);
}
</style>
<style>
/*原来的样式*/
body{
	background:black;
}
ul{
	background:none;
}
li{
	opacity:1;
	border:none;
}
/*left*/
ul:nth-child(1) li:nth-child(1),
ul:nth-child(3) li:nth-child(1),
ul:nth-child(5) li:nth-child(1),
ul:nth-child(7) li:nth-child(1){
	background:purple;
}
/*top*/
ul:nth-child(1) li:nth-child(2),
ul:nth-child(2) li:nth-child(2),
ul:nth-child(5) li:nth-child(2),
ul:nth-child(6) li:nth-child(2){
	background:blue;
}
/*front*/
ul:nth-child(5) li:nth-child(3),
ul:nth-child(6) li:nth-child(3),
ul:nth-child(7) li:nth-child(3),
ul:nth-child(8) li:nth-child(3){
	background:white;
}
/*right*/
ul:nth-child(2) li:nth-child(6),
ul:nth-child(4) li:nth-child(6),
ul:nth-child(6) li:nth-child(6),
ul:nth-child(8) li:nth-child(6){
	background:red;
}
/*bottom*/
ul:nth-child(3) li:nth-child(5),
ul:nth-child(4) li:nth-child(5),
ul:nth-child(7) li:nth-child(5),
ul:nth-child(8) li:nth-child(5){
	background:green;
}
/*back*/
ul:nth-child(1) li:nth-child(4),
ul:nth-child(2) li:nth-child(4),
ul:nth-child(3) li:nth-child(4),
ul:nth-child(4) li:nth-child(4){
	background:yellow;
}
</style>
<div>
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>
</div>
<style>
/*原来的代码*/
div{
	transform-origin: 105px 105px 55px;
}
ul{
	margin:0;
}
</style>
<script>
/*原来的代码*/
var flag = false;// 是否启动拖拽
document.addEventListener('mousedown', function() {
	flag = true;
}, false);
document.addEventListener('mouseup', function() {
	flag = false;
}, false);
var div = document.querySelector('div');
document.addEventListener('mousemove', function move(e) {
	if (!flag) {
		move.lastX = e.x;
		move.lastY = e.y;
		return;
	}
	var transform = getComputedStyle(div).getPropertyValue('transform');
	div.style.setProperty('transform', transform + 'rotateX(' + (move.lastY - e.y) / 5 + 'deg) rotateY(' + (e.x - move.lastX) / 5 + 'deg)');
	move.lastX = e.x;
	move.lastY = e.y;
}, false);
</script>
<style>
/*新增加的代码*/
li{
	font-size:50px;
	line-height:100px;
	text-align:center;
	color:#00ffff;
}
</style>


第6步,让某一面旋转起来(任意一面可以通过轴向和第几层来定位)

比如我要旋转左面,只需点击白色面的第一个块即可。
首先我要获取到整个左面,为第1,3,5,7个ul。
绕着x轴正向旋转90度。
<style>
/*原来的样式*/
div{
	margin:150px 200px;
	width:100px;
	height:100px;
	transform: rotateX(-45deg) rotateY(30deg);
	transform-style:preserve-3d;
}
ul{
	width:100px;
	height:100px;
	padding:0;
	list-style:none;
	background:black;
	position:relative;
	transform-style:preserve-3d;
}
li{
	width:100px;
	height:100px;
	background:#00ffff;
	opacity:0.5;
	border:1px solid red;
	position:absolute;
	left:0;
	top:0;
}
li:nth-child(1){
	transform:translateX(-50px) rotateY(-90deg);
}
li:nth-child(2){
	transform:translateY(-50px) rotateX(90deg);
}
li:nth-child(3){
	transform:translateZ(50px) rotateY(0deg);
}
li:nth-child(4){
	transform:translateZ(-50px) rotateY(180deg);
}
li:nth-child(5){
	transform:translateY(50px) rotateX(90deg);
}
li:nth-child(6){
	transform:translateX(50px) rotateY(90deg);
}
</style>
<style>
/*原来的样式*/
div{
	position:absolute;
}
ul{
	position:absolute;
	top:0;
	left:0;
}
ul:nth-child(1){
}
ul:nth-child(2){
	transform:translateX(110px);
}
ul:nth-child(3){
	transform:translateY(110px);
}
ul:nth-child(4){
	transform:translateX(110px) translateY(110px);
}
ul:nth-child(5){
	transform:translateZ(110px);
}
ul:nth-child(6){
	transform:translateX(110px) translateZ(110px);
}
ul:nth-child(7){
	transform:translateY(110px) translateZ(110px);
}
ul:nth-child(8){
	transform:translateX(110px) translateY(110px) translateZ(110px);
}
</style>
<style>
/*原来的样式*/
body{
	background:black;
}
ul{
	background:none;
}
li{
	opacity:1;
	border:none;
}
/*left*/
ul:nth-child(1) li:nth-child(1),
ul:nth-child(3) li:nth-child(1),
ul:nth-child(5) li:nth-child(1),
ul:nth-child(7) li:nth-child(1){
	background:purple;
}
/*top*/
ul:nth-child(1) li:nth-child(2),
ul:nth-child(2) li:nth-child(2),
ul:nth-child(5) li:nth-child(2),
ul:nth-child(6) li:nth-child(2){
	background:blue;
}
/*front*/
ul:nth-child(5) li:nth-child(3),
ul:nth-child(6) li:nth-child(3),
ul:nth-child(7) li:nth-child(3),
ul:nth-child(8) li:nth-child(3){
	background:white;
}
/*right*/
ul:nth-child(2) li:nth-child(6),
ul:nth-child(4) li:nth-child(6),
ul:nth-child(6) li:nth-child(6),
ul:nth-child(8) li:nth-child(6){
	background:red;
}
/*bottom*/
ul:nth-child(3) li:nth-child(5),
ul:nth-child(4) li:nth-child(5),
ul:nth-child(7) li:nth-child(5),
ul:nth-child(8) li:nth-child(5){
	background:green;
}
/*back*/
ul:nth-child(1) li:nth-child(4),
ul:nth-child(2) li:nth-child(4),
ul:nth-child(3) li:nth-child(4),
ul:nth-child(4) li:nth-child(4){
	background:yellow;
}
</style>
<div>
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>
</div>
<style>
/*原来的代码*/
div{
	transform-origin: 105px 105px 55px;
}
ul{
	margin:0;
}
</style>
<script>
/*原来的代码*/
var flag = false;// 是否启动拖拽
document.addEventListener('mousedown', function() {
	flag = true;
}, false);
document.addEventListener('mouseup', function() {
	flag = false;
}, false);
var div = document.querySelector('div');
document.addEventListener('mousemove', function move(e) {
	if (!flag) {
		move.lastX = e.x;
		move.lastY = e.y;
		return;
	}
	var transform = getComputedStyle(div).getPropertyValue('transform');
	div.style.setProperty('transform', transform + 'rotateX(' + (move.lastY - e.y) / 5 + 'deg) rotateY(' + (e.x - move.lastX) / 5 + 'deg)');
	move.lastX = e.x;
	move.lastY = e.y;
}, false);
</script>
<style>
/*原来的代码*/
li{
	font-size:50px;
	line-height:100px;
	text-align:center;
	color:#00ffff;
}
</style>
<!-- 新增的代码 -->
<style id="style"></style>
<script>
// 新增的代码
function getLeftFace() {
	var cubes = document.querySelectorAll('ul');
	return [cubes[0], cubes[2], cubes[6], cubes[4]];
}
function rotateLeftFace() {
	var leftFace = getLeftFace();
	for (var i = 0; i < leftFace.length; i++) {
		var cube = leftFace[i];
		
		// 获取之前的transform
		var transform = getComputedStyle(cube).getPropertyValue('transform');
		transform == 'none' && (transform = ''); 
		var uuid = (new Date()).getTime();
		
		// 旋转原点
		var origin;
		(i == 0) && (origin = '0px 105px 55px'); 
		(i == 1) && (origin = '0px -5px 55px');
		(i == 2) && (origin = '0px -5px -55px'); 
		(i == 3) && (origin = '0px 105px -55px'); 
		 
		
		// 拼接动画样式
		style.innerHTML += 
			'.rotate' + uuid + '{'
				+ 'animation: rotate' + uuid + ' 2s linear;'
				+ 'transform-origin:' + origin
			+'} '
			+'@keyframes rotate' + uuid + '{'
				+'to{'
					+'transform:' + transform + 'rotateX(90deg);'
				+'}'
			+'} ';
		
		// 添加样式触发动画
		cube.className = 'rotate' + uuid;
	}
}
var running = false;
var style = document.querySelector('#style');
document.addEventListener('click', function(e) {
	if (e.target.nodeName.toLowerCase() == 'span') {
		if (running) return;
		rotateLeftFace();
		running = true;
	}
})
</script>
<script>
// 转动的是4个元素,只要一个监听事件即可
getLeftFace()[0].addEventListener('animationend', function end() {
	running = false;
}, false);
</script>

点击任何一块,左面都进行旋转。
但是旋转后,又恢复了原来状态。
我们需要在旋转结束后(animationend事件),轮换颜色。
即第1、3、7、5立方块轮换其颜色
1 5 => 5 7
3 7    1 3
比如
第5块的前面的颜色,变成第7块的下面的颜色。
第5块的左边的颜色,变成第7块的左边的颜色。
至于3阶魔方那就是9宫格轮换,
7阶魔方就是47宫格轮换。
这里先让我们页面支持旋转一面。

<style>
/*原来的样式*/
div{
	margin:150px 200px;
	width:100px;
	height:100px;
	transform: rotateX(-45deg) rotateY(30deg);
	transform-style:preserve-3d;
}
ul{
	width:100px;
	height:100px;
	padding:0;
	list-style:none;
	background:black;
	position:relative;
	transform-style:preserve-3d;
}
li{
	width:100px;
	height:100px;
	background:#00ffff;
	opacity:0.5;
	border:1px solid red;
	position:absolute;
	left:0;
	top:0;
}
li:nth-child(1){
	transform:translateX(-50px) rotateY(-90deg);
}
li:nth-child(2){
	transform:translateY(-50px) rotateX(90deg);
}
li:nth-child(3){
	transform:translateZ(50px) rotateY(0deg);
}
li:nth-child(4){
	transform:translateZ(-50px) rotateY(180deg);
}
li:nth-child(5){
	transform:translateY(50px) rotateX(90deg);
}
li:nth-child(6){
	transform:translateX(50px) rotateY(90deg);
}
</style>
<style>
/*原来的样式*/
div{
	position:absolute;
}
ul{
	position:absolute;
	top:0;
	left:0;
}
ul:nth-child(1){
}
ul:nth-child(2){
	transform:translateX(110px);
}
ul:nth-child(3){
	transform:translateY(110px);
}
ul:nth-child(4){
	transform:translateX(110px) translateY(110px);
}
ul:nth-child(5){
	transform:translateZ(110px);
}
ul:nth-child(6){
	transform:translateX(110px) translateZ(110px);
}
ul:nth-child(7){
	transform:translateY(110px) translateZ(110px);
}
ul:nth-child(8){
	transform:translateX(110px) translateY(110px) translateZ(110px);
}
</style>
<style>
/*原来的样式*/
body{
	background:black;
}
ul{
	background:none;
}
li{
	opacity:1;
	border:none;
}
/*left*/
ul:nth-child(1) li:nth-child(1),
ul:nth-child(3) li:nth-child(1),
ul:nth-child(5) li:nth-child(1),
ul:nth-child(7) li:nth-child(1){
	background:purple;
}
/*top*/
ul:nth-child(1) li:nth-child(2),
ul:nth-child(2) li:nth-child(2),
ul:nth-child(5) li:nth-child(2),
ul:nth-child(6) li:nth-child(2){
	background:blue;
}
/*front*/
ul:nth-child(5) li:nth-child(3),
ul:nth-child(6) li:nth-child(3),
ul:nth-child(7) li:nth-child(3),
ul:nth-child(8) li:nth-child(3){
	background:white;
}
/*right*/
ul:nth-child(2) li:nth-child(6),
ul:nth-child(4) li:nth-child(6),
ul:nth-child(6) li:nth-child(6),
ul:nth-child(8) li:nth-child(6){
	background:red;
}
/*bottom*/
ul:nth-child(3) li:nth-child(5),
ul:nth-child(4) li:nth-child(5),
ul:nth-child(7) li:nth-child(5),
ul:nth-child(8) li:nth-child(5){
	background:green;
}
/*back*/
ul:nth-child(1) li:nth-child(4),
ul:nth-child(2) li:nth-child(4),
ul:nth-child(3) li:nth-child(4),
ul:nth-child(4) li:nth-child(4){
	background:yellow;
}
</style>
<div>
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>	
	<ul>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
		<li><span>⇑</span></li>
	</ul>
</div>
<style>
/*原来的代码*/
div{
	transform-origin: 105px 105px 55px;
}
ul{
	margin:0;
}
</style>
<script>
/*原来的代码*/
var flag = false;// 是否启动拖拽
document.addEventListener('mousedown', function() {
	flag = true;
}, false);
document.addEventListener('mouseup', function() {
	flag = false;
}, false);
var div = document.querySelector('div');
document.addEventListener('mousemove', function move(e) {
	if (!flag) {
		move.lastX = e.x;
		move.lastY = e.y;
		return;
	}
	var transform = getComputedStyle(div).getPropertyValue('transform');
	div.style.setProperty('transform', transform + 'rotateX(' + (move.lastY - e.y) / 5 + 'deg) rotateY(' + (e.x - move.lastX) / 5 + 'deg)');
	move.lastX = e.x;
	move.lastY = e.y;
}, false);
</script>
<style>
/*原来的代码*/
li{
	font-size:50px;
	line-height:100px;
	text-align:center;
	color:#00ffff;
}
</style>
<!-- 原来的代码 -->
<style id="style"></style>
<script>
/*原来的代码*/
function getLeftFace() {
	var cubes = document.querySelectorAll('ul');
	return [cubes[0], cubes[2], cubes[6], cubes[4]];
}
function rotateLeftFace() {
	var leftFace = getLeftFace();
	for (var i = 0; i < leftFace.length; i++) {
		var cube = leftFace[i];
		
		// 获取之前的transform
		var transform = getComputedStyle(cube).getPropertyValue('transform');
		transform == 'none' && (transform = ''); 
		var uuid = (new Date()).getTime();
		
		// 旋转原点
		var origin;
		(i == 0) && (origin = '0px 105px 55px'); 
		(i == 1) && (origin = '0px -5px 55px');
		(i == 2) && (origin = '0px -5px -55px'); 
		(i == 3) && (origin = '0px 105px -55px'); 
		 
		
		// 拼接动画样式
		style.innerHTML += 
			'.rotate' + uuid + '{'
				+ 'animation: rotate' + uuid + ' 2s linear;'
				+ 'transform-origin:' + origin
			+'} '
			+'@keyframes rotate' + uuid + '{'
				+'to{'
					+'transform:' + transform + 'rotateX(90deg);'
				+'}'
			+'} ';
		
		// 添加样式触发动画
		cube.className = 'rotate' + uuid;
	}
}
var running = false;
var style = document.querySelector('#style');
document.addEventListener('click', function(e) {
	if (e.target.nodeName.toLowerCase() == 'span') {
		if (running) return;
		rotateLeftFace();
		running = true;
	}
})
</script>
<script>
/*新增的代码*/
function changeColor() {
	
	// 获取或设置颜色
	var c = function color(ul, face, color) {
		if (!color) {
			return getComputedStyle(ul.querySelector('li:nth-child(' + face + ')')).getPropertyValue('background-color');
		}
		ul.querySelector('li:nth-child(' + face + ')').style.setProperty('background-color', color);
	};
	
	var f = getLeftFace();
	// 边 分别为前上后下
	var sides = [[f[3], f[2], 3], [f[0], f[3], 2], [f[1], f[0], 4], [f[2], f[1], 5]];
	var sideColors = sides.map(function(pair) {
		return [c(pair[0], pair[2]), c(pair[1], pair[2])];
	});
	sides.push(sides[0]);
	
	// 轮换侧边颜色
	for (var i = 1; i < sides.length; i++) {
		c(sides[i][0], sides[i][2], sideColors[i - 1][0]);
		c(sides[i][1], sides[i][2], sideColors[i - 1][1]);
	}
	
	
	var leftColors = f.map(function(ul) {
		return c(ul, 1);
	});
	f.push(f[0]);
	
	// 轮换左边颜色
	for (var i = 1; i < f.length; i++) {
		c(f[i], 1, leftColors[i - 1]);
	}
	
}
// 转动的是4个元素,只要一个监听事件即可
getLeftFace()[0].addEventListener('animationend', function end() {
	changeColor();
	running = false;
}, false);
</script>

上面代码中的changeColor不是通用算法,哪怕针对2阶来说。
但是意思讲明白了,是怎么旋转左面的,魔方的左面的颜色要轮换,四边颜色也要轮换。

2和3阶原理类似。对于任意阶也是有通用的算法。
比如7阶,那么就是49宫格轮换。轮换算法是什么呢?
想象一下49宫格是一个正方形的披萨,连接两个对角线,把披萨分成了四大块,然后这四大块进行轮换。
也就是说任意阶都可以简化成2阶的问题(待续)。

第七步,完整代码

旋转一面我们会了,那么旋转任意一面,还差什么呢?
每个小块上面有四个箭头,
我们要点击一个箭头时,我们要确定是旋转哪个面,是正向旋转还是负向旋转。其实就是一个映射问题。
这块相对来说,简单一些,可以自己想想。待续。。。

毕竟三阶魔方已经实现了,请点击我

本文待续。。。
z
给个赞 36 人点赞
收藏 33 人收藏
评论 已有 20 条评论;以下用户言论只代表其个人观点,不代表 前端网(QDFuns) 的观点或立场。
登录 以后才能发表评论
最热评论
老姚
老姚2016-11-10 21:07:172F
用chrome看,没有考虑兼容的情况。 //@前端李李:非常不错  但是还是有很多bug   emoticon
举报 支持 (1) 回复 (0)
最新评论
Joudee
Joudee2017-02-09 17:54:1620F
怎么感觉拖的方向好奇怪
举报 支持 (0) 回复 (0)
kkstrive
kkstrive2016-11-29 09:40:0419F
学习来了
举报 支持 (0) 回复 (0)
WindyRain_Happy
WindyRain_Happy2016-11-21 13:56:2218F
打乱后转到第三层,一步错了没转回去,累觉不爱,真想要一个撤销滚动的按钮啊!!!
举报 支持 (0) 回复 (0)
pure_lug
pure_lug2016-11-14 09:07:4317F
赞一个,收藏一个,然后评论一下。emoticon
举报 支持 (0) 回复 (0)
良民
良民2016-11-11 17:28:4116F
玩了那么久三阶魔方,却没玩过二阶的 //@老姚:不是愚昧,是晕了,哈哈。平面是2*2,立体是2*2*2,因此是8个。3阶是27个。 //@良民:原谅我的愚昧,二阶魔方为什么有8个立方体
举报 支持 (0) 回复 (0)
rollcat
rollcat2016-11-11 13:37:5315F
谢谢这么好的笔记。
举报 支持 (0) 回复 (0)
老姚
老姚2016-11-11 12:04:1114F
谢谢支持 //@hbxywdk:通俗易懂
举报 支持 (0) 回复 (0)
老姚
老姚2016-11-11 12:03:5213F
先将究着看emoticon //@brucelyy:眼花花了emoticon
举报 支持 (0) 回复 (0)
老姚
老姚2016-11-11 12:02:4212F
不是愚昧,是晕了,哈哈。平面是2*2,立体是2*2*2,因此是8个。3阶是27个。 //@良民:原谅我的愚昧,二阶魔方为什么有8个立方体
举报 支持 (0) 回复 (1)
老姚
老姚2016-11-11 12:01:0011F
谢谢支持 //@阿波罗D波:老姚,棒棒的
举报 支持 (0) 回复 (0)
老姚
老姚2016-11-11 12:00:3610F
到时帮我找找那个bug,有时进来,旋转时,并没有按照变换原点,去旋转。 //@hugeannex:没时间看。收藏先。再赞一个
举报 支持 (0) 回复 (0)
hbxywdk
hbxywdk2016-11-11 11:32:229F
通俗易懂
举报 支持 (0) 回复 (1)
良民
良民2016-11-11 11:00:258F
好吧,我还以为只有四个立方体emoticon //@良民:原谅我的愚昧,二阶魔方为什么有8个立方体
举报 支持 (0) 回复 (0)
brucelyy
brucelyy2016-11-11 11:00:127F
眼花花了emoticon
举报 支持 (0) 回复 (1)
良民
良民2016-11-11 10:58:136F
原谅我的愚昧,二阶魔方为什么有8个立方体
举报 支持 (0) 回复 (2)
老姚 老姚 作者

与自己为敌,与自己为友,一边深挖思想,一边埋葬自己。

作者最新