Background:
The product sister mentioned a demand, hoping to realize the endless rotation of the commodity map in the banner at the top, each rotation only shows three pictures, each picture stays 1.5s after sliding, less than 4 will not rotate.
Demand for resolution
The next step is to split the requirements. According to xiaojie’s requirements, this round broadcast needs to achieve three function points:
- Images scroll and stay for 1.5 seconds each.
- Infinite rotation. It’s called an infinite rotation, which means after the last one is shown, you continue from the first one, like a circle.
- Only three photos are displayed at a time, less than three will not scroll.
Function implementation
Once the requirements are broken down, it’s easy to implement them one by one
Picture scroll
- First, JS controls the movement of elements by controlling the style attribute. In this case, I’m controlling the marginLeft value.
- Secondly, in order to achieve smooth scrolling, you need to set the transition so that the user can feel the scrolling, rather than jump immediately.
Get the element to scrollconst pages = document.querySelector<HTMLElement>('.carousel-contents'); MarginLeft and Transition to smooth scrolling pages. Style. marginLeft =`${nowPositionLeft}px`;
pages.style.transition = 'margin - left 0.7 s linear';
Copy the code
- Then, slide only one image at a time, and make sure the image stays on display for 1.5 seconds before sliding. Here we need a timer to help us solve, we just need a setTimeout of 1.5s, every 1.5s to perform the next image scroll (this can be encapsulated by a function).
const PAGE_WIDTH = 23; // The width of each image
const TRANSLATION_DELAY = 1500; // How long each image stays in rotation
const translatePage = () = > {
const pages = document.querySelector<HTMLElement>('.carousel-contents'); // Get the element to scroll
let nowPositionLeft = 0; // Initializes the marginLeft of the current scrolling element
const run = () = > {
setTimeout(() = >{# set marginLeft and Transition to smooth scrolling pages. Style. marginLeft =`${nowPositionLeft}px`;
pages.style.transition = 'margin - left 0.7 s linear';
run(); // Keep scrolling
}, TRANSLATION_DELAY);
}
run(); // Perform the scroll
}
Copy the code
Infinite shuffling
The real problem is how to skip to the first image when scrolling to the last image (here’s an example of how to scroll left 👈). You can certainly imagine scrolling to the last image by setting marginLeft: 0px, you can go back to the first one again, that’s really the solution, let’s see how it works;
When rolling from the last sheet back to the first sheet, the user clearly sees the element “rolling back”, that is, sliding to the right, which is not acceptable, we want to be able to always scroll in one direction.
MarginLeft: 0px, and transition remains the same as 0.7s Linear. ‘None’ removes the animation and returns to the first slide instantly, making the user unaware of the slide back. But at this moment, the implementation of the first slide can not make the user perceive the sliding process from 4 — >1, and it does not meet the requirements.
After a lot of thinking, we can’t achieve smooth scrolling of 4 — >1, so why not add a 1 after the 4 to simulate sliding of 4 — >1? If the viewport needs to display three images, add 1 and 2 to the end of the 4 (similarly, if you want to display only two photos, just add one to the end of the 4). Since it is added to the end, it must be added to the front to switch back, so it is added to the last scroll element, 4. When 4 scrolls to the first image in the viewport, it switches to the head of the element (the user doesn’t notice any change) and then scrolls again, so the element is instantly back to the first image instead of scrolling in the same direction.
const PAGE_WIDTH = 23; // The width of each image
const TRANSLATION_DELAY = 1500; // How long each image stays in rotation
const translatePage = () = > {
const pages = document.querySelector<HTMLElement>('.carousel-contents'); // Get the element to scroll
let nowPositionLeft = 0; // Initializes the marginLeft of the current scrolling element
const lastChild = pages.lastElementChild;
const firstChild = pages.firstElementChild;
const secondChild = pages.childNodes[1];
// Add the first and last two at the beginning and end of the Pages container
lastChild && pages.insertBefore(lastChild.cloneNode(true), firstChild);
firstChild && pages.appendChild(firstChild.cloneNode(true));
secondChild && pages.appendChild(secondChild.cloneNode(true));
const run = () = > {
setTimeout(() = >{# set marginLeft and Transition to achieve smooth scrollingif (nowPositionLeft + ((pages.childNodes.length - 3) * PAGE_WIDTH) <= 0) {
nowPositionLeft = 0;
pages.style.transition = 'none'; // Remove the animation so that the user does not feel the scroll back to the far left
pages.style.marginLeft = `${nowPositionLeft}px`;
} else {
nowPositionLeft = nowPositionLeft - PAGE_WIDTH;
pages.style.transition = 'margin - left 0.7 s linear';
pages.style.marginLeft = `${nowPositionLeft}px`;
}
run();
}, TRANSLATION_DELAY);
}
run(); // Perform the scroll
}
Copy the code
Go here, almost have been able to achieve the infinite scroll, but careful friend must be able to find, so there is a problem, you will find 4, 1, 2 in the viewport stay 2 TRANSLATION_DELAY length, that is because when the moment to switch to the head (here users perceive that is still in 4, 1, 2), The timer remains TRANSLATION_DELAY, that is, wait for TRANSLATION_DELAY before performing the next operation. Therefore, it needs to be optimized, that is, when switching to the head position, the timer interval is 0, as follows
Limit scrolling when there are not enough images
Based on the above three function points, the final implementation is as follows
const PAGE_WIDTH = 23; // The width of each image
const TRANSLATION_DELAY = 1500; // How long each image stays in rotation
const translatePage = () = > {
const pages = document.querySelector<HTMLElement>('.carousel-contents'); // Get the element to scroll
// If the number is less than 4, no rotation is allowed
if (pages.childNodes.length < 4) return;
let nowPositionLeft = 0; // Initializes the marginLeft of the current scrolling element
const lastChild = pages.lastElementChild;
const firstChild = pages.firstElementChild;
const secondChild = pages.childNodes[1];
// Add the first and last two at the beginning and end of the Pages container
lastChild && pages.insertBefore(lastChild.cloneNode(true), firstChild);
firstChild && pages.appendChild(firstChild.cloneNode(true));
secondChild && pages.appendChild(secondChild.cloneNode(true));
const run = (time = TRANSLATION_DELAY) = > {
setTimeout(() = >{# set marginLeft and Transition to achieve smooth scrollingif (nowPositionLeft + ((pages.childNodes.length - 3) * PAGE_WIDTH) <= 0) {
nowPositionLeft = 0;
pages.style.transition = 'none'; // Remove the animation so that the user does not feel the scroll back to the far left
pages.style.marginLeft = `${nowPositionLeft}px`;
run(0); // The setTime interval is not designed to avoid two consecutive occurrences of this state
} else {
nowPositionLeft = nowPositionLeft - PAGE_WIDTH;
pages.style.transition = 'margin - left 0.7 s linear';
pages.style.marginLeft = `${nowPositionLeft}px`;
run();
}
}, time);
}
run(); // Perform the scroll
}
Copy the code
(Note: The video is not full)