魔方魔方可以旋转的魔方
<!DOCTYPE html> <html lang="zh-cn"> <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no, width=device-width" /> <meta charset="UTF-8"> <title>我也来画个魔方</title> <style> body{ background:black; overflow:hidden; transform-style:preserve-3d; } .container{ width:100px; height:100px; position:absolute; transform-style:preserve-3d; transform:rotateX(-45deg) rotateY(30deg) translate(-50%,-50%); top:40%; left:50%; } ul{ list-style:none; padding-left:0px; width:100px; height:100px; position:absolute; transform-style:preserve-3d; } ul li{ width:100px; height:100px; position:absolute; background:gray; } ul li:nth-of-type(1) { transform:translateX(-50px) rotateY(-90deg); } ul li:nth-of-type(2) { transform:translateY(-50px) rotateX(90deg); } ul li:nth-of-type(3) { transform:translateZ(50px) rotateY(0deg); } ul li:nth-of-type(4) { transform:translateZ(-50px) rotateY(180deg); } ul li:nth-of-type(5) { transform:translateY(50px) rotateX(90deg); } ul li:nth-of-type(6) { transform:translateX(50px) rotateY(90deg); } /*left face*/ ul[data-x="-1"] li:nth-of-type(1){ background:purple; } /*right face*/ ul[data-x="1"] li:nth-of-type(6){ background:red; } /*top face*/ ul[data-y="-1"] li:nth-of-type(2){ background:blue; } /*bottom face*/ ul[data-y="1"] li:nth-of-type(5){ background:green; } /*front face*/ ul[data-z="1"] li:nth-of-type(3){ background:white; } /*back face*/ ul[data-z="-1"] li:nth-of-type(4){ background:yellow; } ul li span{ color:silver; font-size:30px; display:block; width:30px; height:30px; position:absolute; text-align:center; line-height:30px; display:none; } ul li span:nth-of-type(1){ top:35px; left:10px; } ul li span:nth-of-type(2){ top:10px; left:35px; } ul li span:nth-of-type(3){ top:35px; right:10px; } ul li span:nth-of-type(4){ bottom:10px; left:35px; } ul li:hover span{ display:block; } ul li:hover span:hover{ cursor:pointer; } </style> <style id="keyframes"></style> </head> <body> <div class="container"></div> <script type="text/template" id="template"> <li> <span data-action="left">⇐</span> <span data-action="up">⇑</span> <span data-action="right">⇒</span> <span data-action="down">⇓</span> </li> </script> <script> var cube = { init: function() { // 生成dom节点 var container = document.querySelector('.container'); var template = document.querySelector('#template'); var indexs = [-1, 0, 1]; var num = 1; this.blocks = []; for (var k = 0; k < 3; k++) { for (var j = 0; j < 3; j++) { for (var i = 0; i < 3; i++) { var ul = document.createElement('ul'); var transform = ['translateX(', indexs[i] * 110, 'px)', 'translateY(', indexs[j] * 110, 'px)', 'translateZ(', indexs[k] * 110, 'px)'].join(''); ul.style.setProperty('transform', transform); ul.setAttribute('data-num', num++); ul.setAttribute('data-x', indexs[i]); ul.setAttribute('data-y', indexs[j]); ul.setAttribute('data-z', indexs[k]); ul.setAttribute('data-coordinate', [indexs[i], indexs[j], indexs[k]].join('')); ul.x = indexs[i]; ul.y = indexs[j]; ul.z = indexs[k]; var html = template.innerHTML; var htmls = [html, html, html, html, html, html]; ul.innerHTML = htmls.join(''); this.blocks.push(ul); container.appendChild(ul); } } } this.bindEvent(); }, bindEvent: function() { var self = this; document.addEventListener('click', function(e) { var target = e.target; if (target.nodeName.toLowerCase() == 'span') { var action; (action = target.getAttribute('data-action')) && this.actionDirector(target.parentNode, action); e.stopPropagation(); } }.bind(this)); }, actionDirector: function(li, action) { var ul = li.parentNode; var index = [].indexOf.call(ul.children, li); var mapping = ['left', 'top', 'front', 'back', 'bottom', 'right']; var faceName = mapping[index]; var configs = { left: { up: { axis: 'z', direction: 1 }, down: { axis: 'z', direction: -1 }, left: { axis: 'y', direction: -1 }, right: { axis: 'y', direction: 1 } }, right: { up: { axis: 'z', direction: -1 }, down: { axis: 'z', direction: 1 }, left: { axis: 'y', direction: -1 }, right: { axis: 'y', direction: 1 } }, top: { up: { axis: 'x', direction: 1 }, down: { axis: 'x', direction: -1 }, left: { axis: 'z', direction: -1 }, right: { axis: 'z', direction: 1 } }, bottom: { up: { axis: 'x', direction: -1 }, down: { axis: 'x', direction: 1 }, left: { axis: 'z', direction: 1 }, right: { axis: 'z', direction: -1 } }, front: { up: { axis: 'x', direction: 1 }, down: { axis: 'x', direction: -1 }, left: { axis: 'y', direction: -1 }, right: { axis: 'y', direction: 1 } }, back: { up: { axis: 'x', direction: -1 }, down: { axis: 'x', direction: 1 }, left: { axis: 'y', direction: -1 }, right: { axis: 'y', direction: 1 } } }; var config = configs[faceName][action]; this.changeKeyframes(config.axis, ul[config.axis], config.direction); this.rotateFace(config.axis, ul[config.axis], config.direction).forEach(function(block) { block.className = 'rotate' + block.getAttribute('data-coordinate'); }) }, changeKeyframes: function(axis, index, direction) { var face = this.getFace(axis, index); var style = document.querySelector('#keyframes'); style.innerHTML = ""; for (var i = 0; i < face.length; i++) { var transform = getComputedStyle(face[i]).getPropertyValue('transform'); var x = face[i].x; var y = face[i].y; var z = face[i].z; var className = "rotate" + face[i].getAttribute('data-coordinate'); var origin = { x: [(-x) * 110 + 50, (-y) * 110 + 50, (-z) * 110], } var origin = origin.x[0] + 'px ' + origin.x[1] + 'px ' + origin.x[2] + 'px '; style.innerHTML += "." + className + " { animation: " + className + " 1s linear backwards; transform-origin: " + origin + "} @keyframes " + className + "{ to{ transform:" + transform + " rotate" + axis.toUpperCase() + "(" + direction * 90 + "deg); } }" } }, // 获取一面 比如x轴第1面 axis = 'x' index = 1 getFace: function(axis, index) { return this.blocks.filter(function(block) { return block[axis] == index; }); }, // 获取一条 getBar: function(axis, index, faceName) { var face = this.getFace(axis, index); var configs = { x: { front: { axis: 'z', index: 1, sortAxis: 'y', sortDirection: 1, faceIndex: 3 }, back: { axis: 'z', index: -1, sortAxis: 'y', sortDirection: -1, faceIndex: 4 }, top: { axis: 'y', index: -1, sortAxis: 'z', sortDirection: 1, faceIndex: 2 }, bottom: { axis: 'y', index: 1, sortAxis: 'z', sortDirection: -1, faceIndex: 5 } }, y: { front: { axis: 'z', sortAxis: 'x', sortDirection: 1, index: 1, faceIndex: 3 }, back: { axis: 'z', sortAxis: 'x', sortDirection: -1, index: -1, faceIndex: 4 }, left: { axis: 'x', sortAxis: 'z', sortDirection: 1, index: -1, faceIndex: 1 }, right: { axis: 'x', sortAxis: 'z', sortDirection: -1, index: 1, faceIndex: 6 } }, z: { top: { axis: 'y', sortAxis: 'x', sortDirection: 1, index: -1, faceIndex: 2 }, bottom: { axis: 'y', sortAxis: 'x', sortDirection: -1, index: 1, faceIndex: 5 }, left: { axis: 'x', sortAxis: 'y', sortDirection: -1, index: -1, faceIndex: 1 }, right: { axis: 'x', sortAxis: 'y', sortDirection: 1, index: 1, faceIndex: 6 } } }; var config = configs[axis][faceName]; return face.filter(function(block) { return block[config.axis] == config.index; }).sort(function(a, b) { if (a[config.sortAxis] < b[config.sortAxis]) { return -1 * config.sortDirection; } if (a[config.sortAxis] > b[config.sortAxis]) { return 1 * config.sortDirection; } return 0; }).map(function(block) { var a, b; (axis == 'x') && (a = 1, b = 6); (axis == 'y') && (a = 2, b = 5); (axis == 'z') && (a = 3, b = 4); return [block.querySelector('li:nth-of-type(' + config.faceIndex + ')'), block.querySelector('li:nth-of-type(' + a + ')'), block.querySelector('li:nth-of-type(' + b + ')') ]; }); }, // axis表示坐标轴 'x'、'y'、'z' // index-1 0 1 左中右 // direction 1 -1 旋转方向 rotateFace: function(axis, index, direction) { var results = this.getFace(axis, index); var self = this; var length = results.length; var block = results[length - 1]; block.end && block.removeEventListener('animationend', block.end, false); block.addEventListener('animationend', block.end = function rotate() { if (rotate.already) return; rotate.already = true; self.changeColor(axis, index, direction); }, false); return results; }, changeColor: function(axis, index, direction) { var face = this.getFace(axis, index); var configs = { x :['front', 'top', 'back', 'bottom', 'front'], y: ['front', 'right', 'back', 'left', 'front'], z: ['top', 'right', 'bottom', 'left', 'top'] }; var config = (direction == 1) ? configs[axis] : configs[axis].reverse(); var self = this; var arr = config.map(function(faceName) { return self.getBar(axis, index, faceName); }); var colors = arr.map(function(array) { return array.map(function(lists) { return lists.map(function(li) { return getComputedStyle(li).getPropertyValue('background-color'); }) }); }); arr.forEach(function(array, i) { array.forEach(function(list, j) { list.forEach(function(li, k) { if ((i > 0) ) { li.style.setProperty('background-color', colors[i - 1][j][k]) } }); }); }); } }; cube.init(); </script> <script> var flag; function direction(e) { var position = direction.position = direction.position || {x: 0, y: 0}; var x = e.x; var y = e.y; var r = {}; r.x = Math.abs(y - position.y) < 2 ? 0 : y < position.y ? 1 : -1; r.y = Math.abs(x - position.x) < 2 ? 0 : x < position.x ? -1 : 1; direction.position.x = x; direction.position.y = y; return r; } var container = document.querySelector('.container'); document.addEventListener('mousemove', function(e) { if (!flag) return; var transform = getComputedStyle(container).getPropertyValue('transform'); var r = direction(e); container.style.setProperty('transform', transform + 'rotateX(' + r.x * 2 + 'deg) rotateY(' + r.y * 1.5 + 'deg)'); }, false); document.addEventListener('mousedown', function(e) { flag = true; }, false); document.addEventListener('mouseup', function(e) { flag = false; }, false); </script> </body> </html>