admin管理员组文章数量:1391976
I have a vertical slider and I'm using swiper to navigate through the slides.
Every swiper-slide container height is 100vh.
I have a slide which content is greater than the view height and when scrolling with the mousewheel, I want to first scroll it's content and when the end or top is reached, according to the scroll direction, move to the next or previous slide.
I went through swiper documentation, SO and other pages but didn't find a solution.
Here is the jsfiddle: /
HTML
<div class="swiper-container">
<main class="main swiper-wrapper">
<!-- landing -->
<section id="home" class="swiper-slide">
<div id="particles-js"></div>
<div id="typeIt" class="d-flex align-center"></div>
</section>
<!-- about -->
<section id="about" class="swiper-slide">
<span class="animation">About</span>
</section>
<!-- portfolio -->
<section id="portfolio" class="swiper-slide d-flex flex-wrap col-3">
<div class="card">
card 1
</div>
<div class="card">
card 2
</div>
<div class="card">
card 3
</div>
<div class="card">
card 4
</div>
<div class="card">
card 1
</div>
<div class="card">
card 1
</div>
<div class="card">
card 1
</div>
<div class="card">
card 1
</div>
<div class="card">
card 1
</div>
<div class="card">
card 1
</div>
<div class="card">
card 1
</div>
</section>
<!-- technologies -->
<section id="skills" class="swiper-slide">
Skills
</section>
<!-- contact -->
<section id="contact" class="swiper-slide">
Contact
</section>
</main>
</div>
CSS
body {
margin: 0;
padding: 0;
}
.d-flex {
display: flex;
}
.align-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.justify-between {
justify-content: space-between;
}
.flex-column {
flex-flow: column;
}
.column-reverse {
flex-flow: column-reverse;
}
.flex-wrap {
flex-wrap: wrap;
}
.col-2 > * {
width: calc(100% / 2 - 7.5px);
margin-right: 15px;
margin-bottom: 15px;
}
.col-2 > *:nth-child(2n) {
margin-right: 0;
}
.col-3 > * {
width: calc(100% / 3 - 10px);
margin-right: 15px;
}
.col-3 > *:nth-child(3n) {
margin-right: 0;
}
.col-4 > * {
width: calc(100% / 4 - 10.5px);
margin-right: 14px;
}
.col-4 > *:nth-child(4n) {
margin-right: 0;
}
.card {
height: 300px;
}
.swiper-container {
width: 100% - 120px;
height: 100vh;
margin-left: auto;
margin-right: auto;
}
.swiper-slide {
text-align: center;
font-size: 18px;
background: #fff;
/* Center slide text vertically */
display: -webkit-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
overflow-y: auto;
}
.swiper-pagination {
display: flex;
flex-flow: column;
}
.swiper-pagination-bullet-active {
opacity: 0;
}
.swiper-pagination-bullet {
width: 120px;
height: 96px;
border-radius: 0;
opacity: 0;
}
JS
const swiperConf = {
direction: 'vertical',
slidesPerView: 1,
spaceBetween: -1,
mousewheel: true,
keyboard: true,
pagination: {
el: '.swiper-pagination',
clickable: true,
}
}
var swiper = new Swiper('.swiper-container', swiperConf);
I have a vertical slider and I'm using swiper to navigate through the slides.
Every swiper-slide container height is 100vh.
I have a slide which content is greater than the view height and when scrolling with the mousewheel, I want to first scroll it's content and when the end or top is reached, according to the scroll direction, move to the next or previous slide.
I went through swiper documentation, SO and other pages but didn't find a solution.
Here is the jsfiddle: https://jsfiddle/gentian28/6wdsep1v/13/
HTML
<div class="swiper-container">
<main class="main swiper-wrapper">
<!-- landing -->
<section id="home" class="swiper-slide">
<div id="particles-js"></div>
<div id="typeIt" class="d-flex align-center"></div>
</section>
<!-- about -->
<section id="about" class="swiper-slide">
<span class="animation">About</span>
</section>
<!-- portfolio -->
<section id="portfolio" class="swiper-slide d-flex flex-wrap col-3">
<div class="card">
card 1
</div>
<div class="card">
card 2
</div>
<div class="card">
card 3
</div>
<div class="card">
card 4
</div>
<div class="card">
card 1
</div>
<div class="card">
card 1
</div>
<div class="card">
card 1
</div>
<div class="card">
card 1
</div>
<div class="card">
card 1
</div>
<div class="card">
card 1
</div>
<div class="card">
card 1
</div>
</section>
<!-- technologies -->
<section id="skills" class="swiper-slide">
Skills
</section>
<!-- contact -->
<section id="contact" class="swiper-slide">
Contact
</section>
</main>
</div>
CSS
body {
margin: 0;
padding: 0;
}
.d-flex {
display: flex;
}
.align-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.justify-between {
justify-content: space-between;
}
.flex-column {
flex-flow: column;
}
.column-reverse {
flex-flow: column-reverse;
}
.flex-wrap {
flex-wrap: wrap;
}
.col-2 > * {
width: calc(100% / 2 - 7.5px);
margin-right: 15px;
margin-bottom: 15px;
}
.col-2 > *:nth-child(2n) {
margin-right: 0;
}
.col-3 > * {
width: calc(100% / 3 - 10px);
margin-right: 15px;
}
.col-3 > *:nth-child(3n) {
margin-right: 0;
}
.col-4 > * {
width: calc(100% / 4 - 10.5px);
margin-right: 14px;
}
.col-4 > *:nth-child(4n) {
margin-right: 0;
}
.card {
height: 300px;
}
.swiper-container {
width: 100% - 120px;
height: 100vh;
margin-left: auto;
margin-right: auto;
}
.swiper-slide {
text-align: center;
font-size: 18px;
background: #fff;
/* Center slide text vertically */
display: -webkit-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
overflow-y: auto;
}
.swiper-pagination {
display: flex;
flex-flow: column;
}
.swiper-pagination-bullet-active {
opacity: 0;
}
.swiper-pagination-bullet {
width: 120px;
height: 96px;
border-radius: 0;
opacity: 0;
}
JS
const swiperConf = {
direction: 'vertical',
slidesPerView: 1,
spaceBetween: -1,
mousewheel: true,
keyboard: true,
pagination: {
el: '.swiper-pagination',
clickable: true,
}
}
var swiper = new Swiper('.swiper-container', swiperConf);
Share
Improve this question
asked Mar 28, 2019 at 21:03
gentiangentian
1781 gold badge2 silver badges10 bronze badges
7
- 1 I searched hard in API and forum and could not fix your jsFiddle. It seems very possible with dragging but not with mousewheel. Research nested swipers and try to recreate a new example or see other's mousewheel solutions idangero.us/swiper/forum/#!/?mousewheel – TheWandererLee Commented Mar 28, 2019 at 22:10
- 1 I managed to solve it, here is the jsfiddle: jsfiddle/gentian28/6wdsep1v/20 Expect that sometimes, after a lot of scrolls it suddenly freezes and I have no idea what may be causing it. – gentian Commented Mar 29, 2019 at 15:53
- Really well done on this solution, it wasn't freezing for me at all. – TheWandererLee Commented Mar 29, 2019 at 16:24
- 3 Excellent. I've pressed your findScrollDirectionOtherBrowsers a little. jsfiddle/etu5jc7g Perhaps you should post your solution to their forums. Best of luck to you! – TheWandererLee Commented Mar 29, 2019 at 16:52
- 1 The fiddle is not available. Could you remake it? – Amirhosein Al Commented Feb 17, 2022 at 21:55
3 Answers
Reset to default 2ahh guys! thank you a lot for the input, i had the same issue. but your solutions didnt work for me on mobile devices. so i tried sth own with your input and hacked a little bit. so here is mine: (my first post on stack overflow yee)
const handleScrollInside = (swiper) => {
swiper.on("slideChangeTransitionEnd", () => {
const activeSlide = document.querySelector('.swiper-slide-active');
const hasVerticalScrollbar = activeSlide.scrollHeight > activeSlide.clientHeight;
if (hasVerticalScrollbar) {
const scrollDifferenceTop = activeSlide.scrollHeight - activeSlide.swiperSlideSize;
if (activeSlide.scrollTop === 0) activeSlide.scrollTop += 1;
if (activeSlide.scrollTop === scrollDifferenceTop) activeSlide.scrollTop -= 2;
swiper.mousewheel.disable();
swiper.allowTouchMove = false;
activeSlide.addEventListener("scroll", () => {
if (activeSlide.scrollTop <= 0 || scrollDifferenceTop - activeSlide.scrollTop <= 1 ) {
swiper.mousewheel.enable();
swiper.allowTouchMove = true;
}
});
}
})
}
Also ran into this issue, and the fiddle from @Daryll was very helpful, but with typescript there were some issues getting the 'swiperSlideSize' from an HTMLElement (also using reactjs there are some differences). This worked for me as the event handler for 'onSlideChangeTransitionEnd':
const allowScroll = (swiper: SwiperEvent) => {
var activeIndex = swiper.activeIndex;
var activeSlide = swiper.slides[activeIndex];
var { scrollHeight, clientHeight } = activeSlide;
const diff = scrollHeight - clientHeight;
if (diff > 0) {
const findScroll = (e) => {
const scrollUp = e.deltaY < 0;
if (scrollUp && activeSlide.scrollTop === 0) {
swiper.mousewheel.enable();
activeSlide.removeEventListener("wheel", findScroll);
} else if (!scrollUp && activeSlide.scrollTop === diff) {
swiper.mousewheel.enable();
activeSlide.scrollTop = 0;
activeSlide.removeEventListener("wheel", findScroll);
}
};
activeSlide.addEventListener("wheel", findScroll);
swiper.mousewheel.disable();
}
};
edit: the "SwiperEvent" type is an alias I'm using with import { Swiper as SwiperEvent } from "swiper";
to avoid namespace conflict with import { Swiper } from "swiper/react";
edit 2: for mobile usage, you have to consider 'touchmove' events, which don't (always?) register the 'wheel' event. By disabling 'allowTouchMove' on the swiper, you achieve the same effect on mobile as with mousewheel.disable()
on desktop. Here's some code for that case:
const allowScroll = (swiper: SwiperEvent) => {
var activeIndex = swiper.activeIndex;
var activeSlide = swiper.slides[activeIndex];
var { scrollHeight, clientHeight } = activeSlide;
const diff = scrollHeight - clientHeight;
if (activeSlide.scrollTop === 0) activeSlide.scrollTop = 1;
else if (activeSlide.scrollTop === diff) activeSlide.scrollTop = diff - 1;
if (diff > 0) {
const findScroll = (e) => {
const scrollUp = e.deltaY < 0;
if (
(scrollUp || e.type === "touchmove") &&
activeSlide.scrollTop <= 0
) {
swiper.mousewheel.enable();
swiper.allowTouchMove = true;
activeSlide.removeEventListener("wheel", findScroll);
activeSlide.removeEventListener("touchmove", findScroll);
} else if (
(!scrollUp || e.type === "touchmove") &&
activeSlide.scrollTop >= diff
) {
swiper.mousewheel.enable();
swiper.allowTouchMove = true;
activeSlide.removeEventListener("wheel", findScroll);
activeSlide.removeEventListener("touchmove", findScroll);
}
};
activeSlide.addEventListener("wheel", findScroll);
activeSlide.addEventListener("touchmove", findScroll);
swiper.mousewheel.disable();
swiper.allowTouchMove = false;
}
};
Basically, by setting the scrollTop to 1px from either the top or bottom of the range, you prevent the mousewheel.enable() call from triggering immediately. In the original version, the slide would always start at the top of the scroll height when activated, while this version starts at the "top" (technically 1px down) if you're swiping down to it and the "bottom" if you're swiping up to it.
Modified from @LeTsoy's answer,
- Main change is to trigger this logic when the slide with the scrollbar is the first slide. Where the slideChangeTransitionEnd event will not trigger, especially when HashNavigation is enabled.
- disable mousewheel & allowTouchMove as the first thing, to allow inside scrolling to function properly
- Removing the scroll event listener to avoid event listener accumulation
- added {passive:true} to improve performance (optional)
const handleScrollInside = (swiper) => {
swiper.on("slideChangeTransitionEnd", () => {
swiper.mousewheel.disable();
swiper.allowTouchMove = false;
const activeSlide = document.querySelector('.swiper-slide-active');
const hasVerticalScrollbar = activeSlide.scrollHeight > activeSlide.clientHeight;
if (hasVerticalScrollbar) {
const scrollDifferenceTop = activeSlide.scrollHeight - activeSlide.swiperSlideSize;
if (activeSlide.scrollTop === 0) activeSlide.scrollTop += 1;
if (activeSlide.scrollTop === scrollDifferenceTop) activeSlide.scrollTop -= 2;
swiper.mousewheel.disable();
swiper.allowTouchMove = false;
const scrollHandler = () => {
if (activeSlide.scrollTop <= 0 || scrollDifferenceTop - activeSlide.scrollTop <= 1 ) {
swiper.mousewheel.enable();
swiper.allowTouchMove = true;
activeSlide.removeEventListener('scroll',scrollHandler);
}
};
activeSlide.addEventListener('scroll', scrollHandler, { passive: true });
} else {
swiper.mousewheel.enable();
swiper.allowTouchMove = true;
}
})
const activeSlide = document.querySelector('.swiper-slide-active');
const hasVerticalScrollbar = activeSlide.scrollHeight > activeSlide.clientHeight;
if (hasVerticalScrollbar) {
swiper.emit('slideChangeTransitionEnd');
}
}
if you have trouble with the inside scrolling, try bringing the active slide to the top for it to receive the scroll event. (i'm at swiper version: 11.x) it looks like all the slides in the swiper are stacked one above the other starting from 1th slide and the opacity is set to 0. only the active slide's opacity is set to 1. so the last (nth) slide will be at the top, preventing the active slide to receive the scrolling event.
.swiper-slide-active{
position: relative;
z-index: 10;
}
本文标签: javascriptNested scroll inside vertical swiper slide when mousewheel trueStack Overflow
版权声明:本文标题:javascript - Nested scroll inside vertical swiper slide when mousewheel: true - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744732979a2622154.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论