Guest
Wie verschiebe ich eine Folie mit der Maus und auf meinem Telefon?
Post
by Guest » 06 Jan 2025, 22:36
Wie kann ich eine Lösung implementieren, die es Benutzern ermöglicht, mit der Maus auf Desktop-Geräten und mit Touch-Gesten (Fingerstreichen) auf Mobilgeräten durch Folien zu navigieren, ohne auf Bibliotheken von Drittanbietern angewiesen zu sein? Ich möchte, dass die Navigation intuitiv und reibungslos ist und sowohl Mauszieh- als auch Touch-Ereignisse unterstützt. Das Ziel besteht darin, einen reaktionsfähigen Slider zu erstellen, der nahtlos über verschiedene Bildschirmgrößen und Eingabemethoden hinweg funktioniert
Code: Select all
const paginationCirclesContainerEl = document.querySelector('.pagination_circles_container');
const btnPrevPositionEl = document.querySelector('.btn_prev_position');
const btnNextPositionEl = document.querySelector('.btn_next_position');
const sliderRobotsTrackEl = document.querySelector('.slider_robots_track');
const sliderRobotWrapperEls = [...document.querySelectorAll('.slider_robot_wrapper')];
// Variables for working with slides
const visibleSlidesCount = 3;
let currentSlideIndex = visibleSlidesCount;
let isAnimating = false;
// Creating pagination circles equal to the number of slides
const paginationCircles = [];
const createPaginationCircles = () => {
const li = document.createElement('li');
li.className = 'pagination_circle';
paginationCirclesContainerEl.appendChild(li);
paginationCircles.push(li);
};
const addPagination = () => {
sliderRobotWrapperEls.forEach(createPaginationCircles);
paginationCircles[0].classList.add('circle_active');
};
addPagination();
// Base slide width
let slideWidth = sliderRobotWrapperEls[0].offsetWidth;
// Cloning the first three slides
const lastThreeSlides = sliderRobotWrapperEls.slice(0, visibleSlidesCount);
lastThreeSlides.forEach(slideEl => {
const clone = slideEl.cloneNode(true);
sliderRobotsTrackEl.append(clone);
});
// Cloning the last three slides
const firstThreeSlides = sliderRobotWrapperEls.slice(-visibleSlidesCount);
firstThreeSlides.forEach(slideEl => {
const clone = slideEl.cloneNode(true);
sliderRobotsTrackEl.insertBefore(clone, sliderRobotWrapperEls[0]);
});
// New list of slides (12)
const allSlides = [...document.querySelectorAll('.slider_robot_wrapper')];
let isDragging = false;
let startX = 0;
let currentTranslate = 0;
let prevTranslate = 0;
sliderRobotsTrackEl.addEventListener('mousedown', e => {
isDragging = true;
startX = e.clientX;
});
sliderRobotsTrackEl.addEventListener('mousemove', e => {
if (!isDragging) return;
const currentX = e.clientX;
const diffX = currentX - startX;
currentTranslate = prevTranslate + diffX;
updateSliderPosition();
});
sliderRobotsTrackEl.addEventListener('mouseup', e => {
if (!isDragging) return;
isDragging = false;
prevTranslate = currentTranslate;
});
// Dynamically update the slide width on resize
const updateSlideWidth = () => {
slideWidth = allSlides[0].offsetWidth;
updateSliderPosition();
};
window.addEventListener('resize', updateSlideWidth);
// Move the track with slides
const updateSliderPosition = (withTransition = true, index) => {
if (index) {
removeActiveClass(currentSlideIndex);
currentSlideIndex = index;
addActiveClass(currentSlideIndex);
}
const offset = -currentSlideIndex * slideWidth;
sliderRobotsTrackEl.style.transition = withTransition ? 'transform .5s' : 'none';
sliderRobotsTrackEl.style.transform = `translate3d(${offset}px, 0px, 0px)`;
};
// Navigation through pagination circles
paginationCircles.forEach((circleEl, index) => {
circleEl.addEventListener('click', () => {
updateSliderPosition(true, index + visibleSlidesCount);
});
});
// Add active circle class to the current slide
const addActiveClass = (currentSlideIndexCircle) => {
let normalizedIndex;
if (currentSlideIndexCircle) {
normalizedIndex = (currentSlideIndexCircle - visibleSlidesCount + paginationCircles.length) % paginationCircles.length;
} else {
normalizedIndex = (currentSlideIndex - visibleSlidesCount + paginationCircles.length) % paginationCircles.length;
}
paginationCircles[normalizedIndex].classList.add('circle_active');
};
// Remove active circle class from the previous slide
const removeActiveClass = (currentSlideIndexCircle) => {
let normalizedIndex;
if (currentSlideIndexCircle) {
normalizedIndex = (currentSlideIndexCircle - visibleSlidesCount + paginationCircles.length) % paginationCircles.length;
} else {
normalizedIndex = (currentSlideIndex - visibleSlidesCount + paginationCircles.length) % paginationCircles.length;
}
paginationCircles[normalizedIndex].classList.remove('circle_active');
};
// Show the next slide
const nextSlide = () => {
// Block click until animation is finished
if (isAnimating) return;
isAnimating = true;
setTimeout(() => {
isAnimating = false;
}, 500);
removeActiveClass();
currentSlideIndex++;
updateSliderPosition();
addActiveClass();
// Quick rewind when reaching the last clone
if (currentSlideIndex === allSlides.length - visibleSlidesCount) {
setTimeout(() => {
currentSlideIndex = visibleSlidesCount;
updateSliderPosition(false);
}, 500);
}
};
// Show the previous slide
const prevSlide = () => {
// Block click until animation is finished
if (isAnimating) return;
isAnimating = true;
setTimeout(() => {
isAnimating = false;
}, 500);
removeActiveClass();
currentSlideIndex--;
updateSliderPosition();
addActiveClass();
// Quick rewind when reaching the first clone
if (currentSlideIndex === 0) {
setTimeout(() => {
currentSlideIndex = allSlides.length - visibleSlidesCount * 2;
updateSliderPosition(false);
}, 500);
}
};
// Event handlers for the buttons
btnNextPositionEl.addEventListener('click', nextSlide);
btnPrevPositionEl.addEventListener('click', prevSlide);
// Initialize the initial position of the slider
updateSliderPosition(false);
Code: Select all
ol {
list-style: none;
}
.slider_robots {
display: flex;
flex-direction: column;
align-items: center;
padding: 80px 0 0 20px;
margin: 0 20px 80px 0;
}
.slider_robots_container {
position: relative;
width: clamp(414px, 100%, 1282px);
margin-bottom: 45px;
overflow: hidden;
}
.slider_btn_container {
position: absolute;
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
background-color: #f7f7f7;
border-radius: 100%;
width: 54px;
height: 54px;
cursor: pointer;
}
.btn_prev_position {
left: 0;
top: 380px;
}
.btn_next_position {
right: 0;
top: 380px;
}
.slider_btn {
width: 11px;
height: 11px;
border-top: 2px solid #6a768c;
border-right: 2px solid #6a768c;
}
.btn_prev {
transform: rotate(-135deg);
margin-left: 3px;
}
.btn_next {
transform: rotate(45deg);
margin-right: 3px;
}
.slider_robots_track {
display: flex;
padding: 0 15px;
transition: transform .5s;
}
.slider_robot_wrapper {
display: flex;
flex-direction: column;
flex-grow: 0;
flex-shrink: 0;
flex-basis: calc((100% - 2 * 20px) / 3);
gap: 25px;
padding-right: 20px;
pointer-events: none;
}
.slide_robot {
width: 414px;
height: 414px;
border-radius: 12px;
pointer-events: auto;
}
.pagination_circles_container {
display: flex;
justify-content: center;
width: 100%;
gap: 24px;
}
.pagination_circle {
background-color: #b8edb7;
border-radius: 100%;
width: 10px;
height: 10px;
cursor: pointer;
}
.circle_active {
background-color: #b8edb7;
width: 10px;
height: 10px;
border-radius: 100%;
box-shadow: 0 0 0 7px #447355;
}
@media screen and (max-width: 1024px) {
.slider_robots_container {
width: clamp(462px, 100%, 942px);
}
.slider_robot_wrapper {
flex-basis: calc((100% - 20px) / 2);
}
}
@media screen and (max-width: 768px) {
.slider_robots_container {
width: clamp(100px, 100%, 687px);
}
.slider_robot_wrapper {
flex-basis: 100%;
}
}
1736199402
Guest
Wie kann ich eine Lösung implementieren, die es Benutzern ermöglicht, mit der Maus auf Desktop-Geräten und mit Touch-Gesten (Fingerstreichen) auf Mobilgeräten durch Folien zu navigieren, ohne auf Bibliotheken von Drittanbietern angewiesen zu sein? Ich möchte, dass die Navigation intuitiv und reibungslos ist und sowohl Mauszieh- als auch Touch-Ereignisse unterstützt. Das Ziel besteht darin, einen reaktionsfähigen Slider zu erstellen, der nahtlos über verschiedene Bildschirmgrößen und Eingabemethoden hinweg funktioniert [code]const paginationCirclesContainerEl = document.querySelector('.pagination_circles_container'); const btnPrevPositionEl = document.querySelector('.btn_prev_position'); const btnNextPositionEl = document.querySelector('.btn_next_position'); const sliderRobotsTrackEl = document.querySelector('.slider_robots_track'); const sliderRobotWrapperEls = [...document.querySelectorAll('.slider_robot_wrapper')]; // Variables for working with slides const visibleSlidesCount = 3; let currentSlideIndex = visibleSlidesCount; let isAnimating = false; // Creating pagination circles equal to the number of slides const paginationCircles = []; const createPaginationCircles = () => { const li = document.createElement('li'); li.className = 'pagination_circle'; paginationCirclesContainerEl.appendChild(li); paginationCircles.push(li); }; const addPagination = () => { sliderRobotWrapperEls.forEach(createPaginationCircles); paginationCircles[0].classList.add('circle_active'); }; addPagination(); // Base slide width let slideWidth = sliderRobotWrapperEls[0].offsetWidth; // Cloning the first three slides const lastThreeSlides = sliderRobotWrapperEls.slice(0, visibleSlidesCount); lastThreeSlides.forEach(slideEl => { const clone = slideEl.cloneNode(true); sliderRobotsTrackEl.append(clone); }); // Cloning the last three slides const firstThreeSlides = sliderRobotWrapperEls.slice(-visibleSlidesCount); firstThreeSlides.forEach(slideEl => { const clone = slideEl.cloneNode(true); sliderRobotsTrackEl.insertBefore(clone, sliderRobotWrapperEls[0]); }); // New list of slides (12) const allSlides = [...document.querySelectorAll('.slider_robot_wrapper')]; let isDragging = false; let startX = 0; let currentTranslate = 0; let prevTranslate = 0; sliderRobotsTrackEl.addEventListener('mousedown', e => { isDragging = true; startX = e.clientX; }); sliderRobotsTrackEl.addEventListener('mousemove', e => { if (!isDragging) return; const currentX = e.clientX; const diffX = currentX - startX; currentTranslate = prevTranslate + diffX; updateSliderPosition(); }); sliderRobotsTrackEl.addEventListener('mouseup', e => { if (!isDragging) return; isDragging = false; prevTranslate = currentTranslate; }); // Dynamically update the slide width on resize const updateSlideWidth = () => { slideWidth = allSlides[0].offsetWidth; updateSliderPosition(); }; window.addEventListener('resize', updateSlideWidth); // Move the track with slides const updateSliderPosition = (withTransition = true, index) => { if (index) { removeActiveClass(currentSlideIndex); currentSlideIndex = index; addActiveClass(currentSlideIndex); } const offset = -currentSlideIndex * slideWidth; sliderRobotsTrackEl.style.transition = withTransition ? 'transform .5s' : 'none'; sliderRobotsTrackEl.style.transform = `translate3d(${offset}px, 0px, 0px)`; }; // Navigation through pagination circles paginationCircles.forEach((circleEl, index) => { circleEl.addEventListener('click', () => { updateSliderPosition(true, index + visibleSlidesCount); }); }); // Add active circle class to the current slide const addActiveClass = (currentSlideIndexCircle) => { let normalizedIndex; if (currentSlideIndexCircle) { normalizedIndex = (currentSlideIndexCircle - visibleSlidesCount + paginationCircles.length) % paginationCircles.length; } else { normalizedIndex = (currentSlideIndex - visibleSlidesCount + paginationCircles.length) % paginationCircles.length; } paginationCircles[normalizedIndex].classList.add('circle_active'); }; // Remove active circle class from the previous slide const removeActiveClass = (currentSlideIndexCircle) => { let normalizedIndex; if (currentSlideIndexCircle) { normalizedIndex = (currentSlideIndexCircle - visibleSlidesCount + paginationCircles.length) % paginationCircles.length; } else { normalizedIndex = (currentSlideIndex - visibleSlidesCount + paginationCircles.length) % paginationCircles.length; } paginationCircles[normalizedIndex].classList.remove('circle_active'); }; // Show the next slide const nextSlide = () => { // Block click until animation is finished if (isAnimating) return; isAnimating = true; setTimeout(() => { isAnimating = false; }, 500); removeActiveClass(); currentSlideIndex++; updateSliderPosition(); addActiveClass(); // Quick rewind when reaching the last clone if (currentSlideIndex === allSlides.length - visibleSlidesCount) { setTimeout(() => { currentSlideIndex = visibleSlidesCount; updateSliderPosition(false); }, 500); } }; // Show the previous slide const prevSlide = () => { // Block click until animation is finished if (isAnimating) return; isAnimating = true; setTimeout(() => { isAnimating = false; }, 500); removeActiveClass(); currentSlideIndex--; updateSliderPosition(); addActiveClass(); // Quick rewind when reaching the first clone if (currentSlideIndex === 0) { setTimeout(() => { currentSlideIndex = allSlides.length - visibleSlidesCount * 2; updateSliderPosition(false); }, 500); } }; // Event handlers for the buttons btnNextPositionEl.addEventListener('click', nextSlide); btnPrevPositionEl.addEventListener('click', prevSlide); // Initialize the initial position of the slider updateSliderPosition(false);[/code] [code]ol { list-style: none; } .slider_robots { display: flex; flex-direction: column; align-items: center; padding: 80px 0 0 20px; margin: 0 20px 80px 0; } .slider_robots_container { position: relative; width: clamp(414px, 100%, 1282px); margin-bottom: 45px; overflow: hidden; } .slider_btn_container { position: absolute; z-index: 1000; display: flex; align-items: center; justify-content: center; background-color: #f7f7f7; border-radius: 100%; width: 54px; height: 54px; cursor: pointer; } .btn_prev_position { left: 0; top: 380px; } .btn_next_position { right: 0; top: 380px; } .slider_btn { width: 11px; height: 11px; border-top: 2px solid #6a768c; border-right: 2px solid #6a768c; } .btn_prev { transform: rotate(-135deg); margin-left: 3px; } .btn_next { transform: rotate(45deg); margin-right: 3px; } .slider_robots_track { display: flex; padding: 0 15px; transition: transform .5s; } .slider_robot_wrapper { display: flex; flex-direction: column; flex-grow: 0; flex-shrink: 0; flex-basis: calc((100% - 2 * 20px) / 3); gap: 25px; padding-right: 20px; pointer-events: none; } .slide_robot { width: 414px; height: 414px; border-radius: 12px; pointer-events: auto; } .pagination_circles_container { display: flex; justify-content: center; width: 100%; gap: 24px; } .pagination_circle { background-color: #b8edb7; border-radius: 100%; width: 10px; height: 10px; cursor: pointer; } .circle_active { background-color: #b8edb7; width: 10px; height: 10px; border-radius: 100%; box-shadow: 0 0 0 7px #447355; } @media screen and (max-width: 1024px) { .slider_robots_container { width: clamp(462px, 100%, 942px); } .slider_robot_wrapper { flex-basis: calc((100% - 20px) / 2); } } @media screen and (max-width: 768px) { .slider_robots_container { width: clamp(100px, 100%, 687px); } .slider_robot_wrapper { flex-basis: 100%; } }[/code] [code] [list][/list] [/code]