使用 3D 动画进一步向下滚动页面时,元素的形状会失真



我正在开发一款适用于移动设备和桌面的内存游戏。为了翻转卡片,我正在使用3D动画。动画在桌面上看起来不错,但在移动设备上时,您需要向下滚动才能看到卡片。但是你越向下滚动页面,动画就越"失真"。以下是一些可视化问题的 gif:

  • 在桌面/大屏幕上:

    https://gyazo.com/bc0ee776b6b562f00411eda9efff92b5 在移动设备上

  • 向下滚动时:

    https://gyazo.com/e2e56190b8baffd548427aa8715630b3

我的代码:

/*----------------------------create list of memory-cards*/
const cards = document.querySelectorAll(
".memory-card,.memory-card2,.memory-card3"
);
let hasFlippedCard = false;
let firstCard, secondCard;
let lockBoard = false;
/*----------------------------add flip class when card is clicked*/
function flipCard() {
if (lockBoard) return;
if (this === firstCard) return;
this.classList.add("flip");
if (!hasFlippedCard) {
/*----------------------------on first click*/
hasFlippedCard = true;
firstCard = this;
return;
}
/*----------------------------on second click*/
secondCard = this;
checkForMatch();
}
function checkForMatch() {
let Match = firstCard.dataset.frontface === secondCard.dataset.frontface;

Match ? disableCards() : unflipCards();
}
function disableCards() {
firstCard.removeEventListener("click", flipCard);
secondCard.removeEventListener("click", flipCard);
resetBoard();
}
function unflipCards() {
/*----------------------------if it is not a match:*/
lockBoard = true; // lock the board
setTimeout(() => {
firstCard.classList.remove("flip");
secondCard.classList.remove("flip");
resetBoard(); //unlock board when cards have flipped
}, 1500); //timeout to see the front face when not a match
}
function resetBoard() {
[hasFlippedCard, lockBoard] = [false, false];
[firstCard, secondCard] = [null, null];
}
(function shuffle() {
cards.forEach((card) => {
let randomPosition = Math.floor(Math.random() * 24); //Math.random returns random number between 0 and 1 excluding 1, so * 12 to get 12 numbers
card.style.order = randomPosition;
});
})(); //IIFE so the cards get shuffled at the start of the game
cards.forEach((card) => card.addEventListener("click", flipCard));
* {
padding: 0;
margin: 0;
box-sizing: border-box;
cursor: url(http://cur.cursors-4u.net/games/gam-14/gam1340.cur), auto;
}
html {
height: 100%;
}
@font-face {
font-family: leaguefont;
src: url("../fonts/font.ttf");
}
body {
height: 100%;
min-height: 100%;
display: flex;
flex-direction: column;
background-image: url("../images/other/background.png");
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
background-attachment: fixed;
}

/*--------------------------------------------navbar*/
.nav-link {
display: block;
padding: 0.5rem 5rem;
}
a {
font-family: leaguefont;
background: -webkit-linear-gradient(#ff9900, #ffff66);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 4rem;
text-transform: capitalize;
}

/*--------------------------------------------memory card containers*/
.memory-game1,
.memory-game2,
.memory-game3 {
min-height: 40rem;
margin: auto;
display: flex;
flex-wrap: wrap;
perspective: 62.5rem;
max-width: 100vw;
justify-content: center;
}
.memory-game1 {
width: 40rem;
}
.memory-game2 {
width: 60rem;
}
.memory-game3 {
width: 80rem;
}

/*--------------------------------------------memory cards*/
.memory-card,
.memory-card2,
.memory-card3 {
height: calc(33.333% - 0.625rem);
margin: 0.313rem;
position: relative;
transform: scale(1);
transform-style: preserve-3d;
transition: transform 0.5s;
min-width: 149px;
}
.memory-card {
width: calc(25% - 0.625rem);
}
.memory-card2 {
width: calc(16.666% - 0.625rem);
}
.memory-card3 {
width: calc(12.5% - 0.625rem);
}
.front-face,
.back-face {
width: 100%;
height: 100%;
padding: 1.25rem;
position: absolute;
border-radius: 0.513rem;
background-image: linear-gradient(#1c7ccc, #00ccff);
backface-visibility: hidden;
}

/*--------------------------------------------click animation*/
div.memory-card:active,
div.memory-card2:active,
div.memory-card3:active {
transform: scale(0.97);
transition: transform 0.2s;
}
.memory-card.flip,
.memory-card2.flip,
.memory-card3.flip {
transform: rotateY(180deg);
}
.front-face {
transform: rotateY(180deg);
}

/*--------------------------------------------Footer*/
footer {
width: 100%;
padding: 3.75rem 0rem;
font-family: leaguefont;
background: -webkit-linear-gradient(#ff9900, #ffff66);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 3rem;
text-transform: capitalize;
text-align: center;
}

/*--------------------------------------------Media queries*/
@media screen and (max-width: 1200px) {
a {
font-size: 2.5rem;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>League of Memory</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link rel="stylesheet" href="assets/css/style.css" type="text/css" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous" />
<script defer src="assets/js/cards.js"></script>
</head>
<!-------------------------------------------------navbar-->
<nav>
<ul class="nav justify-content-center">
<li class="nav-item">
<a class="nav-link active" href="index.html">level one</a>
</li>
<li class="nav-item">
<a class="nav-link" href="leveltwo.html">level two</a>
</li>
<li class="nav-item">
<a class="nav-link" href="levelthree.html">level three</a>
</li>
</ul>
</nav>
<body>
<!-----------------------------------------------memory cards (12)-->
<section class="memory-game1">
<div class="memory-card" data-frontface="Caitlyn">
<img class="front-face" src="assets/images/champions/caitlyn.png" alt="Caitlyn" />
<img class="back-face" src="assets/images/other/cardlogo.png" alt="League Logo" />
</div>
<div class="memory-card" data-frontface="Caitlyn">
<img class="front-face" src="assets/images/champions/caitlyn.png" alt="Caitlyn" />
<img class="back-face" src="assets/images/other/cardlogo.png" alt="League Logo" />
</div>
<div class="memory-card" data-frontface="Darius">
<img class="front-face" src="assets/images/champions/darius.png" alt="Darius" />
<img class="back-face" src="assets/images/other/cardlogo.png" alt="League Logo" />
</div>
<div class="memory-card" data-frontface="Darius">
<img class="front-face" src="assets/images/champions/darius.png" alt="Darius" />
<img class="back-face" src="assets/images/other/cardlogo.png" alt="League Logo" />
</div>
<div class="memory-card" data-frontface="Ezreal">
<img class="front-face" src="assets/images/champions/ezreal.png" alt="Ezreal" />
<img class="back-face" src="assets/images/other/cardlogo.png" alt="League Logo" />
</div>
<div class="memory-card" data-frontface="Ezreal">
<img class="front-face" src="assets/images/champions/ezreal.png" alt="Ezreal" />
<img class="back-face" src="assets/images/other/cardlogo.png" alt="League Logo" />
</div>
<div class="memory-card" data-frontface="Gangplank">
<img class="front-face" src="assets/images/champions/gangplank.png" alt="Gangplank" />
<img class="back-face" src="assets/images/other/cardlogo.png" alt="League Logo" />
</div>
<div class="memory-card" data-frontface="Gangplank">
<img class="front-face" src="assets/images/champions/gangplank.png" alt="Gangplank" />
<img class="back-face" src="assets/images/other/cardlogo.png" alt="League Logo" />
</div>
<div class="memory-card" data-frontface="Graves">
<img class="front-face" src="assets/images/champions/graves.png" alt="Graves" />
<img class="back-face" src="assets/images/other/cardlogo.png" alt="League Logo" />
</div>
<div class="memory-card" data-frontface="Graves">
<img class="front-face" src="assets/images/champions/graves.png" alt="Graves" />
<img class="back-face" src="assets/images/other/cardlogo.png" alt="League Logo" />
</div>
<div class="memory-card" data-frontface="Katarina">
<img class="front-face" src="assets/images/champions/katarina.png" alt="Katarina" />
<img class="back-face" src="assets/images/other/cardlogo.png" alt="League Logo" />
</div>
<div class="memory-card" data-frontface="Katarina">
<img class="front-face" src="assets/images/champions/katarina.png" alt="Katarina" />
<img class="back-face" src="assets/images/other/cardlogo.png" alt="League Logo" />
</div>
</section>
<footer>
<div>
<a href="index.html">
RESET
</a>
</div>
</footer>
</body>
</html>

有没有办法使动画在不同的屏幕尺寸上保持一致?

我认为您应该将每张卡包装在另一个div中,并将perspective属性应用于该卡:

<div class="card-wrapper">
<div class="memory-card" data-frontface="Katarina">
<img class="front-face" src="assets/images/champions/katarina.png" alt="Katarina" />
<img class="back-face" src="assets/images/other/cardlogo.png" alt="League Logo" />
</div>
</div>
.card-wrapper {
perspective: 62.5rem;
}

如果将透视应用于卡片的公共父级,则每张卡片到透视原点的距离将不同。为每张卡提供自己的来源应该可以解决您的问题。