简体   繁体   English

水平网站上的scrollTop或scrollLeft?

[英]scrollTop or scrollLeft on horizontal website?

I have a website I am working on (here is a basic example ), yesterday I got some help to implement active states on the radio-style button navigation, and I am now trying to link this up so that it also changes on page scroll/when in view as currently it's only onClick. 我有一个我正在研究的网站(这是一个基本的例子 ),昨天我得到了一些帮助,以实现无线电式按钮导航的活动状态,我现在正试图将其链接起来,以便它也在页面滚动上更改/当在视图中时,它目前只有onClick。

I roughly know how to achieve this as I've done something similar before, but then it occurred to me that because the page and scrollbar are rotated to accommodate the horizontal effect, I don't know if it would now be scrollTop or scrollLeft. 我大致知道如何实现这一点,因为我之前做过类似的事情,但后来我发现因为页面和滚动条被旋转以适应水平效果,我不知道它现在是否是scrollTop或scrollLeft。 I've never used scrollLeft before so I am unsure how to use it correctly. 我之前从未使用过scrollLeft,所以我不确定如何正确使用它。 I am wondering if anyone has implemented something similar before and what the correct way would be? 我想知道是否有人之前已经实现了类似的东西以及正确的方法是什么? I've tried both and nothing seems to be working. 我试过了两个,似乎没有任何工作。 This is what I'm roughly trying to achieve (but only one class active at a time). 就是我大致想要实现的目标(但一次只有一个类活跃)。

I thought maybe using Waypoints could be another option, but again it's hard to find anything online which explains how this works when a site is rotated multiple times. 我想也许使用Waypoints可能是另一种选择,但同样很难在网上找到任何解释当网站多次旋转时如何工作的东西。

My JS knowledge is shaky (still learning!), I'm only trying to implement what I think would work so this is probably not even correct, any help understanding what I'm doing wrong would be appreciated! 我的JS知识是不稳定的(仍在学习!),我只是想实现我认为可行的,所以这可能甚至不正确,任何帮助理解我做错了什么都将受到赞赏!

Heres the latest thing I've tried. 这是我尝试过的最新事情。

 // --- change span classes on click const setIconState = (icon, state) => icon.className = state ? icon.className.replace('button-off', 'button-on') : icon.className.replace('button-on', 'button-off') const toggleIcon = element => { const className = element.className; element.className = className.indexOf('button-on') > -1 ? setIconState(element, false) : setIconState(element, true); } const setIconActiveState = (icon, state) => icon.className = state ? icon.className = `${icon.className} active` : icon.className = icon.className.replace('active', '') document.querySelectorAll('.bottomnav span.icon') .forEach(icon => { icon.onclick = (e) => { const { target: clickedSpan } = e; const siblings = [...clickedSpan.parentElement.parentElement.querySelectorAll('span.icon')] .filter(sibling => sibling != clickedSpan); siblings.forEach(icon => { setIconState(icon, false); setIconActiveState(icon, false); }); setIconState(clickedSpan, true); setIconActiveState(clickedSpan, true); }; }); // --- change span classes on scroll test function onScroll(event){ var scrollPos = $(document).scrollTop(); $('.bottomnav a').each(function () { var currLink = $(this); var refElement = $(currLink.attr("href")); if (refElement.position().top <= scrollPos && refElement.position().top + refElement.height() > scrollPos) { $('.bottomnav a span').removeClass("active"); currLink.addClass("active"); } else{ currLink.removeClass("active"); } }); } 
 * { margin: 0; padding: 0; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } html, body { color: #000; font-family: 'IBM Plex Sans', sans-serif; font-weight: 100; font-size: 7px; text-rendering: optimizeLegibility; overflow-x: hidden; scroll-behavior: smooth; } .bottomnav { display: flex; justify-content: center; align-items: center; flex-wrap: wrap; overflow: hidden; position: fixed; bottom: 0px; width: 100%; z-index: 2; } .bottomnav span { float: left; display: block; color: #888; text-align: center; padding: 14px 16px; text-decoration: none; font-size: 26px; } .bottomnav span:hover { color: #fac123; } .bottomnav span.active { color: #fac123; } #container { overflow-y: scroll; overflow-x: hidden; transform: rotate(270deg) translateX(-100vh); transform-origin: top left; position: absolute; width: 100vh; height: 100vw; white-space: nowrap; scroll-snap-type: y mandatory; } #container .card { width: 100vw; height: 100vh; display: inline-flex; position: relative; scroll-snap-align: start; } #player { transform: rotate(90deg) translateY(-100vh); transform-origin: top left; font-size: 0; width: 100vh; height: 100vh; display: flex; /* position: absolute;*/ } #player section > object { width: 100vw; overflow-x: hidden; } section object > div { white-space: normal; } .container::-webkit-scrollbar { display: none; } section { padding: 5%; display: flex; justify-content: center; align-items: center; flex-wrap: wrap; position: relative; transition: .5s ease; } .cardwhite { color: white; background-color: black; } .cardblack { color: black; background-color: white; } h2 { font-size: 40px; font-weight: 700; font-family: 'IBM Plex Serif', sans-serif; } p { font-size: 10px; margin-bottom: 15px; font-weight: 100; font-family: 'IBM Plex Sans', sans-serif; } 
  <link href="https://unpkg.com/ionicons@4.5.5/dist/css/ionicons.min.css" rel="stylesheet"> <div class="bottomnav" id="bottomnav"> <a href="#1"><span class="icon ion-ios-radio-button-on active"></span></a> <a href="#2"><span class="icon ion-ios-radio-button-off"></span></a> <a href="#3"><span class="icon ion-ios-radio-button-off"></span></a> </div> <div class="container" id="container"> <div id="player"> <section class="card cardwhite" id="1"> <object> <h2>Section 1</h2> <p>Description</p> </object> </section> <section class="card cardblack" id="2"> <object> <h2>Section 2</h2> <p>Description</p> </object> </section> <section class="card cardwhite" id="3"> <object> <h2>Section 3</h2> <p>Description</p> </object> </section> </div> </div> 

For horizontal scrolling I would go the following route, simplifying your HTML and whaty you listen for. 对于水平滚动,我会采用以下路线,简化您的HTML并且您会倾听。 Since touch devices can easily just swipe to scroll, all you need to do is make it accessible for people with scroll wheels. 由于触摸设备可以轻松地滑动滚动,因此您需要做的就是让滚动轮的人可以使用它。 You could also add an animation, but it makes this snippet too long. 您还可以添加动画,但它会使此片段太长。

 const main = document.querySelector( 'main' ); const nav = document.querySelector( 'nav' ); let scrollend; function onwheel(){ /* When using the scrollwheel, translate Y direction scrolls to X direction. This way scrollwheel users get the benefit of scrolling down to go right, while touch and other users get default behaviour. */ event.preventDefault(); event.stopImmediatePropagation(); main.scrollLeft += event.wheelDeltaY; } function onscroll(){ /* When scrolling, find the nearest element to the center of the screen. Then find the link in the nav that links to it and activate it while deactivating all others. */ const current = Array.from( main.children ).find(child => { return child.offsetLeft >= main.scrollLeft - innerWidth / 2; }); const link = Array.from( nav.children ).reduce((find, child) => { child.classList.remove( 'selected' ); return find || (child.href.indexOf( current.id ) >= 0 ? child : find); }, false); if( link ) link.classList.add( 'selected' ); clearTimeout( scrollend ); scrollend = setTimeout( onscrollend, 100 ); } function onscrollend(){ /* After scrolling ends, snap the appropriate element. This could be done with an animation. */ clearTimeout( scrollend ); const current = Array.from( main.children ).find(child => { return child.offsetLeft >= main.scrollLeft - innerWidth / 2; }); main.scrollLeft = current.offsetLeft; } /* Bind and initial call */ main.addEventListener( 'wheel', onwheel ); main.addEventListener( 'scroll', onscroll ); onscroll(); 
 html, body, main { height: 100%; } body { padding: 0; margin: 0; } main { display: flex; overflow: auto; width: 100%; height: 100%; scroll-snap-type: x proximity; } main section { width: 100%; height: 100%; flex: 0 0 100%; } nav { position: fixed; bottom: 0; left: 0; width: 100%; display: flex; justify-content: center; align-items: center; } nav a { width: 1em; height: 1em; margin: 1em; display: block; overflow: hidden; color: transparent; border: 1px solid black; border-radius: 50%; } nav a.selected { background: black; } .bland { background: gray; } .dark { background: darkgray; color: white; } .bright { background: yellow; } 
 <nav> <a href="#section-1">Section 1</a> <a href="#section-2">Section 2</a> <a href="#section-3">Section 3</a> </nav> <main> <section class="bright" id="section-1"> <h2>Section 1</h2> </section> <section class="dark" id="section-2"> <h2>Section 2</h2> </section> <section class="bland" id="section-3"> <h2>Section 3</h2> </section> </main> 

As mentioned, I would also prefer a design that does not flip the X and Y axis. 如上所述,我也更喜欢不翻转X和Y轴的设计。 Doing so might bite us in the future, when we try to include non-trivial content on our pages. 当我们尝试在页面上包含非平凡的内容时,这样做可能会让我们在将来感到不满。 Also if we don't do that axis flip, we have no need at all to do positional calculations. 此外,如果我们不进行轴翻转,我们根本不需要进行位置计算。

So both the HTML structure and the CSS will be simpler. 因此HTML结构和CSS都会更简单。

AFAIK, it's not possible to do the scrolling purely in non-hacky CSS. AFAIK,不可能纯粹在非hacky CSS中滚动。

 /** * Change icon state on click. */ const icons = Array.from( document.querySelectorAll( '.icon' )); const toggleIcon = icon => { icon.classList.toggle( 'ion-ios-radio-button-on' ); icon.classList.toggle( 'ion-ios-radio-button-off' ); }; const clickIcon = event => { // toggle previous active state toggleIcon( document.querySelector( 'i.ion-ios-radio-button-on' )); // toggle own state toggleIcon( event.target ); }; icons.forEach( icon => icon.addEventListener( 'click', clickIcon )); /** * Scroll horizontally on scroll wheel. * The combination of "scroll-behavior: smooth;" and the "<a href=#>" anchor links, * can be reused to do and endless snapping cycle on wheel event. */ let scroll_state = 0; window.addEventListener( 'wheel', event => { window.requestAnimationFrame(() => { // cast to -1 or +1 const offset = event.deltaY / Math.abs( event.deltaY ); scroll_state += offset; // Arrays are zero-based. // So if the length matches our state, restart over from the first page. if ( scroll_state === icons.length ) scroll_state = 0; else if ( scroll_state < 0 ) scroll_state = icons.length - 1; // scrolll_state will now always contain the next icon to click. icons[ scroll_state ].click(); }); }); 
 * { box-sizing: border-box; margin: 0; padding: 0; } html { scroll-behavior: smooth; } body { overflow-x: scroll; width: 100%; } main { display: block; height: 90vh; width: 300vw; } nav { background-color: orange; display: block; height: 10vh; position: fixed; width: 100%; } a { text-decoration: none; } .page { display: inline-block; float: left; height: 100%; padding: 50px; width: 100vw; } 
 <link href="https://unpkg.com/ionicons@4.5.5/dist/css/ionicons.min.css" rel="stylesheet"> <body> <main> <section class="page" id="myapp_first"> <h1>First</h1> <p>Lorem Ipsum</p> </section> <section class="page" id="myapp_second"> <h1>Second</h1> <p>Lorem Ipsum</p> </section> <section class="page" id="myapp_third"> <h1>Third</h1> <p>Lorem Ipsum</p> </section> </main> <nav id="myapp_navigation"> <a href="#myapp_first"> <i class="icon ion-ios-radio-button-on active"></i> </a> <a href="#myapp_second"> <i class="icon ion-ios-radio-button-off"></i> </a> <a href="#myapp_third"> <i class="icon ion-ios-radio-button-off"></i> </a> </nav> </body> 

By leveraging the click event of the icons, we get the icons changing class and the transition for free. 通过利用图标的点击事件,我们可以免费获得改变课程和过渡的图标。 Adding more pages now just becomes adding the correct HTML and updating the width of the <main> element. 现在添加更多页面只是添加正确的HTML并更新<main>元素的宽度。

A last thing I would personally add, is a debounce function around the wheel event, so we don't try to scroll faster than we can render. 我个人补充的最后一件事是围绕滚轮事件的去抖功能,所以我们不会尝试滚动得比我们可以渲染的更快。

Without debouncing, we might want to merge the functions so we can include the class changing inside the animationFrame for hopefully less yanky visuals, but that would complicate the click events again, so i'd prefer debouncing the wheel handler. 在没有去抖动的情况下,我们可能想要合并这些函数,这样我们就可以在animationFrame中包含更改的类,希望不那么抽象的视觉效果,但这会使点击事件再次复杂化,所以我更喜欢去抖动轮处理程序。

 /** * Change icon state on click. */ const icons = Array.from( document.querySelectorAll( '.icon' )); const toggleIcon = icon => { icon.classList.toggle( 'ion-ios-radio-button-on' ); icon.classList.toggle( 'ion-ios-radio-button-off' ); icon.classList.toggle( 'active' ); }; const clickIcon = event => { // toggle previous active state toggleIcon( document.querySelector( '.ion-ios-radio-button-on' ));// toggle own state toggleIcon( event.target ); }; icons.forEach( icon => icon.addEventListener( 'click', clickIcon )); /** * Scroll horizontally on scroll wheel. * The combination of "scroll-behavior: smooth;" and the "<a href=#>" anchor links, * can be reused to do and endless snapping cycle on wheel event. */ let scroll_state = 0; window.addEventListener( 'wheel', event => { // ANimation frame to smooth out the transition. window.requestAnimationFrame(() => { // cast to -1 or +1 const offset = event.deltaY / Math.abs( event.deltaY ); scroll_state += offset; // Arrays are zero-based. // So if the length matches our state, restart over from the first page. if ( scroll_state === icons.length ) scroll_state = 0; else if ( scroll_state < 0 ) scroll_state = icons.length - 1; // scrolll_state will now always contain the next icon to click. icons[ scroll_state ].click(); }); }); 
 * { margin: 0; padding: 0; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } html, body { color: #000; font-family: 'IBM Plex Sans', sans-serif; font-weight: 100; /* EDIT: font-weight: 100 basically equals no font weight at all */ font-size: 7px; /* EDIT: Why so small ? */ text-rendering: optimizeLegibility; overflow-x: scroll; overflow-y: hidden; scroll-behavior: smooth; height: 100vh; /* EDIT: add height, so we can scale off this */ width: 100vw; /* EDIT: add width, so we can scale off this */ } .bottomnav { display: flex; justify-content: center; align-items: center; flex-wrap: wrap; /* overflow: hidden; */ position: fixed; /*bottom: 0px; EDIT: not needed after we place the nav at the bottom */ height: 15vh; /* EDIT: .bottomnav height + #container height = 100vh */ width: 100%; z-index: 2; background-color: black; } .bottomnav span { /*float: left; /* why float when flex lets us position exactly? */ display: block; color: #888; text-align: center; padding: 14px 16px; text-decoration: none; font-size: 26px; } .bottomnav span:hover { color: #fac123; } .bottomnav span.active { color: #fac123; } #container { /* overflow-y: scroll; overflow-x: hidden; transform: rotate(270deg) translateX(-100vh); transform-origin: top left; position: absolute; */ width: 300vw; /* EDIT: 300vw, 100 per page of 100vw */ height: 85vh; /* EDIT: .bottomnav height + #container height = 100vh */ /*scroll-snap-type: y mandatory; EDIT: only needed if we use snappoints */ } /* EDIT: .card and section are the same elements, merged rule "container" here */ .card { width: 100vw; /* EDIT: 100vw for each page of 100vw width */ height: 100%; /* EDIT: 100% so it scales with the container, not the screen */ display: inline-block; /* EDIT: block level, since we do not need to flex these */ float: left; /* EDIT: float left so our pages leave no space between them so 300vw = 100+100+100 . THis can be done with flexbox or grid as well, but is more complicated than needed */ /*position: relative; EDIT: not needed */ /* scroll-snap-align: start; EDIT: only needed if we use snappoints */ padding: 50px; /* EDIT: justify-content: center; align-items: center; flex-wrap: wrap; position: relative; */ /* transition: .5s ease; EDIT: I would think that "scroll-behavior: smooth;" already does this */ } /* EDIT: Since there's no use for the extra wrapper element, positioning it absolute + flex only harms us instead of helping #player { transform: rotate(90deg) translateY(-100vh); transform-origin: top left; font-size: 0; width: 100vh; height: 100vh; display: flex; position: absolute; } #player section > object { width: 100vw; overflow-x: hidden; } */ /* EDIT: I don't see any <div>s inside the objects section object > div { white-space: normal; } */ /* EDIT: ? Attempt to remove vertical scroll? Not needed .container::-webkit-scrollbar { display: none; } */ .cardwhite { color: white; background-color: black; } .cardblack { color: black; background-color: white; } h2 { font-size: 40px; font-weight: 700; font-family: 'IBM Plex Serif', sans-serif; } p { font-size: 10px; margin-bottom: 15px; font-weight: 100; font-family: 'IBM Plex Sans', sans-serif; } 
 <link href="https://unpkg.com/ionicons@4.5.5/dist/css/ionicons.min.css" rel="stylesheet"> <div id="container"> <!-- the extra player <div> is useless since the cards fully overlap it. so it can be removed --> <section class="card cardwhite" id="1"> <object> <h2>Section 1</h2> <p>Description</p> </object> </section> <section class="card cardblack" id="2"> <object> <h2>Section 2</h2> <p>Description</p> </object> </section> <section class="card cardwhite" id="3"> <object> <h2>Section 3</h2> <p>Description</p> </object> </section> </div> <!-- EDIT: Put the nav at the bottom so we do not have position issues --> <div class="bottomnav" id="bottomnav"> <a href="#1"> <span class="icon ion-ios-radio-button-on active"></span> </a> <a href="#2"> <span class="icon ion-ios-radio-button-off"></span> </a> <a href="#3"> <span class="icon ion-ios-radio-button-off"></span> </a> </div> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM