4

So what I'm trying is basically rotating a cube by clicking on buttons that are either located right on the cube or floating next to it. For now I keep them floating because I was trying out a lot and it prooved to be easier this way, but placing them on the cube would be no problem at all.

The ACTUAL problem is that not all rotation axes seem to be treated equally. What I mean by that is that if I rotate along X, the Y and Z axes rotate aswell. But if I rotate along Y (or Z), the Z-axis (or Y-axis) rotate BUT the X-axis ALWAYS STAYS THE SAME. In my code this means that no matter how the cube is rotated, the rotation along the X-axis, triggered by clicking on the red squares "3" or "4" will always rotate the cube up or down whilst the other buttons rotate the cube according to its position. Don't mind that the numbers 1,2,5 and 6 get switched. This is just the closest I could get to a solution but rotating the cube randomly for a while will always result in strange movement sooner or later.

My code:

var rotationX = 0;
var rotationY = 0;
var rotationZ = 0;
var rotation = 0;
var translate = 0;

function showDebug() {
    var deBugInfo = '<p>X: '+rotationX + '</p>';
    deBugInfo += '<p>Y: '+rotationY + '</p>';
    deBugInfo += '<p>Z: '+rotationZ + '</p>';
    
    $('#deBug').html(deBugInfo);
}

function cubeRotate() {
    $('#cube').css('transform', 'rotateX('+rotationX+'deg) rotateY('+rotationY+'deg) rotateZ('+rotationZ+'deg)');
    
    showDebug();
}

/*

function buttonsRotate() {
    $('#buttons').css('transform', 'rotate('+rotation+'deg) translate('+0+')');
    
    showDebug();
}

*/

$(function () {

    $('#dir1').on('click', function () {
        //var myStyle = $('#cube').css('transform');
        //console.log(myStyle);
        //rotationY = rotationY - 90;
        rotationY -= 90;
        cubeRotate();
    });
    
    $('#dir2').on('click', function () {
        rotationY += 90;
        cubeRotate();
    });
    
    $('#dir3').on('click', function () {
        rotationX -= 90;
        cubeRotate();
        rotation -= 90;
//        buttonsRotate();
    });
    
    $('#dir4').on('click', function () {
        rotationX += 90;
        cubeRotate();
        rotation += 90;
//        buttonsRotate();
    });
   
    $('#dir5').on('click', function () {
        rotationZ += 90;
        cubeRotate();
    });
    
    $('#dir6').on('click', function () {
        rotationZ -= 90;
        cubeRotate();
    });

});
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-size: 16px;
    font-family: sans-serif;
    width: 100%;
    height: 100%;
    position: absolute;
    -webkit-perspective: 100vmax;
    /* Safari 4-8 */
    -webkit-perspective-origin: 50% 50%;
    /* Safari 4-8 */
    perspective: 100vmax;
    perspective-origin: 50% 50%;
    overflow: hidden;
}

#cube {
    margin: auto;
    position: absolute;
    top: 50%;
    left: 50%;
    transform-style: preserve-3d;
    transition-duration: 0.5s;
}

.cubeface {
    position: absolute;
    height: 60vmin;
    width: 60vmin;
    margin-left: -30vmin;
    margin-top: -30vmin;
    opacity: 0.5;
}

.cubeface:nth-child(1) {
    transform: rotateY(0deg) translateY(0px) translateZ(30vmin);
    background-color: black;
}

.cubeface:nth-child(2) {
    transform: rotateY(90deg) translateY(0px) translateZ(30vmin);
    background-color: #343434;
}

.cubeface:nth-child(3) {
    transform: rotateY(180deg) translateY(0px) translateZ(30vmin);
    background-color: #525252;
}

.cubeface:nth-child(4) {
    transform: rotateY(270deg) translateY(0px) translateZ(30vmin);
    background-color: #818181;
}

.cubeface:nth-child(5) {
    transform: rotateX(90deg) translateZ(30vmin) translateY(0px);
    background-color: #a0a0a0;
}

.cubeface:nth-child(6) {
    transform: rotateX(-90deg) translateZ(30vmin) translateY(0px);
    background-color: #d8d8d8;
}


.content {
    width: 100%;
    height: 100%;
    position: absolute;
    border-radius: 50%;
    background-color: #a7ff8d;

    color: #f00;
    font-size: 3em;
}

.arrow {
    margin: auto;
    position: absolute;
    left: 50%;
    top: 90vmin;
    width: 5vmin;
    height: 5vmin;

    margin-left: -2.5vmin;
    margin-top: -2.5vmin;

}

.achse {
    height: 2px;
    width: 65vmin;
    background: #f00;
    position: absolute;
    top: 0;
    left: 0;
    transform-origin: 0;
    backface-visibility: visible;
    transform-style: preserve-3d;
}

.achseY {
    background: #0f0;
    transform: rotateZ(-90deg) rotateX(45deg);
}

.achseZ {
    background: #00f;
    transform: rotateY(-90deg) rotateX(45deg);
}

.achseX {
    transform: rotateX(45deg);
}

#deBug {
    background: #000;
    color: #FFF;
    font-size: 2em;
    padding: 1em;
    position: absolute;
    top: 0;
    right: 0;

}

#buttons {
    width: 100%;
    height: 100%;
    z-index: 5;
}

#dir1 {
    margin-left: -27.5vmin;
    background-color: red;
    z-index: 5;

}

#dir2 {
    margin-left: -17.5vmin;
    background-color: red;
    z-index: 5;

}

#dir3 {
    margin-left: -7.5vmin;
    background-color: red;
    z-index: 10;

}

#dir4 {
    margin-left: 2.5vmin;
    background-color: red;
    z-index: 10;

}

#dir5 {
    margin-left: 12.5vmin;
    background-color: red;
    z-index: 5;

}

#dir6 {
    margin-left: 22.5vmin;
    background-color: red;
    z-index: 5;

}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!doctype html>
<html>

<head>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">

    <title>Test</title>

    <link rel="stylesheet" href="styles/screen.css">

    <script src="scripts/jquery-3.3.1.min.js"></script>
    <script src="scripts/app.js"></script>

</head>

<body>

    <div id="deBug">

    </div>

    <div class='arrow' id='dir3'>X-</div>
    <div class='arrow' id='dir4'>X+</div>

    <div id="buttons">

        <div class='arrow' id='dir1'>Y-</div>
        <div class='arrow' id='dir2'>Y+</div>
        <div class='arrow' id='dir5'>Z+</div>
        <div class='arrow' id='dir6'>Z-</div>

    </div>

    <div id="cube">

        <div class="cubeface" id="A">

            <div class="content" id="main">

                <p>A</p>

            </div>


            <div class="dir1 right">

            </div>

            <div class="dir2 left">

            </div>

            <div class="dir3 up">

            </div>

            <div class="dir4 down">

            </div>


        </div>

        <div class="cubeface" id="B">

            <div class="content" id="">

                <p>B</p>

            </div>


            <div class="dir1 right">

            </div>

            <div class="dir2 left">

            </div>

            <div class="dir5 up">

            </div>

            <div class="dir6 down">

            </div>


        </div>

        <div class="cubeface" id="C">

            <div class="content" id="">

                <p>C</p>

            </div>


            <div class="dir1 right">

            </div>

            <div class="dir2 left">

            </div>

            <div class="dir4 up">

            </div>

            <div class="dir3 down">

            </div>


        </div>

        <div class="cubeface" id="D">

            <div class="content" id="">

                <p>D</p>

            </div>


            <div class="dir1 right">

            </div>

            <div class="dir2 left">

            </div>

            <div class="dir6 up">

            </div>

            <div class="dir5 down">

            </div>


        </div>

        <div class="cubeface" id="E">

            <div class="content" id="">

                <p>E</p>

            </div>



            <div class="dir6 right">

            </div>

            <div class="dir5 left">

            </div>

            <div class="dir3 up">

            </div>

            <div class="dir4 down">

            </div>


        </div>

        <div class="cubeface" id="F">

            <div class="content" id="">

                <p>F</p>

            </div>


            <div class="dir5 right">

            </div>

            <div class="dir6 left">

            </div>

            <div class="dir3 up">

            </div>

            <div class="dir4 down">

            </div>


        </div>

        <div class="achse achseX"></div>
        <div class="achse achseY"></div>
        <div class="achse achseZ"></div>

    </div>

</body>

</html>

I've tried giving every single element the "transform-style: preserve-3d". Didn't work. I think it could actually better work with the buttons on the cube but that's a question to be answered later and NOT the subject here.

If anyone has an idea how to get ALL AXES TREATED EQUALLY, please tell me, I'm slowly getting mad af with this cube :D

Thank you!

Edits:

2018_04_18: Also tied to set the tranform origin without success.

2018_04_19: Updated the snippet so the buttons don't rotate anymore but are fixed so you can always click them.

2018_04_20:

I found two examples from pencode that could be helpful (they both work by dragging the cube with the mouse, I'm not sure if that's got something to do with the problem)...

  1. https://codepen.io/jordizle/pen/haIdo/ This cube has the nice aspect that the sides automatically rotate so that they can be read properly. However if you rotate it to sides 1 or 6, it shows the same problem as my cube does as it simply won't rotate left or right.

  2. https://codepen.io/ge1doot/pen/PqZKbv In this example the problem seems to be solved. No matter the cube's position you can always spin it in every direction. (I won't need the function to split it however) My problem is that I can't really see what's the difference between the scrips they use and therefore I don't know why the second one rotates perfectly but I have a feeling this is the right track. If anyone could compare them and solve this mystery it would be really nice ;)

2 Answers2

2

RELATIVE ROTATION - AXES ROTATE WITH THE CUBE

function rotate(axis, degrees) {
    cube.outerHTML = `<div class='gimbal' id='container' style="transition: all 0.5s; transform-style: preserve-3d; transform: rotate${axis}(0deg); position: relative; transition-timing-function: ease-in-out; width: 0; height: 0; transform-origin: 50vw 50vh;">${cube.outerHTML}</div>`;
    window.setTimeout(function () {
        container.style.transform = `rotate${axis}(${degrees}deg)`;
        container.removeAttribute('id');
    }, 10);
}
$('#dir1').on('click', function () {
    rotate('Y', '90');
});

$('#dir2').on('click', function () {
    rotate('Y', '-90');
});

$('#dir3').on('click', function () {
    rotate('X', '90');
});

$('#dir4').on('click', function () {
    rotate('X', '-90');
});

$('#dir5').on('click', function () {
    rotate('Z', '90');
});

$('#dir6').on('click', function () {
    rotate('Z', '-90');
});
rotate('X', '0');
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html, body {
    font-size: 16px;
    font-family: sans-serif;
    width: 100%;
    height: 100%;
    -webkit-perspective: 100vmax;
    /* Safari 4-8 */
    -webkit-perspective-origin: 50% 50%;
    /* Safari 4-8 */
    perspective: 100vmax;
    perspective-origin: 50% 50%;
    overflow: hidden;
}

#cube {
    margin: auto;
    position: absolute;
    top: 50vh; left: 50vw;
    transform-style: preserve-3d;
}

.cubeface {
    position: absolute;
    height: 60vmin;
    width: 60vmin;
    margin-left: -30vmin;
    margin-top: -30vmin;
    opacity: 0.5;
}

.cubeface:nth-child(1) {
    transform: rotateY(0deg) translateY(0px) translateZ(30vmin);
    background-color: black;
}

.cubeface:nth-child(2) {
    transform: rotateY(90deg) translateY(0px) translateZ(30vmin);
    background-color: #343434;
}

.cubeface:nth-child(3) {
    transform: rotateY(180deg) translateY(0px) translateZ(30vmin);
    background-color: #525252;
}

.cubeface:nth-child(4) {
    transform: rotateY(270deg) translateY(0px) translateZ(30vmin);
    background-color: #818181;
}

.cubeface:nth-child(5) {
    transform: rotateX(90deg) translateZ(30vmin) translateY(0px);
    background-color: #a0a0a0;
}

.cubeface:nth-child(6) {
    transform: rotateX(-90deg) translateZ(30vmin) translateY(0px);
    background-color: #d8d8d8;
}


.content {
    width: 100%;
    height: 100%;
    position: absolute;
    border-radius: 50%;
    background-color: #a7ff8d;

    color: #f00;
    font-size: 3em;
}

.arrow {
    margin: auto;
    position: absolute;
    left: 50%;
    top: 90vmin;
    width: 5vmin;
    height: 5vmin;

    margin-left: -2.5vmin;
    margin-top: -2.5vmin;

}

.achse {
    height: 2px;
    width: 65vmin;
    background: #f00;
    position: absolute;
    top: 0;
    left: 0;
    transform-origin: 0;
    backface-visibility: visible;
    transform-style: preserve-3d;
}

.achseY {
    background: #0f0;
    transform: rotateZ(-90deg) rotateX(45deg);
}

.achseZ {
    background: #00f;
    transform: rotateY(-90deg) rotateX(45deg);
}

.achseX {
    transform: rotateX(45deg);
}

#deBug {
    background: #000;
    color: #FFF;
    font-size: 2em;
    padding: 1em;
    position: absolute;
    top: 0;
    right: 0;

}

#buttons {
    width: 100%;
    height: 100%;
    z-index: 5;
}

#dir1 {
    margin-left: -27.5vmin;
    background-color: red;
    z-index: 5;

}

#dir2 {
    margin-left: -17.5vmin;
    background-color: red;
    z-index: 5;

}

#dir3 {
    margin-left: -7.5vmin;
    background-color: red;
    z-index: 10;

}

#dir4 {
    margin-left: 2.5vmin;
    background-color: red;
    z-index: 10;

}

#dir5 {
    margin-left: 12.5vmin;
    background-color: red;
    z-index: 5;

}

#dir6 {
    margin-left: 22.5vmin;
    background-color: red;
    z-index: 5;

}
<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1">

        <title>Test</title>

        <link rel="stylesheet" href="styles/screen.css">

        <script src="scripts/jquery-3.3.1.min.js"></script>
        <script src="scripts/app.js"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    </head>

    <body>

        <div id="cube" class="gimbal">

            <div class="cubeface" id="A">

                <div class="content" id="main">

                    <p>A</p>

                </div>


                <div class="dir1 right">

                </div>

                <div class="dir2 left">

                </div>

                <div class="dir3 up">

                </div>

                <div class="dir4 down">

                </div>


            </div>

            <div class="cubeface" id="B">

                <div class="content" id="">

                    <p>B</p>

                </div>


                <div class="dir1 right">

                </div>

                <div class="dir2 left">

                </div>

                <div class="dir5 up">

                </div>

                <div class="dir6 down">

                </div>


            </div>

            <div class="cubeface" id="C">

                <div class="content" id="">

                    <p>C</p>

                </div>


                <div class="dir1 right">

                </div>

                <div class="dir2 left">

                </div>

                <div class="dir4 up">

                </div>

                <div class="dir3 down">

                </div>


            </div>

            <div class="cubeface" id="D">

                <div class="content" id="">

                    <p>D</p>

                </div>


                <div class="dir1 right">

                </div>

                <div class="dir2 left">

                </div>

                <div class="dir6 up">

                </div>

                <div class="dir5 down">

                </div>


            </div>

            <div class="cubeface" id="E">

                <div class="content" id="">

                    <p>E</p>

                </div>



                <div class="dir6 right">

                </div>

                <div class="dir5 left">

                </div>

                <div class="dir3 up">

                </div>

                <div class="dir4 down">

                </div>


            </div>

            <div class="cubeface" id="F">

                <div class="content" id="">

                    <p>F</p>

                </div>


                <div class="dir5 right">

                </div>

                <div class="dir6 left">

                </div>

                <div class="dir3 up">

                </div>

                <div class="dir4 down">

                </div>


            </div>
        </div>
        
        <div id="buttons">
            <div class='arrow' id='dir1'>Y+</div>
            <div class='arrow' id='dir2'>Y-</div>
            <div class='arrow' id='dir3'>X+</div>
            <div class='arrow' id='dir4'>X-</div>
            <div class='arrow' id='dir5'>Z+</div>
            <div class='arrow' id='dir6'>Z-</div>
        </div>
    </body>
</html>

GLOBAL ROTATION - AXES DON'T ROTATE WITH THE CUBE

function rotate(axis, degrees) {
    let outermostRotator = $('body > .gimbal').get(0);
    outermostRotator.outerHTML = `<div class='gimbal' id='container' style="transition: all 0.5s; transform-style: preserve-3d; transform: rotate${axis}(0deg); position: relative; transition-timing-function: ease-in-out; width: 0; height: 0; transform-origin: 50vw 50vh;">${outermostRotator.outerHTML}</div>`;
    window.setTimeout(function () {
        container.style.transform = `rotate${axis}(${degrees}deg)`;
        container.removeAttribute('id');
    }, 10);
}
$('#dir1').on('click', function () {
    rotate('Y', '90');
});

$('#dir2').on('click', function () {
    rotate('Y', '-90');
});

$('#dir3').on('click', function () {
    rotate('X', '90');
});

$('#dir4').on('click', function () {
    rotate('X', '-90');
});

$('#dir5').on('click', function () {
    rotate('Z', '90');
});

$('#dir6').on('click', function () {
    rotate('Z', '-90');
});
rotate('X', '0');
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html, body {
    font-size: 16px;
    font-family: sans-serif;
    width: 100%;
    height: 100%;
    -webkit-perspective: 100vmax;
    /* Safari 4-8 */
    -webkit-perspective-origin: 50% 50%;
    /* Safari 4-8 */
    perspective: 100vmax;
    perspective-origin: 50% 50%;
    overflow: hidden;
}

#cube {
    margin: auto;
    position: absolute;
    top: 50vh; left: 50vw;
    transform-style: preserve-3d;
}

.cubeface {
    position: absolute;
    height: 60vmin;
    width: 60vmin;
    margin-left: -30vmin;
    margin-top: -30vmin;
    opacity: 0.5;
}

.cubeface:nth-child(1) {
    transform: rotateY(0deg) translateY(0px) translateZ(30vmin);
    background-color: black;
}

.cubeface:nth-child(2) {
    transform: rotateY(90deg) translateY(0px) translateZ(30vmin);
    background-color: #343434;
}

.cubeface:nth-child(3) {
    transform: rotateY(180deg) translateY(0px) translateZ(30vmin);
    background-color: #525252;
}

.cubeface:nth-child(4) {
    transform: rotateY(270deg) translateY(0px) translateZ(30vmin);
    background-color: #818181;
}

.cubeface:nth-child(5) {
    transform: rotateX(90deg) translateZ(30vmin) translateY(0px);
    background-color: #a0a0a0;
}

.cubeface:nth-child(6) {
    transform: rotateX(-90deg) translateZ(30vmin) translateY(0px);
    background-color: #d8d8d8;
}


.content {
    width: 100%;
    height: 100%;
    position: absolute;
    border-radius: 50%;
    background-color: #a7ff8d;

    color: #f00;
    font-size: 3em;
}

.arrow {
    margin: auto;
    position: absolute;
    left: 50%;
    top: 90vmin;
    width: 5vmin;
    height: 5vmin;

    margin-left: -2.5vmin;
    margin-top: -2.5vmin;

}

.achse {
    height: 2px;
    width: 65vmin;
    background: #f00;
    position: absolute;
    top: 0;
    left: 0;
    transform-origin: 0;
    backface-visibility: visible;
    transform-style: preserve-3d;
}

.achseY {
    background: #0f0;
    transform: rotateZ(-90deg) rotateX(45deg);
}

.achseZ {
    background: #00f;
    transform: rotateY(-90deg) rotateX(45deg);
}

.achseX {
    transform: rotateX(45deg);
}

#deBug {
    background: #000;
    color: #FFF;
    font-size: 2em;
    padding: 1em;
    position: absolute;
    top: 0;
    right: 0;

}

#buttons {
    width: 100%;
    height: 100%;
    z-index: 5;
}

#dir1 {
    margin-left: -27.5vmin;
    background-color: red;
    z-index: 5;

}

#dir2 {
    margin-left: -17.5vmin;
    background-color: red;
    z-index: 5;

}

#dir3 {
    margin-left: -7.5vmin;
    background-color: red;
    z-index: 10;

}

#dir4 {
    margin-left: 2.5vmin;
    background-color: red;
    z-index: 10;

}

#dir5 {
    margin-left: 12.5vmin;
    background-color: red;
    z-index: 5;

}

#dir6 {
    margin-left: 22.5vmin;
    background-color: red;
    z-index: 5;

}
<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1">

        <title>Test</title>

        <link rel="stylesheet" href="styles/screen.css">

        <script src="scripts/jquery-3.3.1.min.js"></script>
        <script src="scripts/app.js"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    </head>

    <body>

        <div id="cube" class="gimbal">

            <div class="cubeface" id="A">

                <div class="content" id="main">

                    <p>A</p>

                </div>


                <div class="dir1 right">

                </div>

                <div class="dir2 left">

                </div>

                <div class="dir3 up">

                </div>

                <div class="dir4 down">

                </div>


            </div>

            <div class="cubeface" id="B">

                <div class="content" id="">

                    <p>B</p>

                </div>


                <div class="dir1 right">

                </div>

                <div class="dir2 left">

                </div>

                <div class="dir5 up">

                </div>

                <div class="dir6 down">

                </div>


            </div>

            <div class="cubeface" id="C">

                <div class="content" id="">

                    <p>C</p>

                </div>


                <div class="dir1 right">

                </div>

                <div class="dir2 left">

                </div>

                <div class="dir4 up">

                </div>

                <div class="dir3 down">

                </div>


            </div>

            <div class="cubeface" id="D">

                <div class="content" id="">

                    <p>D</p>

                </div>


                <div class="dir1 right">

                </div>

                <div class="dir2 left">

                </div>

                <div class="dir6 up">

                </div>

                <div class="dir5 down">

                </div>


            </div>

            <div class="cubeface" id="E">

                <div class="content" id="">

                    <p>E</p>

                </div>



                <div class="dir6 right">

                </div>

                <div class="dir5 left">

                </div>

                <div class="dir3 up">

                </div>

                <div class="dir4 down">

                </div>


            </div>

            <div class="cubeface" id="F">

                <div class="content" id="">

                    <p>F</p>

                </div>


                <div class="dir5 right">

                </div>

                <div class="dir6 left">

                </div>

                <div class="dir3 up">

                </div>

                <div class="dir4 down">

                </div>


            </div>
        </div>
        
        <div id="buttons">
            <div class='arrow' id='dir1'>Y+</div>
            <div class='arrow' id='dir2'>Y-</div>
            <div class='arrow' id='dir3'>X+</div>
            <div class='arrow' id='dir4'>X-</div>
            <div class='arrow' id='dir5'>Z+</div>
            <div class='arrow' id='dir6'>Z-</div>
        </div>
    </body>
</html>
clickbait
  • 2,818
  • 1
  • 25
  • 61
  • 1
    Hmm thanks for the answer! (and sorry for the late reply) It seems to work perfectly here in this forum but if I copy it to my project the buttons won't react. Could it have something to do with an imported library or the different commas you used? – Anton Feirer Jul 20 '18 at 14:23
1

Your problem is caused by something called Euler Order. Basically, it's in what order the axes are used to calculate the final rotation.

The CSS transform Euler Order is the most common one: X, Y, Z.

The X-axis is computed first, so it's always stuck to its resting position. You cannot change the Euler Order in CSS. Even if you could, the first axis of the Euler Order will always be "not treated equally."

Another problem with Euler Rotation is Gimbal Lock. Click X, click Y, and click Z in your demo. Now try rotating X and Z. They're the same! You've just encountered Gimbal Lock, which is super hard to solve and not possible to fix with just CSS.

clickbait
  • 2,818
  • 1
  • 25
  • 61