It's kind of hard to explain in words what I'm looking for without a visual example, so here's the basic idea: https://codepen.io/jwiggiff/full/xxwvVbr
var cube = $('.cube');
var wrapper = $('.wrapper');
$(document).mousemove((e) => {
if(!cube.hasClass('locked')) {
var deltaX = e.pageX - (wrapper.offset().left+(wrapper.width()/2));
var deltaY = e.pageY - (wrapper.offset().top+(wrapper.height()/2));
var rotateY = deltaX/($(document).width()/2) * 90;
var rotateX = deltaY/(($(document).height()/2)) * -90;
cube.css("transform", " rotateY("+rotateY+"deg) rotateX("+rotateX+"deg)");
}
});
$('.top-label').click((e) => {
cube.toggleClass('locked locked-bottom');
$('.labels div:not(.top-label)').fadeToggle();
});
$('.bottom-label').click((e) => {
cube.toggleClass('locked locked-top');
$('.labels div:not(.bottom-label)').fadeToggle();
});
$('.left-label').click((e) => {
cube.toggleClass('locked locked-right');
$('.labels div:not(.left-label)').fadeToggle();
});
$('.right-label').click((e) => {
cube.toggleClass('locked locked-left');
$('.labels div:not(.right-label)').fadeToggle();
});
/* Variables */
:root {
--cube-scale: 60vh;
}
/* Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Montserrat", sans-serif;
overflow: hidden;
}
.cube div {
padding: 10vh;
border: 5px solid blue;
transition: all 500ms ease-in-out;
}
.cube div h1 {
font-family: "Carter One", cursive;
}
.cube div p {
font-size: 14px;
}
/* Labels */
.labels {
color: black;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.labels div {
transition: all 500ms ease-in-out;
}
.left-label {
position: absolute;
top: 50%;
left: 0;
transform: translate(0, -50%);
padding: 20px 10px;
}
.right-label {
position: absolute;
top: 50%;
right: 0;
transform: translate(0, -50%);
padding: 20px 10px;
}
.top-label {
position: absolute;
top: 0;
left: 50%;
width: 100%;
text-align: center;
transform: translate(-50%, 0);
padding: 10px 20px;
}
.bottom-label {
position: absolute;
bottom: 0;
left: 50%;
width: 100%;
text-align: center;
transform: translate(-50%, 0);
padding: 10px 20px;
}
/* 3D Cube */
.wrapper {
width: var(--cube-scale);
height: var(--cube-scale);
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
perspective: calc(var(--cube-scale)*4);
}
.cube {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
}
.cube.locked.locked-bottom {
transition: all 500ms ease-in-out;
transform: rotateX(90deg) translateY(calc(var(--cube-scale) / -2)) !important;
}
.locked-bottom .bottom {
height: 100vh;
width: 100vw;
transform: rotateX(-90deg) translateZ(calc(var(--cube-scale) / 2)) translate(calc(((100vw/2) - (var(--cube-scale)/2)) * -1), calc(((100vh/2) - (var(--cube-scale)/2)) * -1));
}
.cube.locked.locked-top {
transition: all 500ms ease-in-out;
transform: rotateX(-90deg) translateY(calc(var(--cube-scale) / 2)) !important;
}
.locked-top .top {
height: 100vh;
width: 100vw;
transform: rotateX(90deg) translateZ(calc(var(--cube-scale) / 2)) translate(calc(((100vw/2) - (var(--cube-scale)/2)) * -1), calc(((100vh/2) - (var(--cube-scale)/2)) * -1));
}
.cube.locked.locked-right {
transition: all 500ms ease-in-out;
transform: rotateY(-90deg) translateX(calc(var(--cube-scale) / -2)) !important;
}
.locked-right .right {
height: 100vh;
width: 100vw;
transform: rotateY(90deg) translateZ(calc(var(--cube-scale) / 2)) translate(calc(((100vw/2) - (var(--cube-scale)/2)) * -1), calc(((100vh/2) - (var(--cube-scale)/2)) * -1));
}
.cube.locked.locked-left {
transition: all 500ms ease-in-out;
transform: rotateY(90deg) translateX(calc(var(--cube-scale) / 2)) !important;
}
.locked-left .left {
height: 100vh;
width: 100vw;
transform: rotateY(-90deg) translateZ(calc(var(--cube-scale) / 2)) translate(calc(((100vw/2) - (var(--cube-scale)/2)) * -1), calc(((100vh/2) - (var(--cube-scale)/2)) * -1));
}
.cube div {
transform-origin: calc(var(--cube-scale)/2) calc(var(--cube-scale)/2);
position: absolute;
width: var(--cube-scale);
height: var(--cube-scale);
}
.front {
background: rgba(255,0,0,0.7);
transform: rotateY(0deg) translateZ(calc(var(--cube-scale) / 2));
}
.right {
background-color: rgba(0,255,0,0.7);
transform: rotateY(90deg) translateZ(calc(var(--cube-scale) / 2));
}
.back {
background-color: rgba(0,0,255,0.7);
transform: rotateY(180deg) translateZ(calc(var(--cube-scale) / 2));
}
.left {
background-color: rgba(255,165,0,0.7);
transform: rotateY(-90deg) translateZ(calc(var(--cube-scale) / 2));
}
.top {
background-color: rgba(128,0,128,0.7);
transform: rotateX(90deg) translateZ(calc(var(--cube-scale) / 2));
}
.bottom {
background-color: rgba(255,0,255,0.7);
transform: rotateX(-90deg) translateZ(calc(var(--cube-scale) / 2));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<div class="cube">
<div class="front"></div>
<div class="back"></div>
<div class="left"></div>
<div class="right"></div>
<div class="top"></div>
<div class="bottom"></div>
</div>
</div>
<div class="labels">
<div class="left-label"><span>Right</span></div>
<div class="right-label"><span>Left</span></div>
<div class="top-label"><span>Bottom</span></div>
<div class="bottom-label"><span>Top</span></div>
</div>
In the codepen, if you click the text on the edge of the screen, it moves the current face to cover the entire window. When you click the left or right text, it has the expected result. However, if you click the top or the bottom text, you can see the cube has a bit of an arc when translating away from the screen, whereas the left/right ones do not. Any ideas as to what is causing this bounce/arc and how to fix it would be much appreciated!
The text we’re about to create gives off the illusion of bouncing up and down on the viewport. The bounce effect is created using CSS animations and by manipulated CSS’s text-shadow property.
Bounce scroll in the browser is a feature of some versions of iOS / macOS. To prevent it from happening on a website we can use the following: html, body { height: 100%; overflow: hidden; } #main-container { position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow: auto; } While the accepted answer works.
As you read through the tutorial, keep in mind that just about every stylistic choice you see, from font families to colors to even the speed of the bouncing animation are totally customizable and can always be changed to suit the needs of any project. The effect in this tutorial is adapted from this CodePen.
Bounce Animation effect is used to move the element quick up, back, or away from a surface after hitting it.
Add rotateY(0)
to .cube.locked.locked-bottom
and to .cube.locked.locked-top
to avoid this issue.
It's a bit tricky but this is due to the fact that you are applying a dynamic transformation using rotateY(a) rotateX(b)
and this will not transition the same way to rotateX() translateZ()
and to rotateY() translateZ()
. If you make rotateX() translateZ()
equal to rotateY(0) rotateX() translateZ()
you will have the same transition:
var cube = $('.cube');
var wrapper = $('.wrapper');
$(document).mousemove((e) => {
if(!cube.hasClass('locked')) {
var deltaX = e.pageX - (wrapper.offset().left+(wrapper.width()/2));
var deltaY = e.pageY - (wrapper.offset().top+(wrapper.height()/2));
var rotateY = deltaX/($(document).width()/2) * 90;
var rotateX = deltaY/(($(document).height()/2)) * -90;
cube.css("transform", " rotateY("+rotateY+"deg) rotateX("+rotateX+"deg)");
}
});
$('.top-label').click((e) => {
cube.toggleClass('locked locked-bottom');
$('.labels div:not(.top-label)').fadeToggle();
});
$('.bottom-label').click((e) => {
cube.toggleClass('locked locked-top');
$('.labels div:not(.bottom-label)').fadeToggle();
});
$('.left-label').click((e) => {
cube.toggleClass('locked locked-right');
$('.labels div:not(.left-label)').fadeToggle();
});
$('.right-label').click((e) => {
cube.toggleClass('locked locked-left');
$('.labels div:not(.right-label)').fadeToggle();
});
/* Variables */
:root {
--cube-scale: 60vh;
}
/* Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Montserrat", sans-serif;
overflow: hidden;
}
.cube div {
padding: 10vh;
border: 5px solid blue;
transition: all 500ms ease-in-out;
}
.cube div h1 {
font-family: "Carter One", cursive;
}
.cube div p {
font-size: 14px;
}
/* Labels */
.labels {
color: black;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.labels div {
transition: all 500ms ease-in-out;
}
.left-label {
position: absolute;
top: 50%;
left: 0;
transform: translate(0, -50%);
padding: 20px 10px;
}
.right-label {
position: absolute;
top: 50%;
right: 0;
transform: translate(0, -50%);
padding: 20px 10px;
}
.top-label {
position: absolute;
top: 0;
left: 50%;
width: 100%;
text-align: center;
transform: translate(-50%, 0);
padding: 10px 20px;
}
.bottom-label {
position: absolute;
bottom: 0;
left: 50%;
width: 100%;
text-align: center;
transform: translate(-50%, 0);
padding: 10px 20px;
}
/* 3D Cube */
.wrapper {
width: var(--cube-scale);
height: var(--cube-scale);
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
perspective: calc(var(--cube-scale)*4);
}
.cube {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
}
/*here*/
.cube.locked.locked-bottom {
transition: all 500ms ease-in-out;
transform:rotateY(0) rotateX(90deg) translateY(calc(var(--cube-scale) / -2)) !important;
}
.locked-bottom .bottom {
height: 100vh;
width: 100vw;
transform: rotateX(-90deg) translateZ(calc(var(--cube-scale) / 2)) translate(calc(((100vw/2) - (var(--cube-scale)/2)) * -1), calc(((100vh/2) - (var(--cube-scale)/2)) * -1));
}
/* here */
.cube.locked.locked-top {
transition: all 500ms ease-in-out;
transform:rotateY(0) rotateX(-90deg) translateY(calc(var(--cube-scale) / 2)) !important;
}
.locked-top .top {
height: 100vh;
width: 100vw;
transform:rotateY(0) rotateX(90deg) translateZ(calc(var(--cube-scale) / 2)) translate(calc(((100vw/2) - (var(--cube-scale)/2)) * -1), calc(((100vh/2) - (var(--cube-scale)/2)) * -1));
}
.cube.locked.locked-right {
transition: all 500ms ease-in-out;
transform: rotateY(-90deg) translateX(calc(var(--cube-scale) / -2)) !important;
}
.locked-right .right {
height: 100vh;
width: 100vw;
transform: rotateY(90deg) translateZ(calc(var(--cube-scale) / 2)) translate(calc(((100vw/2) - (var(--cube-scale)/2)) * -1), calc(((100vh/2) - (var(--cube-scale)/2)) * -1));
}
.cube.locked.locked-left {
transition: all 500ms ease-in-out;
transform: rotateY(90deg) translateX(calc(var(--cube-scale) / 2)) !important;
}
.locked-left .left {
height: 100vh;
width: 100vw;
transform: rotateY(-90deg) translateZ(calc(var(--cube-scale) / 2)) translate(calc(((100vw/2) - (var(--cube-scale)/2)) * -1), calc(((100vh/2) - (var(--cube-scale)/2)) * -1));
}
.cube div {
transform-origin: calc(var(--cube-scale)/2) calc(var(--cube-scale)/2);
position: absolute;
width: var(--cube-scale);
height: var(--cube-scale);
}
.front {
background: rgba(255,0,0,0.7);
transform: rotateY(0deg) translateZ(calc(var(--cube-scale) / 2));
}
.right {
background-color: rgba(0,255,0,0.7);
transform: rotateY(90deg) translateZ(calc(var(--cube-scale) / 2));
}
.back {
background-color: rgba(0,0,255,0.7);
transform: rotateY(180deg) translateZ(calc(var(--cube-scale) / 2));
}
.left {
background-color: rgba(255,165,0,0.7);
transform: rotateY(-90deg) translateZ(calc(var(--cube-scale) / 2));
}
.top {
background-color: rgba(128,0,128,0.7);
transform: rotateX(90deg) translateZ(calc(var(--cube-scale) / 2));
}
.bottom {
background-color: rgba(255,0,255,0.7);
transform: rotateX(-90deg) translateZ(calc(var(--cube-scale) / 2));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<div class="cube">
<div class="front"></div>
<div class="back"></div>
<div class="left"></div>
<div class="right"></div>
<div class="top"></div>
<div class="bottom"></div>
</div>
</div>
<div class="labels">
<div class="left-label"><span>Right</span></div>
<div class="right-label"><span>Left</span></div>
<div class="top-label"><span>Bottom</span></div>
<div class="bottom-label"><span>Top</span></div>
</div>
Related question to get more details about how interpolation is done between transform
: Weird behavior when rotating an element on hover
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With