html 代码
- <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>
- <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>
这点代码用了三天, 才写出来
自从知道 css3 的 3d 变换技术以来, 就一直想如何实现个魔方
后来在本站上等了一年, 确实有人画了出来点击这里 https://notes/22725/61b3de4534ce2ac29a2da9a4943bc3d5.html
当时看了之后确实感觉到振奋, 心想心中一个愿望, 终于实现了虽然是别人先做成的
前两天又看了一下, 了解其原理后, 觉得很简单, 于是决定自己动手搞个
现在搞完了, 只想说一句, 谁做谁知道
本文并没有怎么参考其代码 (看了 10 分钟, 没看进去, 人家算法是支持任意阶的), 按照自己的思路实现的
三个通宵, 到现在还没怎么睡好过一闭眼就想这个, 脑海里始终有个魔方在那旋转着
用到的技术
1. 基本 3d 变换
终于熟悉了 3d 旋转方向规则, 左手定则!
2. 动画
keyframe 是样式规则
如果里面有变量怎么办呢?
js 动态生成样式的问题
3. 魔方知识
实现原理, 正如那篇文章里说的那样其实元素并没有动, 只是换了颜色
4. 认识到了前端动画技术有待发展啊
谁做谁知道啊, 太局限了
比如, animation 熟悉设置 forwards 可以保持动画最终效果, 但是并没有提供新的位置信息
5. 代码风格稍微简单一些
用了策略模式和模板, 没怎么封装, 只是为了实现效果去的
6. 有待完善的地方
没有搞事件队列
代码有待优化肯定有优化的算法,
当时只想实现 3 阶魔方来的
貌似终于可以好好睡觉了,
最近 72 小时里, 只睡了 12 小时, 实在睡不着我已经入魔, 大脑不转时就拿手比划
期间除了吃饭和重温一下阿凡达之外, 其余都在研究这个东西, 5% 的时间写代码, 各种调试艾玛~~
其实有时一些事情, 你做完之后, 会觉得这么简单的方式, 怎么没有一开始咋就没想到呢!
个人觉得魔方这个东西可以作为你的一个小练习
我现在在想, 能把这个东西做成教学视频那就好了
最后来句,
纸上得来终觉浅, 绝知此事要躬行
本文完
来源: http://www.qdfuns.com/article/17398/0a2a01df7580639d8e681b25beb2993d.html