I have a simple menu that's currently using CSS Grid that I want to be animated. However, grid-template-columns
can't be smoothly animated. I am now doing the menu completely different, but in my search for a solution I came across something that I can't quite figure out. My first idea, although it might not a good idea at all and probably isn't a suitable alternative for keyframe animations, was to update styles in a while loop to animate the increasing size of the item. It doesn't work.
The relevant JS:
$('#select-oak').hover(function () {
let start = new Date().getTime();
let elapsed = 0;
while (elapsed <= 1000) {
elapsed = new Date().getTime() - start;
let val = elapsed / 1000;
if (elapsed % 100 === 0) {
$('.selector-block').css('grid-template-columns', `${1.0 + val}fr 1fr 1fr 1fr`);
console.log(1 + val);
console.log(elapsed);
}
}
}
The CSS:
.selector-block {
position: relative;
overflow: hidden;
top: -32.5vh;
background-color: white;
gap: 5px;
width: 100vw;
height: 90vh;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
clip-path: polygon(0 36%, 100% 0, 100% 60%, 0% 100%);
z-index: 3;
}
#select-oak {
position: relative;
overflow: hidden;
text-align: center;
background-color: rgb(94, 80, 21);
top: 21.5%;
width: 100%;
height: 200%;
}
#select-oak img {
position: relative;
left: -10vw;
transform: scale(1.06);
filter: blur(5px);
}
#select-oak img:hover {
filter: none;
transform: scale(1.1);
transition: ease-in-out 0.5s;
}
It seemed like an unconventional approach so I wasn't surprised it didn't work, but since I am relatively new to CSS, JS, and JQuery, I'm curious as to what is stopping this from working just to help my understanding of working with JQuery and CSS. Here's the result. As you can see, it doesn't update until the last pass in the loop, even though the correct values are being printed out.
So I have two questions:
The while
loop is blocking the render process, because it is synchronous.
I've come up with a way using CSS transitions, being agnostic of the number of child elements. It also handles the mouse leaving and even moving from one menu item to another.
$('.selector-block > *').hover(function() {
let frValues = Array($(this).siblings().length+1).fill('1fr');
frValues[$(this).prevAll().length] = '2fr';
$('.selector-block').css({
'grid-template-columns': frValues.join(' ')
});
}, function() {
let frValues = Array($(this).siblings().length+1).fill('1fr');
$('.selector-block').css({
'grid-template-columns': frValues.join(' ')
});
});
.selector-block {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
transition: all 1s ease-in-out;
}
.selector-block > * {
border: solid red 1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="selector-block"><div id="select-oak">select-oak</div><div>foobar</div><div>baz</div><div>bat</div></div>
You could do this with CSS only, no JavaScript, if the number of child elements is fixed, via the Actually, you couldn't, as you would need to trigger the parent's property animation from the hover of a child, which is not possible.:nth-child()
selector and :hover()
as well, but this way, you can dynamically add elements to your menu any time.
Old answer:
You're looking for window.requestAnimationFrame()
. Here's a basic demonstration of how it would work. You'll have to tweak your values for a smoother transition and handle the mouse leaving the element of course.
let start;
let elapsed = 0;
let rafHandle;
function hoverAnimation() {
if (elapsed <= 1000) {
elapsed = new Date().getTime() - start;
let val = elapsed / 1000;
$('.selector-block').css('grid-template-columns', `${1.0 + val}fr 1fr 1fr 1fr`);
//console.log(1 + val);
//console.log(elapsed);
window.setTimeout(function() {
rafHandle = window.requestAnimationFrame(hoverAnimation);
}, 1000/60); // about 60 FPS
}
}
$('#select-oak').hover(function() {
start = new Date().getTime();
elapsed = 0;
rafHandle = window.requestAnimationFrame(hoverAnimation);
}, function() {
// handle mouse leave animation here instead
window.cancelAnimationFrame(rafHandle);
elapsed = Infinity;
$('.selector-block').css('grid-template-columns', `1fr 1fr 1fr 1fr`);
});
.selector-block {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="selector-block"><div id="select-oak">select-oak</div><div>foobar</div><div>baz</div><div>bat</div></div>