纯HTML+JS+CSS动画写的3D魔方 (实现了简单的旋转操作)
直接看效果, 这是正常的旋转

但转着转着就乱了^_^

下面直接上代码:
HTML部分:
<!DOCTYPE html><html><head><meta charset="utf-8" /></head><body> <div class="cubes" id="cubes"></div></body></html>*{pxdding:0;margin:0;}.cubes{position:relative;z-index:0;left:0px;top:270px;transform-style: preserve-3d;transform: rotateX(-30deg) rotateY(-30deg);}.cube {position:absolute;left:0;top:0;width: 100px;height: 100px;transform-style: preserve-3d;}.cube span {box-sizing:border-box;border:5px solid #000;border-radius:10px;display: block;width: 100px;height: 100px;position: absolute;font-size:30px;text-align:center;line-height:100px;cursor:default;}.cube .in_front {background:green;transform: rotateY(0deg) translateZ(50px);color:#fff;}.cube .in_back {background:yellow;transform: translateZ(-50px) rotateY(180deg);}.cube .in_left {background:cyan;transform: rotateY(-90deg) translateZ(50px);}.cube .in_right {background:#fcf;transform: rotateY(90deg) translateZ(50px);}.cube .in_top {background:purple;translateY(50px);transform: rotateX(90deg) translateZ(50px);color:#fff;}.cube .in_bottom {background:red;transform: rotateX(-90deg) translateZ(50px);color:#fff}
var tmpl = `<div class="cube" id="{ID}"><span class="in_front">{F}</span><span class="in_back">{B}</span><span class="in_left">{L}</span><span class="in_right">{R}</span><span class="in_top">{T}</span><span class="in_bottom">{M}</span></div>`;var str = '';var txts = { '020' : '9←', '050' : '6←', '080' : '3←', '120' : '8←', '150' : '5←', '180' : '2←', '220' : '7←', '250' : '4←', '280' : '1←', // white '001' : 'S↓', '031' : 'T↓', '061' : 'U↓', '101' : 'V↓', '131' : '*↓', '161' : 'W↓', '201' : 'X↓', '231' : 'Y↓', '261' : 'Z↓', // cyan '002' : 'L←', '012' : 'K←', '022' : 'J←', '102' : 'O←', '112' : 'N←', '122' : 'M←', '202' : 'R←', '212' : 'Q←', '222' : 'P←', // purple '063' : 'A→', '073' : 'B→', '083' : 'C→', '163' : 'D→', '173' : 'E→', '183' : 'F→', '263' : 'G→', '273' : 'G→', '283' : 'I→', // red '004' : '9←', '014' : '8←', '024' : '7←', '034' : '6←', '044' : '5←', '054' : '4←', '064' : '3←', '074' : '2←', '084' : '1←', // green '225' : '9←', '215' : '8←', '205' : '7←', '255' : '6←', '245' : '5←', '235' : '4←', '285' : '3←', '275' : '2←', '265' : '1←' // yellow };var cutewidth = 100; //正方形for(i=0; i<27; i++){ x = parseInt(i / 9); y = i % 9; l = y % 3; t = parseInt(y / 3); tx = l*cutewidth; ty = t*cutewidth; tz = -x*cutewidth; id = "cube"+ i +"\" tx='"+tx+"' ty='"+ty+"' tz='"+tz+"' rx='0' ry='0' rz='0' style=\"transform:translateX("+tx+"px) translateY("+ty+"px) translateZ("+tz+"px);"; str += tmpl.replace("{ID}", id).replace("{F}",txts[''+x+y+'4']?txts[''+x+y+'4']:'').replace("{B}",txts[''+x+y+'5']?txts[''+x+y+'5']:'').replace("{L}",txts[''+x+y+'1']?txts[''+x+y+'1']:'').replace("{R}",txts[''+x+y+'0']?txts[''+x+y+'0']:'').replace("{T}",txts[''+x+y+'2']?txts[''+x+y+'2']:'').replace("{M}",txts[''+x+y+'3']?txts[''+x+y+'3']:'');}$('cubes').innerHTML = str;$('cubes').onselectstart = function(){return false;}function $(id){return document.getElementById(id);}var mousex = -1, mousey = -1, mousepid = '';$('cubes').addEventListener('mousedown', mouseDown);$('cubes').addEventListener('mousemove', mouseUp);//$('cubes').addEventListener('touchstart', mouseDown, { passive: false });//$('cubes').addEventListener('touchmove', touchmove, { passive: false });//$('cubes').addEventListener('touchend', mouseUp, { passive: false });function mouseDown(e){ if (e.type === 'touchstart') { e.preventDefault(); e = e.touches[0]; } var tag = e.target.tagName.toLowerCase(); var pid = e.target.parentNode.id; var prect = $('cubes').parentNode.getBoundingClientRect(); if((tag == 'span') && (pid.substring(0,4) == 'cube')){ mousex = e.clientX - prect.left; mousey = e.clientY - prect.top; mousepid = pid;//console.log('['+mousex+','+mousey+']'); //可用于下面chkInReck四角定位用 }}var nowe = null;function touchmove(e){ nowe = e.touches[0];}function mouseUp(e) { if (e.type === 'touchend') { e.preventDefault(); e = nowe; } if(mousex < 0 || mousey < 0) return; //不在魔方内 var x = e.clientX - $('cubes').parentNode.parentNode.getBoundingClientRect().left - mousex; var y = e.clientY - $('cubes').parentNode.parentNode.getBoundingClientRect().top - mousey; if((Math.abs(x) < 5) && (Math.abs(y) < 5)) return; //小范围移动当成点击 var d, f, a, b, c, p; //方向,面, abc临时变量 var pid = parseInt(mousepid.replace('cube','')); f = chkInRect(mousex,mousey); a = pid % 3; //右中右三层 b = parseInt(pid / 9); //前中后三层 c = parseInt((pid % 9) / 3); //上中下三层 mousex = mousey = -1; mousepid = ''; if(Math.abs(x) <= Math.abs(y)) { if(y < 0){ d = 'duu'; } else { d = 'dud'; } //上下移动 }else{ if(x > 0){ d = 'rlr'; } else { d = 'rll'; } //左右移动 } if(f == '') { return; } if(f == 'f'){ if(d[0] == 'r'){ if(c == 0){ p = 't'; } if(c == 1){ p = 'm'; } if(c == 2){ p = 'b'; } }else{ if(a == 0){ p = 'l'; } if(a == 1){ p = 'm'; } if(a == 2){ p = 'r'; } } } if((f == 'r')){ if(d[0] == 'r'){ if(c == 0){ p = 't'; } if(c == 1){ p = 'm'; } if(c == 2){ p = 'b'; } }else{ if(b == 0){ p = 'l'; } if(b == 1){ p = 'm'; } if(b == 2){ p = 'r'; } } } if(f == 't'){ if(d[0] == 'r'){ if(b == 0){ p = 'b'; } //顶层后层作为上 if(b == 1){ p = 'm'; } //中 if(b == 2){ p = 't'; } //顶层前层作为下 }else{ if(a == 0){ p = 'l'; } if(a == 1){ p = 'm'; } if(a == 2){ p = 'r'; } } } fun = ''+f+p+d[2]+'3d'; console.log(fun); exec = fun+'();'; if(window.hasOwnProperty(fun)) eval(exec);}function chkInRect(x, y){ //注意: 网页坐标与数学坐标有些区别, 网页坐标是左上角为[0,0], 数学坐标是左下角是[0,0], // 所以数学坐标图形是网页坐标图形的垂直镜像(向上翻就是了) var arrt = [[161,18]/*上*/,[411,90]/*右*/,[269,213]/*下*/,[19,142]/*左*/]; //top var arrf = [[17,142]/*上左*/,[268,215]/*上右*/,[268,465]/*下右*/,[17,394]/*下左*/]; //front var arrr = [[276,213]/*上左*/,[420,90]/*上右*/,[420,340]/*下右*/,[276,463]/*下左*/]; //right if((x > arrf[0][0]) && (x < arrf[1][0])){ //front y0 = parseInt(arrf[0][1] + (arrf[1][1] - arrf[0][1]) * (x - arrf[0][0]) / (arrf[1][0] - arrf[0][0])); y1 = parseInt(arrf[2][1] - (arrf[2][1] - arrf[3][1]) * (arrf[2][0] - x) / (arrf[2][0] - arrf[3][0])); if((y >= y0) && (y <= y1)) return 'f'; } else if((x > arrr[0][0]) && (x < arrr[1][0])){ //right y0 = parseInt(arrr[0][1] - (arrr[0][1] - arrr[1][1]) * (x - arrr[0][0]) / (arrr[1][0] - arrr[0][0])); y1 = parseInt(arrr[3][1] - (arrr[3][1] - arrr[2][1]) * (x - arrr[3][0]) / (arrr[2][0] - arrr[3][0])); if((y >= y0) && (y <= y1)) return 'r'; } //如果不在前右,再试一下是否在上面 if((x > arrt[3][0]) && (x < arrt[1][0]) && (y > arrt[0][1]) && (y < arrt[2][1])){ //top 在菱形所在外切长方形内 //只要点不在菱形与外切长方形形成4个外三角形内就说明在菱形内了 if((x > arrt[0][0]) && (x < arrt[1][0])){ //边1 y0 = arrt[0][1]; y1 = y0 + (x - arrt[0][0]) * (arrt[1][1] - arrt[0][1]) / (arrt[1][0] - arrt[0][0]); if((y > y0) && (y < y1)) return ''; } if((x >arrt[2][0]) && (x < arrt[1][0])){ //边2 y1 = arrt[2][1]; y0 = y1 - (x - arrt[2][0]) * (arrt[2][1] - arrt[1][1]) / (arrt[1][0] - arrt[2][0]); if((y > y0) && (y < y1)) return ''; } if((x >arrt[2][0]) && (x < arrt[1][0])){ //边3 y1 = arrt[2][1]; y0 = y1 - (arrt[2][0] - x) * (arrt[2][1] - arrt[3][1]) / (arrt[2][0] - arrt[3][0]); if((y > y0) && (y < y1)) return ''; } if((x >arrt[2][0]) && (x < arrt[1][0])){ //边4 y0 = arrt[0][0]; y1 = y0 + (arrt[0][0] - x) * (arrt[3][1] - arrt[0][1]) / (arrt[0][0] - arrt[3][0]); if((y > y0) && (y < y1)) return ''; } //其它则在菱形内了 return 't'; } return '';}//3*3二维数组旋转90度function rotate90(arr){ n = 3; var arr1 = []; for(i=0; i<n; i++){ for(j=0; j<n; j++){ arr1[j * n + n - 1 - i] = arr[i * n + j]; }} return arr1;}//x面操作function fld3d(){ var elsn = [0,9,18,3,12,21,6,15,24]; roundx(elsn, 0);}function flu3d(){ var elsn = [0,9,18,3,12,21,6,15,24]; roundx(elsn, 1);}function fmd3d(){ var elsn = [1,10,19,4,13,22,7,16,25]; roundx(elsn, 0);}function fmu3d(){ var elsn = [1,10,19,4,13,22,7,16,25]; roundx(elsn, 1);}function frd3d(){ var elsn = [2,11,20,5,14,23,8,17,26]; roundx(elsn, 0);}function fru3d(){ var elsn = [2,11,20,5,14,23,8,17,26]; roundx(elsn, 1);}//y面操作function ftr3d(){ var elsn = [0,1,2,9,10,11,18,19,20]; roundy(elsn, 0);}function ftl3d(){ var elsn = [0,1,2,9,10,11,18,19,20]; roundy(elsn, 1);}function fmr3d(){ var elsn = [3,4,5,12,13,14,21,22,23]; roundy(elsn, 0);}function fml3d(){ var elsn = [3,4,5,12,13,14,21,22,23]; roundy(elsn, 1);}function fbr3d(){ var elsn = [6,7,8,15,16,17,24,25,26]; roundy(elsn, 0);}function fbl3d(){ var elsn = [6,7,8,15,16,17,24,25,26]; roundy(elsn, 1);}//z面操作function rld3d(){ var elsn = [0,1,2,3,4,5,6,7,8]; roundz(elsn, 0);}function rlu3d(){ var elsn = [0,1,2,3,4,5,6,7,8]; roundz(elsn, 1);}function rmd3d(){ var elsn = [9,10,11,12,13,14,15,16,17]; roundz(elsn, 0);}function rmu3d(){ var elsn = [9,10,11,12,13,14,15,16,17]; roundz(elsn, 1);}function rrd3d(){ var elsn = [18,19,20,21,22,23,24,25,26]; roundz(elsn, 0);}function rru3d(){ var elsn = [18,19,20,21,22,23,24,25,26]; roundz(elsn, 1);}function rtr3d(){ftr3d();}function rtl3d(){ftl3d();}function rmr3d(){fmr3d();}function rml3d(){fml3d();}function rbr3d(){fbr3d();}function rbl3d(){fbl3d();}function ttr3d(){rrd3d();}function ttl3d(){rru3d();}function tmr3d(){rmd3d();}function tml3d(){rmu3d();}function tbr3d(){rld3d();}function tbl3d(){rlu3d();}function tld3d(){fld3d();}function tlu3d(){flu3d();}function tmd3d(){fmd3d();}function tmu3d(){fmu3d();}function trd3d(){frd3d();}function tru3d(){fru3d();}function chgId(elsn, zf){ //id更换, 保证旋转后的id还是从0~27 var elsn1 = rotate90(elsn); if(zf == 1){ elsn1 = rotate90(elsn1); elsn1 = rotate90(elsn1);} //总转3次,相当于反转一次 for (var i=0; i<elsn.length; i++){ $('cube'+elsn1[i]).id = 'cube'+elsn[i]+'n'; } for (var i=0; i<elsn.length; i++){ $('cube'+elsn[i]+'n').id = 'cube'+elsn[i]; }}function roundx(elsn, zf){ var ss = document.styleSheets; for (var i=0; i<elsn.length; i++){ const newDiv = $('cube'+elsn[i]); tx = parseInt(newDiv.getAttribute('tx')); ty = parseInt(newDiv.getAttribute('ty')); tz = parseInt(newDiv.getAttribute('tz')); rx = parseInt(newDiv.getAttribute('rx')); ry = parseInt(newDiv.getAttribute('ry')); rz = parseInt(newDiv.getAttribute('rz')); newDiv.style.transformOrigin = "150px 150px -100px"; rx1 = rx + (zf==1?90:-90); //这个是反的 newDiv.setAttribute('rx', rx1); css = `@keyframes rx${elsn[i]}${zf}{from{transform:rotateX(${rx}deg) rotateY(${ry}deg) rotateZ(${rz}deg) translateX(${tx}px) translateY(${ty}px) translateZ(${tz}px)}to{transform:rotateX(${rx1}deg) rotateY(${ry}deg) rotateZ(${rz}deg) translateX(${tx}px) translateY(${ty}px) translateZ(${tz}px)}}`; ss[0].insertRule(css); newDiv.style.animation = '0.3s linear 0s 1 normal forwards running rx'+elsn[i]+zf; //console.log(document.styleSheets[0]); setTimeout(function(id,zf){ //动画执行完后,转为css样式,保留住最终状态 cssn = 'rx'+id+zf; var rules = getkeyframes(cssn); rules.styleSheet.deleteRule(rules.index); css = rules.cssRule.cssText.substring(rules.cssRule.cssText.indexOf('100% {')+6); css = css.substring(0, css.indexOf('}'))+'transform-origin:150px 150px -100px'; $('cube'+id).style.cssText = css; }, 300, elsn[i], zf); //与动画时间一致 } setTimeout((elsn,zf)=>{chgId(elsn,zf);}, 350, elsn, zf); //这个时间要比上面动画时间要长一点}function roundy(elsn, zf){ var ss = document.styleSheets; for (var i=0; i<elsn.length; i++){ const newDiv = $('cube'+elsn[i]); tx = parseInt(newDiv.getAttribute('tx')); ty = parseInt(newDiv.getAttribute('ty')); tz = parseInt(newDiv.getAttribute('tz')); rx = parseInt(newDiv.getAttribute('rx')); ry = parseInt(newDiv.getAttribute('ry')); rz = parseInt(newDiv.getAttribute('rz')); newDiv.style.transformOrigin = '150px 150px -100px'; ry1 = ry + (zf==1?-90:90); newDiv.setAttribute('ry', ry1); css = `@keyframes ry${elsn[i]}${zf}{from{transform:rotateX(${rx}deg) rotateY(${ry}deg) rotateZ(${rz}deg) translateX(${tx}px) translateY(${ty}px) translateZ(${tz}px)}to{transform:rotateX(${rx}deg) rotateY(${ry1}deg) rotateZ(${rz}deg) translateX(${tx}px) translateY(${ty}px) translateZ(${tz}px)}}`; ss[0].insertRule(css); newDiv.style.animation = '0.3s linear 0s 1 normal forwards running ry'+elsn[i]+zf; //console.log(document.styleSheets[0]); setTimeout(function(id,zf){ //动画执行完后,转为css样式,保留住最终状态 cssn = 'ry'+id+zf; var rules = getkeyframes(cssn); rules.styleSheet.deleteRule(rules.index); css = rules.cssRule.cssText.substring(rules.cssRule.cssText.indexOf('100% {')+6); //取出最终状态样式 css = css.substring(0, css.indexOf('}'))+'transform-origin:150px 150px -100px'; $('cube'+id).style.cssText = css; }, 300, elsn[i], zf); } setTimeout((elsn,zf)=>{chgId(elsn,zf);}, 350, elsn, zf); //这个时间要比上面动画时间要长一点}function roundz(elsn, zf){ //elsn:对象, zf正反转: 0,1 var ss = document.styleSheets; for (var i=0; i<elsn.length; i++){ const newDiv = $('cube'+elsn[i]); tx = parseInt(newDiv.getAttribute('tx')); ty = parseInt(newDiv.getAttribute('ty')); tz = parseInt(newDiv.getAttribute('tz')); rx = parseInt(newDiv.getAttribute('rx')); ry = parseInt(newDiv.getAttribute('ry')); rz = parseInt(newDiv.getAttribute('rz')); rz1 = rz+(zf==1?-90:90); newDiv.style.transformOrigin = '150px 150px 0'; newDiv.setAttribute('rz', rz1); //newDiv.style.animation = 'rz'+elsn[i]+goZ+zf+' 0.3s linear infinite'; //newDiv.style.animationIterationCount = 1; //动画只执行一次 //newDiv.style.animationFillMode = 'forwards'; //动画结束后停留在最后一帧 css = `@keyframes rz${elsn[i]}${zf}{from{transform:rotateX(${rx}deg) rotateY(${ry}deg) rotateZ(${rz}deg) translateX(${tx}px) translateY(${ty}px) translateZ(${tz}px)}to{transform:rotateX(${rx}deg) rotateY(${ry}deg) rotateZ(${rz1}deg) translateX(${tx}px) translateY(${ty}px) translateZ(${tz}px)}}`; ss[0].insertRule(css); newDiv.style.animation = '0.3s linear 0s 1 normal forwards running rz'+elsn[i]+zf; //console.log(document.styleSheets[0]); setTimeout(function(id,zf){ cssn = 'rz'+id+zf; var rules = getkeyframes(cssn); rules.styleSheet.deleteRule(rules.index); css = rules.cssRule.cssText.substring(rules.cssRule.cssText.indexOf('100% {')+6); css = css.substring(0, css.indexOf('}'))+'transform-origin:150px 150px 0'; $('cube'+id).style.cssText = css; }, 300, elsn[i], zf); } setTimeout((elsn,zf)=>{chgId(elsn,zf);}, 350, elsn, zf); //这个时间要比上面动画时间要长一点}var getkeyframes=(name)=> { var animation = {}; // 获取所有的style var ss = document.styleSheets; const item = ss[0]; for (var i = 0; i < item.cssRules.length; ++i) { if (item.cssRules[i] && item.cssRules[i].name && item.cssRules[i].name === name) { animation.cssRule = item.cssRules[i]; animation.styleSheet = item; animation.index = i; } } return animation;}
浙公网安备 33010602011771号