完全基于html和three.js实现的魔方

发布时间 2023-08-30 14:18:43作者: 远山飘来清风
记录大学时代的一个实验demo
 
代码完全基于html、three.js实现
 
1. 实现效果演示:

  

 

2. 项目结构:

  

 

3. three.js需自行下载依赖包,index.html完整代码:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript" src="js/three.js"></script>
        <!--
            <script type="text/javascript" src="js/OrbitControls.js"></script>
        -->
        <style type="text/css">
            body{
                margin: 0;
                overflow: hidden;
            }
        </style>
    </head>
    <body onload="initThreeScene();">
        
        
        <script>
            
            var width=window.innerWidth;
            var height=window.innerHeight;
            
            var camera;
            var renderer;
            var scene;
            var light,ambient;
            var cube=[];//立方体
            var cube_to_rotate=[];//待旋转的立方体
            var mesh=[];//立方体mesh
            var raycaster;//检测点击物体
            var obj_click_pos=[];//被检测到的物体坐标
            
            var startpoint=[],leavepoint=[];//鼠标点击与离开的点
            var rotate=-1,direction=-1;//旋转方向
            
            var x=[],y=[],z=[],x1=[],y1=[],z1=[];//相隔帧的position
            var axisX=[],axisY=[],axisZ=[];//调整坐标系
            
            function initThreeScene(){
                
                initScene();
                initCube();
                initLight();
                initCamera();
                initRender();
                render();
                initListener();
                
//                var controls=new THREE.OrbitControls(camera);
                //controls.addListener("change",render);
                
                var axesHelper=new THREE.AxesHelper(250);
                scene.add(axesHelper);
                
                window.addEventListener("resize",function(){
                    width=window.innerWidth;
                    height=window.innerHeight;
                    renderer.setSize(width,height);
                    camera.aspect=width/height;
                    camera.updateProjectionMatrix();
                },false);
                
            }
            
            function initScene(){
                
                scene=new THREE.Scene();
                
            }
            
            function initCamera(){
                
//                var k=width/height;
//                var s=200;
//                camera=new THREE.OrthographicCamera(-s*k,s*k,s,-s,1,1000);
                camera = new THREE.PerspectiveCamera(50,width/height,1,1000);
//                camera.position.set(200,300,200);
                camera.position.set(300,260,300);
                camera.lookAt(scene.position);
                
            }
            
            function initRender(){
                
                renderer=new THREE.WebGLRenderer();
                renderer.setSize(width,height);
                renderer.setClearColor(0xb9d3ff,1);
                document.body.appendChild(renderer.domElement);
                
            }
            
            function initLight(){
                
                light=new THREE.PointLight(0xffffff);
                light.position.set(400,200,300);
                ambient=new THREE.AmbientLight(0x555555);
                scene.add(light);
                scene.add(ambient);
                
            }
            
            function initCube(){
                
                for(i=0;i<27;i++)
                    cube[i]=new THREE.BoxGeometry(50,50,50);
                
                for(i=0;i<9;i++){
                    
                    cube[i].faces[2].color.setHex(0xff0000);
                    cube[i].faces[3].color.setHex(0xff0000);
                    if(i%3==0){
                        cube[i].faces[10].color.setHex(0x00ff00);
                        cube[i].faces[11].color.setHex(0x00ff00);
                    }
                    if(i%3==2){
                        cube[i].faces[8].color.setHex(0xffff00);
                        cube[i].faces[9].color.setHex(0xffff00);
                    }
                    if(i>5){
                        cube[i].faces[4].color.setHex(0x0000ff);
                        cube[i].faces[5].color.setHex(0x0000ff);
                    }
                    if(i<3){
                        cube[i].faces[6].color.setHex(0x00ffff);
                        cube[i].faces[7].color.setHex(0x00ffff);
                    }
                    
                }
                
                for(i=9;i<18;i++){
                    
                    if((i-9)%3==0){
                        cube[i].faces[10].color.setHex(0x00ff00);
                        cube[i].faces[11].color.setHex(0x00ff00);
                    }
                    if((i-9)%3==2){
                        cube[i].faces[8].color.setHex(0xffff00);
                        cube[i].faces[9].color.setHex(0xffff00);
                    }
                    if((i-9)>5){
                        cube[i].faces[4].color.setHex(0x0000ff);
                        cube[i].faces[5].color.setHex(0x0000ff);
                    }
                    if((i-9)<3){
                        cube[i].faces[6].color.setHex(0x00ffff);
                        cube[i].faces[7].color.setHex(0x00ffff);
                    }
                    
                }
                
                for(i=18;i<27;i++){
                    
                    if((i-18)%3==0){
                        cube[i].faces[10].color.setHex(0x00ff00);
                        cube[i].faces[11].color.setHex(0x00ff00);
                    }
                    if((i-18)%3==2){
                        cube[i].faces[8].color.setHex(0xffff00);
                        cube[i].faces[9].color.setHex(0xffff00);
                    }
                    if((i-18)>5){
                        cube[i].faces[4].color.setHex(0x0000ff);
                        cube[i].faces[5].color.setHex(0x0000ff);
                    }
                    if((i-18)<3){
                        cube[i].faces[6].color.setHex(0x00ffff);
                        cube[i].faces[7].color.setHex(0x00ffff);
                    }
                    
                    cube[i].faces[0].color.setHex(0xff00ff);
                    cube[i].faces[1].color.setHex(0xff00ff);
                    
                }
                
                for(i=0;i<27;i++){
                    var material=new THREE.MeshBasicMaterial({
                        vertexColors:THREE.FaceColors
                    });
                    mesh[i] = new THREE.Mesh(cube[i],material);
                    mesh[i].position.set((Math.floor(i/9)-1)*53,((Math.floor(i%9/3))-1)*53,(Math.floor(i%9%3)-1)*53);
                    scene.add(mesh[i]);
                    x[i]=mesh[i].position.x;
                    y[i]=mesh[i].position.y;
                    z[i]=mesh[i].position.z;
                    axisX[i]=1;
                    axisY[i]=2;
                    axisZ[i]=3;
                }

            }
            
            var angle=0;
            function render(){
                renderer.render(scene,camera);
                
                if(rotate==0||rotate==1){
                    var length=rotate==0?27:9;
                    if(direction==0||direction==1){
                        var dir=(direction==0)?1:-1;
                        var dir1;
                        var pre;
                        if(angle<=0.5){
                            angle+=0.01;
                            for(i=0;i<length;i++){
                                pre=cube_to_rotate[i];
                                x1[pre]=Math.cos(dir*angle*Math.PI)*x[pre]-Math.sin(dir*angle*Math.PI)*z[pre];
                                z1[pre]=Math.cos(dir*angle*Math.PI)*z[pre]+Math.sin(dir*angle*Math.PI)*x[pre];
                                mesh[pre].position.set(x1[pre],mesh[pre].position.y,z1[pre]);
                                dir1=(axisY[pre]>0)?1:-1;
                                switch(Math.abs(axisY[pre])){
                                    case 1:
                                        mesh[pre].rotateX(-0.01*dir*dir1*Math.PI);
                                        break;
                                    case 2:
                                        mesh[pre].rotateY(-0.01*dir*dir1*Math.PI);
                                        break;
                                    case 3:
                                        mesh[pre].rotateZ(-0.01*dir*dir1*Math.PI);
                                        break;
                                }
                            }
                        }else{
                            reviseAxis(direction);
                            direction=-1;
                            rotate=-1;
                            revisePosition();
                            angle=0;
                        }
                    }else if(direction==2||direction==3){
                        var dir=(direction==2)?-1:1;
                        var dir1;
                        var pre;
                        if(angle<=0.5){
                            angle+=0.01;
                            for(i=0;i<length;i++){
                                pre=cube_to_rotate[i];
                                y1[pre]=Math.cos(dir*angle*Math.PI)*y[pre]-Math.sin(dir*angle*Math.PI)*x[pre];
                                x1[pre]=Math.cos(dir*angle*Math.PI)*x[pre]+Math.sin(dir*angle*Math.PI)*y[pre];
                                mesh[pre].position.set(x1[pre],y1[pre],mesh[pre].position.z);
                                dir1=(axisZ[pre]>0)?1:-1;
                                switch(Math.abs(axisZ[pre])){
                                    case 1:
                                        mesh[pre].rotateX(-0.01*dir*dir1*Math.PI);
                                        break;
                                    case 2:
                                        mesh[pre].rotateY(-0.01*dir*dir1*Math.PI);
                                        break;
                                    case 3:
                                        mesh[pre].rotateZ(-0.01*dir*dir1*Math.PI);
                                        break;
                                }
                            }
                        }else{
                            reviseAxis(direction);
                            direction=-1;
                            rotate=-1;
                            revisePosition();
                            angle=0;
                        }
                    }else if(direction==4||direction==5){
                        var dir=(direction==4)?-1:1;
                        var dir1;
                        var pre;
                        if(angle<=0.5){
                            angle+=0.01;
                            for(i=0;i<length;i++){
                                pre=cube_to_rotate[i];
                                z1[pre]=Math.cos(dir*angle*Math.PI)*z[pre]-Math.sin(dir*angle*Math.PI)*y[pre];
                                y1[pre]=Math.cos(dir*angle*Math.PI)*y[pre]+Math.sin(dir*angle*Math.PI)*z[pre];
                                mesh[pre].position.set(mesh[pre].position.x,y1[pre],z1[pre]);
                                dir1=(axisX[pre]>0)?1:-1;
                                switch(Math.abs(axisX[pre])){
                                    case 1:
                                        mesh[pre].rotateX(-0.01*dir*dir1*Math.PI);
                                        break;
                                    case 2:
                                        mesh[pre].rotateY(-0.01*dir*dir1*Math.PI);
                                        break;
                                    case 3:
                                        mesh[pre].rotateZ(-0.01*dir*dir1*Math.PI);
                                        break;
                                }
                            }
                        }else{
                            reviseAxis(direction);
                            direction=-1;
                            rotate=-1;
                            revisePosition();
                            angle=0;
                        }
                    }
                }
                
                requestAnimationFrame(render);
                
            }
            
            function revisePosition(){
                
                var x2,y2,z2;
                for(i=0;i<27;i++){
                    x2=Math.round(mesh[i].position.x/53)*53;
                    y2=Math.round(mesh[i].position.y/53)*53;
                    z2=Math.round(mesh[i].position.z/53)*53;
                    x[i]=x2;
                    y[i]=y2;
                    z[i]=z2;
                    mesh[i].position.set(x2,y2,z2);
                }
            }
            
            function reviseAxis(dir){
                var temp;
                var pre;
                var length=rotate==0?27:9;
                switch(dir){
                    case 0://横左
                        for(i=0;i<length;i++){
                            pre=cube_to_rotate[i];
                            temp=axisX[pre];
                            axisX[pre]=-axisZ[pre];
                            axisZ[pre]=temp;
                        }
                        break;
                    case 1://横右
                        for(i=0;i<length;i++){
                            pre=cube_to_rotate[i];
                            temp=axisX[pre];
                            axisX[pre]=axisZ[pre];
                            axisZ[pre]=-temp;
                        }
                        break;
                    case 2://左上
                        for(i=0;i<length;i++){
                            pre=cube_to_rotate[i];
                            temp=axisX[pre];
                            axisX[pre]=-axisY[pre];
                            axisY[pre]=temp;
                        }
                        break;
                    case 3://右下
                        for(i=0;i<length;i++){
                            pre=cube_to_rotate[i];
                            temp=axisX[pre];
                            axisX[pre]=axisY[pre];
                            axisY[pre]=-temp;
                        }
                        break;
                    case 4://左下
                        for(i=0;i<length;i++){
                            pre=cube_to_rotate[i];
                            temp=axisZ[pre];
                            axisZ[pre]=axisY[pre];
                            axisY[pre]=-temp;
                        }
                        break;
                    case 5://右上
                        for(i=0;i<length;i++){
                            pre=cube_to_rotate[i];
                            temp=axisZ[pre];
                            axisZ[pre]=-axisY[pre];
                            axisY[pre]=temp;
                        }
                        break;
                }
            }
            
            function initListener(){
                raycaster=new THREE.Raycaster();
                window.addEventListener("mousedown",mousedown);
                window.addEventListener("mouseup",mouseup);
            }

            function mousedown(e){
                //将html坐标系转化为webgl坐标系,并确定鼠标点击位置
                startpoint[0]=e.clientX;
                startpoint[1]=e.clientY;
                var clickPoint=new THREE.Vector2();
                clickPoint.x =  e.clientX / renderer.domElement.clientWidth*2-1;
                clickPoint.y =  -(e.clientY / renderer.domElement.clientHeight*2)+1;
                raycaster.setFromCamera(clickPoint,camera);
                var intersects = raycaster.intersectObjects(scene.children);
                
                if(intersects.length==0){
                    if(rotate==-1)
                        rotate=0;
                }else{
                    if(rotate==-1){
                        rotate=1;
                        obj_click_pos[0]=intersects[0].object.position.x;
                        obj_click_pos[1]=intersects[0].object.position.y;
                        obj_click_pos[2]=intersects[0].object.position.z;
                    }
                }
            }
            
            function mouseup(e){
                leavepoint[0]=e.clientX;
                leavepoint[1]=e.clientY;
                var x=leavepoint[0]-startpoint[0];
                var y=leavepoint[1]-startpoint[1];
                if((rotate==0||rotate==1)&&direction==-1){
                    if(Math.abs(x)>50||Math.abs(y)>50){
                        if(rotate==0)
                            for(i=0;i<27;i++){
                                cube_to_rotate[i]=i;
                            }
                        if(Math.abs(x)<Math.abs(y)){
                            if(x<0){
                                if(y<0){//>>左上direction=2,绕z轴旋转
                                    if(rotate==1){
                                        var j=0;
                                        for(i=0;i<27;i++){
                                            if(mesh[i].position.z==obj_click_pos[2]){
                                                cube_to_rotate[j]=i;
                                                j++;
                                            }
                                        }
                                    }
                                    direction=2;
                                }else{//>>左下direction=4,绕x轴旋转
                                    if(rotate==1){
                                        var j=0;
                                        for(i=0;i<27;i++){
                                            if(mesh[i].position.x==obj_click_pos[0]){
                                                cube_to_rotate[j]=i;
                                                j++;
                                            }
                                        }
                                    }
                                    direction=4;
                                }
                            }else{
                                if(y>0){//右下>>direction=3,绕z轴旋转
                                    if(rotate==1){
                                        var j=0;
                                        for(i=0;i<27;i++){
                                            if(mesh[i].position.z==obj_click_pos[2]){
                                                cube_to_rotate[j]=i;
                                                j++;
                                            }
                                        }
                                    }
                                    direction=3;
                                }else{//右上>>direction=5,绕x轴旋转
                                    if(rotate==1){
                                        var j=0;
                                        for(i=0;i<27;i++){
                                            if(mesh[i].position.x==obj_click_pos[0]){
                                                cube_to_rotate[j]=i;
                                                j++;
                                            }
                                        }
                                    }
                                    direction=5;
                                }
                                
                            }
                                
                        }else{//绕y轴旋转
                            if(rotate==1){
                                var j=0;
                                for(i=0;i<27;i++){
                                    if(mesh[i].position.y==obj_click_pos[1]){
                                        cube_to_rotate[j]=i;
                                        j++;
                                    }
                                }
                            }
                            if(x<0){//横左>>direction=0
                                direction=0;
                            }else{//横右>>direction=1
                                direction=1;
                            }
                        }
                    }
                }
            }
            
        </script>
    </body>
</html>