Code Review Asked by Nico Shultz on November 25, 2021
I am trying to recreate a neat effect that I saw posted on Stack Overflow as a GIF .
I am trying to recreate this with CSS and JavaScript and I have created a similar effect with a button press.
I know there is a better way to position the divs since I am working with 3d moving and rotating. I need to have a formula that is using cos and sin to calculate the actual position it needs to be in. My math sadly isn’t that good so I calculate it by getting the actual height after the transform
and with that I calculate the top
value the next elements need to get.
const shiftAmount = 2;
const elements = document.querySelectorAll(".fold");
const elementBase = elements[0].getBoundingClientRect();
let currentState = "open";
let active = false;
let $foldStateLabel = $("#foldButton span");
document.onkeydown = function (e) {
switch (e.which) {
case 37: // left
break;
case 38: // up
addDeg();
break;
case 39: // right
break;
case 40: // down
minDeg();
break;
default: return; // exit this handler for other keys
}
e.preventDefault(); // prevent the default action (scroll / move caret)
};
$("#foldButton").click(function () {
if (currentState == "open" && !active) {
active = true;
let interval = setInterval(function () {
if (!minDeg()) {
clearInterval(interval);
currentState = "closed";
active = false;
$foldStateLabel.html("Out");
}
}, 10);
} else if (currentState == "closed" && !active) {
active = true;
let interval = setInterval(function () {
if (!addDeg()) {
clearInterval(interval);
currentState = "open";
active = false;
$foldStateLabel.html("In");
}
}, 10);
}
});
let rotation = 0;
function addDeg() {
let posDiff = 0;
let origin;
if (Math.abs(rotation) > 0) {
rotation += shiftAmount;
for (var i = 0; i < elements.length; i++) {
let rot;
elem = elements[i];
$element = $(elem);
if (i % 2 == 0) {
origin = "top";
rot = rotation;
} else {
origin = "bottom";
rot = Math.abs(rotation);
}
$element.css(createTransformCss(rot, origin, posDiff));
posDiff += elem.getBoundingClientRect().height - elementBase.height;
if (origin == "bottom") {
$element.css(createTransformCss(rot, origin, posDiff));
}
}
return true;
} else {
return false;
}
}
function minDeg() {
let posDiff = 0;
let origin;
if (Math.abs(rotation) < 90 && Math.abs(rotation) >= 0) {
rotation -= shiftAmount;
for (var i = 0; i < elements.length; i++) {
let rot;
elem = elements[i];
$element = $(elem);
if (i % 2 == 0) {
origin = "top";
rot = rotation;
} else {
origin = "bottom";
rot = Math.abs(rotation);
}
$element.css(createTransformCss(rot, origin, posDiff));
posDiff += elem.getBoundingClientRect().height - elementBase.height;
if (origin == "bottom") {
$element.css(createTransformCss(rot, origin, posDiff));
}
}
return true;
} else {
return false;
}
}
function createTransformCss(rotationDeg, transformOrigin, top, perspective = 200) {
var shadow = 0;
shadow = 140 - (Math.abs(rotationDeg) * 2);
var boxShadow = 'rgba(0, 0, 0, 0.75) 0px 0px 151px -' + shadow + 'px inset';
if (transformOrigin == "bottom") {
shadow = 370 - (Math.abs(rotationDeg) * 3);
boxShadow = 'rgba(0, 0, 0, 0.75) 0px 200px 151px -' + shadow + 'px inset';
}
if(Math.abs(rotationDeg) == 0) {
boxShadow = "none";
}
return {
'top': top + "px",
'transform-origin': transformOrigin,
'transform': 'perspective(' + perspective + 'px) rotateX(' + (rotationDeg) + 'deg)',
'box-shadow': boxShadow
}
}
.container {
max-width: 500px;
margin: 0 auto;
}
.fold-items {
width: 200px;
display: flex;
flex-direction: column;
}
.fold-items section {
height: 75px;
position: relative;
background-color: bisque;
}
.fold-items section.visible {
display: flex;
border-bottom: 2px solid black;
}
.fold-items section.visible a {
margin: auto;
color: black;
font-family: 'Roboto', sans-serif;
font-weight: 700;
text-transform: uppercase;
text-decoration: none;
padding: 5px 10px;
border: 2px solid black;
transition: all .3s;
}
.fold-items section.visible a:hover {
background-color: rgba(0,0,0, 0.3);
border: 4px solid black;
color: white;
}
.fold-items section .inner {
height: 100%;
width: 100%;
position: relative;
}
.fold-items section:nth-of-type(5n + 3) {
background-image: url('https://via.placeholder.com/200x225');
background-size: cover;
background-position: top;
}
.fold-items section:nth-of-type(5n + 4) {
background-image: url('https://via.placeholder.com/200x225');
background-size: cover;
background-position: center;
}
.fold-items section:nth-of-type(5n + 5) {
background-image: url('https://via.placeholder.com/200x225');
background-size: cover;
background-position: bottom;
}
<!DOCTYPE html>
<html lang="en">
<head>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="css/style.css">
<script src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
<div class="fold-wrapper">
<div id="fold-items" class="fold-items">
<section class="visible">
<a id="foldButton" href="#">Fold <span>In</span></a>
</section>
<section class="fold">
</section>
<section class="fold image">
</section>
<section class="fold image">
</section>
<section class="fold image">
</section>
</div>
</div>
</div>
</body>
<script src="js/script.js"></script>
</html>
You can use my snippet by pressing the button or you can use the arrow keys up and down to open or close it in a single step.
UPDATE:
I’ve updated the effect and it now works on scroll like the example.
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP