Reputation: 500
I'm trying to center a card element when it is clicked, to highlight it and separate if from the other cards, using CSS transforms. I know CSS transform is relative to the element's location, but I have to use transform and not top/left to avoid repositioning of the other cards. I've tried to calculate the correct offset using JavaScript, like so:
const cardRect = cardElement.getBoundingClientRect();
const offsetX = window.innerWidth / 2 - cardRect.x - cardRect.width / 2;
const offsetY = window.innerHeight / 2- cardRect.y - cardRect.height / 2;
However that did not work, because the bounding client rect changes all the time, as I have a scale(1.1) on card hover.
So how can I move the clicked card to the center, without offsetting all the other ones?
The cards are created like so in the HTML:
<div class='container'>
<div class='movie'>
<img :src='/images/1.png'>
<h3>Harry Potter and the Sorcerer's Stone</h3>
</div>
<div class='movie'>
<img :src='/images/2.png'>
<h3>Harry Potter and the Chamber of Secrets</h3>
</div>
<div class='movie'>
<img :src='/images/3.png'>
<h3>Harry Potter and the Prisoner of Azkaban</h3>
</div>
etc...
</div>
And everything is styled like so:
.container {
display: flex;
flex-flow: row wrap;
}
.movie {
width: min-content;
flex: 1;
}
.movie:not(.selected-movie):hover { /* .selected-movie is a class applied to the clicked card */
transform: scale(1.1);
box-shadow: 10px 10px 32px 0px rgba(0,0,0,0.4),
-10px -10px 32px 0px rgba(0,0,0,0.4);
}
.selected-movie {
transform: translate(offsetX, offsetY); /* Here are the offsets needed */
flex: 0;
display: grid;
grid-template-areas:
'poster title'
'poster description';
}
Here is a JSFiddle showing the problem: https://jsfiddle.net/u6tpbjna/5/
Upvotes: 1
Views: 850
Reputation: 6119
Some of your other CSS styles for .selected-movie
were throwing off the un-selected cards' styles when the clicked movie is selected, so I've temporarily commented out those styles. Regardless, I believe this should answer the question you're asking about the using those offsets in the CSS.
This is a great opportunity to use CSS variables, which you can set on pageload and window resize using JavaScript. From there, everything else can be done in CSS.
See below:
const cards = Array.from(document.querySelectorAll('.movie'));
const setCardOffsets = () => {
for (card of cards) {
const cardRect = card.getBoundingClientRect();
const cardOffsetX = window.innerWidth / 2 - cardRect.x - cardRect.width / 2;
const cardOffsetY = window.innerHeight / 2- cardRect.y - cardRect.height / 2;
card.style.setProperty('--offset-x', cardOffsetX + "px");
card.style.setProperty('--offset-y', cardOffsetY + "px");
}
}
setCardOffsets();
window.onresize = setCardOffsets;
document.addEventListener('click', e => {
if (e.target && e.target?.matches('.movie, .movie *')) {
const clickedCard = e.target.matches('.movie') ? e.target : e.target.closest('.movie');
if (clickedCard.classList.contains('selected-movie')) {
clickedCard.classList.remove('selected-movie');
return;
}
cards.forEach(card => card.classList.remove('selected-movie'));
clickedCard.classList.add('selected-movie');
}
});
.container {
display: flex;
flex-flow: row wrap;
}
.movie {
transform: scale(1) translate(0px, 0px);
width: min-content;
flex: 1;
position: relative;
transition: all 0.2s ease-out;
}
.movie:not(.selected-movie):hover {
transform: scale(1.1) translate(0px, 0px);
box-shadow: 10px 10px 32px 0px rgba(0,0,0,0.4),
-10px -10px 32px 0px rgba(0,0,0,0.4);
z-index: 2;
}
.selected-movie {
transform: scale(1.1) translate(var(--offset-x), var(--offset-y));
/*flex: 0;
display: grid;
grid-template-areas:
'poster title'
'poster description';*/
z-index: 1;
}
<div class='container'>
<div class='movie'>
<img src='/images/1.png'>
<h3>Harry Potter and the Sorcerer's Stone</h3>
</div>
<div class='movie'>
<img src='/images/2.png'>
<h3>Harry Potter and the Chamber of Secrets</h3>
</div>
<div class='movie'>
<img src='/images/3.png'>
<h3>Harry Potter and the Prisoner of Azkaban</h3>
</div>
</div>
I hope this helps! Let me know if you have any questions about my implementation of this.
Upvotes: 3