我正在使用 JavaScript 开发手动循环轮播,其中位置更改是通过链接到 CSS 属性的数据状态属性来管理的。
但是,我遇到了转换问题。当轮播开始循环时,元素会从与预期相反的方向出现。
imgur 上的视频展示:https://imgur.com/Lz1QOiQ
我在这里做错了什么?
var articles = Array.from(document.querySelectorAll('.carousel article'));
var currentArticle = document.querySelector('[data-status="active"]');
var currentIndex = parseInt(currentArticle.dataset.index);
function swapRight() {
var nextArticleIndex = currentIndex + 1;
var nextArticle = document.querySelector(`[data-index="${nextArticleIndex}"]`);
if (nextArticle) {
console.log('swapped to next');
currentArticle.dataset.status = "afterToLeft";
nextArticle.dataset.status = "moveRight";
nextArticle.dataset.status = "active";
currentIndex = nextArticleIndex;
currentArticle = nextArticle;
} else {
restart();
console.log("Restarting");
console.log('currentIndex:', currentIndex);
console.log('nextArticleIndex:', 0);
}
}
function swapLeft() {
var nextArticleIndex = currentIndex - 1;
var nextArticle = document.querySelector(`[data-index="${nextArticleIndex}"]`);
if (nextArticle) {
console.log('swapped to previous');
currentArticle.dataset.status = "afterToRight";
nextArticle.dataset.status = "resetToLeft";
nextArticle.dataset.status = "active";
currentIndex = nextArticleIndex;
currentArticle = nextArticle;
} else {
swapToEnd();
console.log("Swapping to end");
console.log('currentIndex:', currentIndex);
console.log('nextArticleIndex:', 0);
}
}
function restart() {
var nextArticleIndex = 0;
var nextArticle = document.querySelector(`[data-index="${nextArticleIndex}"]`);
if (nextArticle) {
console.log('restarted');
currentArticle.dataset.status = "afterToRight";
nextArticle.dataset.status = "resetToLeft";
nextArticle.dataset.status = "active";
currentIndex = nextArticleIndex;
currentArticle = nextArticle;
} else {
console.error("Failed to restart");
console.log('currentIndex:', currentIndex);
console.log('nextArticleIndex:', nextArticleIndex);
}
}
function swapToEnd() {
var nextArticleIndex = currentIndex + 4;
var nextArticle = document.querySelector(`[data-index="${nextArticleIndex}"]`);
if (nextArticle) {
console.log('swapped to end');
currentArticle.dataset.status = "afterToRight";
nextArticle.dataset.status = "resetToLeft";
nextArticle.dataset.status = "active";
currentIndex = nextArticleIndex;
currentArticle = nextArticle;
} else {
console.error("Failed to restart");
console.log('currentIndex:', currentIndex);
console.log('nextArticleIndex:', nextArticleIndex);
}
}
* {
margin: 0;
box-sizing: border-box;
}
.wrapper {
min-width: 100vh;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
background: #E9E4D9;
font-family: 'Rubik', sans-serif;
}
.container {
display: flex;
flex-direction: row;
gap: 1rem;
align-items: center;
justify-content: space-around;
}
button {
height: 5rem;
width: 5rem;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
border: none;
cursor: pointer;
}
.carousel {
overflow: hidden;
width: 540px;
height: 375px;
position: relative;
background: #F8F5F0;
}
article {
position: absolute;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
padding: 1rem;
justify-content: center;
gap: 1rem;
cursor: pointer;
transition: left 0.8s ease;
}
article img {
object-fit: cover;
width: 100%;
border-radius: 12px;
height: 200px;
}
article div h2 {
font-weight: 800;
background-color: #827055;
color: #CFC0A8;
padding: 10px;
font-size: 40px;
border-radius: 8px;
margin: 0;
}
article div p {
color: #B49D77;
font-size: 22px;
}
article div {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
article[data-status="unknown"] {
left: 100%;
}
article[data-status="active"] {
left: 0;
}
article[data-status="afterToLeft"] {
left: -100%;
}
article[data-status="afterToRight"] {
left: 100%;
}
article[data-status="resetToRight"] {
left: 100%;
transition: none;
opacity: 0;
}
article[data-status="reset"] {
left: 0;
transition: none;
opacity: 0;
}
article[data-status="moveRight"] {
left: 100%;
transition: none;
opacity: 0;
}
article[data-status="resetToLeft"] {
left: -100%;
}
<div class="wrapper">
<div class="container">
<button onclick="swapLeft()">
<svg xmlns="http://www.w3.org/2000/svg" height="2em" viewBox="0 0 448 512"><!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><style>svg{fill:#827055}</style><path d="M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.2 288 416 288c17.7 0 32-14.3 32-32s-14.3-32-32-32l-306.7 0L214.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z"/></svg>
</button>
<div class="carousel" id="carousel">
<article data-status="active" data-index="0">
<img src="https://pause-maison.ouest-france.fr/wp-content/uploads/2023/08/deco-interieur-beige.jpg.webp" alt="img">
<div>
<h2>DAY 1</h2>
<p>friday, november 10th</p>
</div>
</article>
<article data-status="unknown" data-index="1">
<img src="https://cache.marieclaire.fr/data/photo/w1200_h630_ci/66/salon-beige.jpg" alt="img">
<div>
<h2>DAY 2</h2>
<p>saturday, november 11th</p>
</div>
</article>
<article data-status="unknown" data-index="2">
<img src="https://cache.marieclaire.fr/data/photo/w1200_h630_ci/66/salon-beige.jpg" alt="img">
<div>
<h2>DAY 3</h2>
<p>sunday, november 12th</p>
</div>
</article>
<article data-status="unknown" data-index="3">
<img src="https://cache.marieclaire.fr/data/photo/w1200_h630_ci/66/salon-beige.jpg" alt="img">
<div>
<h2>DAY 4</h2>
<p>monday, november 13th</p>
</div>
</article>
<article data-status="unknown" data-index="4">
<img src="https://cache.marieclaire.fr/data/photo/w1200_h630_ci/66/salon-beige.jpg" alt="img">
<div>
<h2>DAY 5</h2>
<p>tuesday, november 14th</p>
</div>
</article>
</div>
<button onclick="swapRight()">
<svg xmlns="http://www.w3.org/2000/svg" height="2em" viewBox="0 0 448 512"><!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><style>svg{fill:#827055}</style><path d="M438.6 278.6c12.5-12.5 12.5-32.8 0-45.3l-160-160c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L338.8 224 32 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l306.7 0L233.4 393.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l160-160z"/></svg>
</button>
</div>
</div>
让我组织动作流程来执行所需的动画。
按向左箭头时左右交换。
按照您的设置方式,基本上所有事情都会同时发生。在重新打开动画之前,您需要给浏览器时间来移动元素的位置。我通过创建一个辅助函数来等待或停止,然后等待任意
0ms
让浏览器移动内容,然后再重新打开转换。对于我的浏览器来说,这已经足够渲染时间了。这可能会为您实现所需的效果,但是我敢打赌大多数生产图像轮播都会使用所需元素的副本来创建循环的错觉。
希望这个解释有帮助!如果您还有任何疑问,请告诉我。
const get = query => [...document.querySelectorAll(query)];
const wait = ms => new Promise(r => setTimeout(r, ms));
const slides = get(".slide");
let activeIndex = slides.findIndex(slide => slide.classList.contains("active"));
const lBtn = get("button.l")[0];
const rBtn = get("button.r")[0];
const incrementIndex = () => {
activeIndex++;
activeIndex = activeIndex % slides.length;
};
const decrementIndex = () => {
if (activeIndex == 0) activeIndex = slides.length;
activeIndex--;
};
const updateDisplay = async ({moveActiveLeft}) => {
const currentActive = get(".slide.active")[0];
currentActive.classList.remove("active");
const newClass = moveActiveLeft ? "left" : "right";
currentActive.classList.add(newClass);
const newActive = slides[activeIndex];
const incomingClass = moveActiveLeft ? "right" : "left";
newActive.classList.add("no-transition");
newActive.classList.remove(newClass);
newActive.classList.add(incomingClass);
//THIS IS THE MAGIC LINE
await wait(0);
newActive.classList.remove("no-transition");
newActive.classList.remove(incomingClass);
newActive.classList.add("active");
};
lBtn.addEventListener(
"click",
() => {
decrementIndex();
updateDisplay({ moveActiveLeft: false });
});
rBtn.addEventListener(
"click",
() => {
incrementIndex();
updateDisplay({moveActiveLeft: true});
});
* {
box-sizing: border-box;
}
body {
display: grid;
place-items: center;
margin: 0;
}
.wrapper {
border: 5px solid black;
height: 100dvh;
aspect-ratio: 1;
position: relative;
overflow: hidden;
}
.controls {
position: absolute;
bottom: 0; left: 0; right: 0;
display: grid;
grid-template-columns: 1fr 1fr;
}
.controls button {
font-size: 30pt; font-weight: bold;
}
.slide {
position: absolute;
height: 100%;
width: 100%;
top: 0;
left: 0;
background-color: var(--bg);
transition: translate 0.5s;
}
.a { --bg: red; }
.b { --bg: orange; }
.c { --bg: yellow; }
.slide:not(.active), .slide.right {
translate: 100% 0;
}
.slide.left {
translate: -100% 0;
}
.no-transition {
transition: none;
}
<div class="wrapper">
<div class="slide a active"></div>
<div class="slide b"></div>
<div class="slide c"></div>
<div class="controls">
<button class="l"> < </button>
<button class="r"> > </button>
<div>
</div>