【问题标题】:Scrolling to an Anchor using Transition/CSS3使用 Transition/CSS3 滚动到锚点
【发布时间】:2014-09-21 03:16:16
【问题描述】:

我有一系列使用锚机制的链接:

<div class="header">
    <p class="menu"><a href="#S1">Section1</a></p>
    <p class="menu"><a href="#S2">Section2</a></p>
    ...
</div>
<div style="width: 100%;">
<a name="S1" class="test">&nbsp;</a>
<div class="curtain">
Lots of text
</div>

<a name="S2" class="test">&nbsp;</a>
<div class="curtain">
lots of text
</div>
...
</div>

我正在使用以下 CSS:

.test
{
    position:relative; 
    margin: 0; 
    padding: 0; 
    float: left;
    display: inline-block;
    margin-top: -100px; /* whatever offset this needs to be */
}

一切正常。但是当然,当我们点击链接时,它会从一个部分跳到下一个部分。所以我想要一个平滑的过渡,使用某种滚动到选定的开始 部分。

我想我在 Stackoverflow 上读到过,这对于 CSS3 来说(还)是不可能的,但我想确认一下,而且我想知道“可能”是什么解决方案。我很高兴使用 JS,但我不能使用 jQuery。我尝试在链接上使用点击功能,检索需要显示的 div 的“垂直位置”,但没有成功。我还在学习 JS,对它的了解还不够,无法提出自己的解决方案。

任何帮助/想法将不胜感激。

【问题讨论】:

  • 你是对的 - CSS3 无法平滑滚动,因为你无法使用 CSS 更改滚动位置,因此无法像其他属性一样转换它。您将需要使用 JS,但如果没有像 jQuery 这样的库,实现平滑滚动可能并非易事。

标签: html css


【解决方案1】:

您可以使用scroll-behavior CSS 属性 (在除 Internet Explorer 和 Safari 之外的所有浏览器中为 supported):

a {
  display: inline-block;
  padding: 5px 7%;
  text-decoration: none;
}

nav, section {
  display: block;
  margin: 0 auto;
  text-align: center;
}

nav {
  width: 350px;
  padding: 5px;
}

section {
  width: 350px;
  height: 130px;
  overflow-y: scroll;
  border: 1px solid black;
  font-size: 0; 
  scroll-behavior: smooth;    /* <----- THE SECRET ---- */
}

section div{
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  font-size: 8vw;
}
<nav>
  <a href="#page-1">1</a>
  <a href="#page-2">2</a>
  <a href="#page-3">3</a>
</nav>
<section>
  <div id="page-1">1</div>
  <div id="page-2">2</div>
  <div id="page-3">3</div>
</section>

【讨论】:

  • 您的回答很棒,希望将来能成为最简单的方法。但是,对于任何想要使用上述内容的人,请注意目前与任何版本的 IE 或 Safari 的兼容性为零。这种行为,至少在 Safari 上,是“跳跃”的旧行为。
  • 截至 3 月 18 日,Safari 不支持 caniuse.com/#search=scroll-behavior
  • @JerryGoyal - 从一开始,Safari 似乎就没有想到它,而且该链接没有提供有关 March 18 的信息跨度>
  • afaik safari 属于“现代和主流浏览器”,因此最好在答案中提及。谁知道他们是否有一天会将其添加到 safari 中。
  • 我是这样使用的 *{ scroll-behavior: smooth; }
【解决方案2】:

我尝试了 user18490 解决方案,但出现了一些问题,例如:

  • 多次点击时弹跳
  • 在目标元素下方没有足够空间时弹跳
  • 元素未定义
  • 父元素的问题

在我编辑和研究之后,我能够想出一个解决方案。希望它对每个人都有效

只需将脚本标签更改为:

var html = document.documentElement

var body = document.body

var documentHeight = Math.max(body.scrollHeight, body.offsetHeight, html.scrollHeight, html.clientHeight, html.offsetHeight)

var PageHeight = Math.max(html.clientHeight || 0, window.innerHeight || 0)

function scrollDownTo(to, duration) {
    if (document.body.scrollTop == to) return;
    if ((documentHeight-to) < PageHeight) {
        to = documentHeight - PageHeight;
    }
    var diff = to - window.pageYOffset;
    var scrollStep = Math.PI / (duration / 10);
    var count = 0, currPos; ajaxe = 1
    var start = window.pageYOffset;
    var scrollInterval = setInterval(function(){
        if (window.pageYOffset != to) {
            count = count + 1;
            if (ajaxe > count) {
                clearInterval(scrollInterval)
            }
            currPos = start +  diff * (0.5 - 0.5 * Math.cos(count * scrollStep));
            scroll( 0, currPos)
            ajaxe = count
        }
        else { clearInterval(scrollInterval);}
    },20);
    }

function test (elID) {
    var dest = document.getElementById(elID);
    scrollDownTo((dest.getBoundingClientRect().top + window.pageYOffset), 500);
}

HTML 还是一样的:

<div class="header">
    <p class="menu"><a href="#S1" onclick="test('S1'); return false;">S1</a></p>
    <p class="menu"><a href="#S2" onclick="test('S2'); return false;">S2</a></p>
    <p class="menu"><a href="#S3" onclick="test('S3'); return false;">S3</a></p>
    <p class="menu"><a href="#S4" onclick="test('S4'); return false;">S3</a></p>
</div>
<div style="width: 100%;">
    <div id="S1" class="curtain">
    blabla
    </div>
    <div id="S2" class="curtain">
    blabla
    </div>
    <div id="S3" class="curtain">
    blabla
    </div>
    <div id="S4" class="curtain">
    blabla
    </div>
 </div>

如果您仍然遇到任何问题,请发表评论

【讨论】:

    【解决方案3】:

    只需使用这一行代码将滚动行为应用于所有元素:

    *{
    scroll-behavior: smooth !important;
    }

    【讨论】:

    • 尽管我的 html 被部分拆分,但在部分上简单地执行 section {scroll-behavior: smooth;} 时,这对我有用。
    【解决方案4】:

    只有 mozilla 在 css 中实现了一个简单的属性: http://caniuse.com/#search=scroll-behavior

    你至少得使用 JS。

    我个人使用它是因为它易于使用(我使用 JQ,但我猜你可以调整它):

    /*Scroll transition to anchor*/
    $("a.toscroll").on('click',function(e) {
        var url = e.target.href;
        var hash = url.substring(url.indexOf("#")+1);
        $('html, body').animate({
            scrollTop: $('#'+hash).offset().top
        }, 500);
        return false;
    });
    

    只需添加类 toscroll 到您的 a 标签

    【讨论】:

      【解决方案5】:

      如果有人像我一样愿意使用 jQuery,但仍然发现自己正在寻找这个问题,那么这可能会对你们有所帮助:

      https://html-online.com/articles/animated-scroll-anchorid-function-jquery/

      $(document).ready(function () {
                  $("a.scrollLink").click(function (event) {
                      event.preventDefault();
                      $("html, body").animate({ scrollTop: $($(this).attr("href")).offset().top }, 500);
                  });
              });
      <a href="#anchor1" class="scrollLink">Scroll to anchor 1</a>
      <a href="#anchor2" class="scrollLink">Scroll to anchor 2</a>
      <p id="anchor1"><strong>Anchor 1</strong> - Lorem ipsum dolor sit amet, nonumes voluptatum mel ea.</p>
      <p id="anchor2"><strong>Anchor 2</strong> - Ex ignota epicurei quo, his ex doctus delenit fabellas.</p>

      【讨论】:

        【解决方案6】:

        您可以在以下页面找到问题的答案:

        https://stackoverflow.com/a/17633941/2359161

        这是给出的 JSFiddle:

        http://jsfiddle.net/YYPKM/3/

        注意 CSS 末尾的滚动部分,特别是:

        /*
         *Styling
         */
        
        html,body {
            width: 100%;
            height: 100%;
            position: relative; 
        }
        body {
        overflow: hidden;
        }
        
        header {
        background: #fff; 
        position: fixed; 
        left: 0; top: 0; 
        width:100%;
        height: 3.5rem;
        z-index: 10; 
        }
        
        nav {
        width: 100%;
        padding-top: 0.5rem;
        }
        
        nav ul {
        list-style: none;
        width: inherit; 
        margin: 0; 
        }
        
        
        ul li:nth-child( 3n + 1), #main .panel:nth-child( 3n + 1) {
        background: rgb( 0, 180, 255 );
        }
        
        ul li:nth-child( 3n + 2), #main .panel:nth-child( 3n + 2) {
        background: rgb( 255, 65, 180 );
        }
        
        ul li:nth-child( 3n + 3), #main .panel:nth-child( 3n + 3) {
        background: rgb( 0, 255, 180 );
        }
        
        ul li {
        display: inline-block; 
        margin: 0 8px;
        margin: 0 0.5rem;
        padding: 5px 8px;
        padding: 0.3rem 0.5rem;
        border-radius: 2px; 
        line-height: 1.5;
        }
        
        ul li a {
        color: #fff;
        text-decoration: none;
        }
        
        .panel {
        width: 100%;
        height: 500px;
        z-index:0; 
        -webkit-transform: translateZ( 0 );
        transform: translateZ( 0 );
        -webkit-transition: -webkit-transform 0.6s ease-in-out;
        transition: transform 0.6s ease-in-out;
        -webkit-backface-visibility: hidden;
        backface-visibility: hidden;
        
        }
        
        .panel h1 {
        font-family: sans-serif;
        font-size: 64px;
        font-size: 4rem;
        color: #fff;
        position:relative;
        line-height: 200px;
        top: 33%;
        text-align: center;
        margin: 0;
        }
        
        /*
         *Scrolling
         */
        
        a[ id= "servicios" ]:target ~ #main article.panel {
        -webkit-transform: translateY( 0px);
        transform: translateY( 0px );
        }
        
        a[ id= "galeria" ]:target ~ #main article.panel {
        -webkit-transform: translateY( -500px );
        transform: translateY( -500px );
        }
        a[ id= "contacto" ]:target ~ #main article.panel {
        -webkit-transform: translateY( -1000px );
        transform: translateY( -1000px );
        }
        <a id="servicios"></a>
        <a id="galeria"></a>
        <a id="contacto"></a>
        <header class="nav">
        <nav>
            <ul>
                <li><a href="#servicios"> Servicios </a> </li>
                <li><a href="#galeria"> Galeria </a> </li>
                <li><a href="#contacto">Contacta  nos </a> </li>
            </ul>
        </nav>
        </header>
        
        <section id="main">
        <article class="panel" id="servicios">
            <h1> Nuestros Servicios</h1>
        </article>
        
        <article class="panel" id="galeria">
            <h1> Mustra de nuestro trabajos</h1>
        </article>
        
        <article class="panel" id="contacto">
            <h1> Pongamonos en contacto</h1>
        </article>
        </section>

        【讨论】:

        • 谢谢。这意味着您知道 div 的高度,但我相信这是一个完全可以接受的解决方案,考虑到它简短、简单、不需要任何 JS ......所以它得到了我的投票。非常感谢 -
        • 你能在页面加载而不是ancor点击时触发这个吗?
        • @Sagivb.g,我只是通过设置一个链接到自身的锚点并在必要时让一个事件单击它(例如加载事件或其他任何东西)来实现这一效果。
        • 不知道链接目标的位置怎么办?
        【解决方案7】:

        我实现了@user18490 建议的the answer,但遇到了两个问题:


        我开发了以下类来解决上述问题,并且效果很好:

        export class SScroll{
            constructor(){
                this.delay=501      //ms
                this.duration=500   //ms
                this.lastClick=0
            }
        
            lastClick
            delay
            duration
        
            scrollTo=(destID)=>{
                /* To prevent "bounce" */
                /* https://stackoverflow.com/a/28610565/3405291 */
                if(this.lastClick>=(Date.now()-this.delay)){return}
                this.lastClick=Date.now()
        
                const dest=document.getElementById(destID)
                const to=dest.offsetTop
                if(document.body.scrollTop==to){return}
                const diff=to-document.body.scrollTop
                const scrollStep=Math.PI / (this.duration/10)
                let count=0
                let currPos
                const start=window.pageYOffset
                const scrollInterval=setInterval(()=>{
                    if(document.body.scrollTop!=to){
                        count++
                        currPos=start+diff*(.5-.5*Math.cos(count*scrollStep))
                        document.body.scrollTop=currPos
                    }else{clearInterval(scrollInterval)}
                },10)
            }
        }
        

        更新

        here 所述,Firefox 存在问题。因此,为了让它在 Firefox 上运行,我实现了以下代码。它在基于 Chromium 的浏览器和 Firefox 上运行良好。

        export class SScroll{
            constructor(){
                this.delay=501      //ms
                this.duration=500   //ms
                this.lastClick=0
            }
            lastClick
            delay
            duration
            scrollTo=(destID)=>{
                /* To prevent "bounce" */
                /* https://stackoverflow.com/a/28610565/3405291 */
                if(this.lastClick>=(Date.now()-this.delay)){return}
        
                this.lastClick=Date.now()
                const dest=document.getElementById(destID)
                const to=dest.offsetTop
                if((document.body.scrollTop || document.documentElement.scrollTop || 0)==to){return}
        
                const diff=to-(document.body.scrollTop || document.documentElement.scrollTop || 0)
                const scrollStep=Math.PI / (this.duration/10)
                let count=0
                let currPos
                const start=window.pageYOffset
                const scrollInterval=setInterval(()=>{
                    if((document.body.scrollTop || document.documentElement.scrollTop || 0)!=to){
                        count++
                        currPos=start+diff*(.5-.5*Math.cos(count*scrollStep))
                        /* https://stackoverflow.com/q/28633221/3405291 */
                        /* To support both Chromium-based and Firefox */
                        document.body.scrollTop=currPos
                        document.documentElement.scrollTop=currPos
                    }else{clearInterval(scrollInterval)}
                },10)
            }
        }
        

        【讨论】:

        • 嗨,你能详细说明我如何在 HTML 中使用它吗,如上所述 @user18490 表明 onclick 可以做到这一点,但是你已经写了一个完整的类,所以我怎么能用它来调用它HTML。谢谢
        • @MuhammadNoman 好吧,实际上我正在使用带有ReactJSMobX 的此类。但我觉得将类中的scrollTo 方法转换为单独的函数并像@user18490 调用的那样调用它可能非常简单
        • 无法使用你的类方法,转换成函数后又遇到弹跳错误。我所做的是删除delaylastClick 变量,将scrollInterval 设为全局变量,然后在调用setInterval 之前直接调用clearInterval(scrollInterval)。问题解决了。
        【解决方案8】:

        这是一个纯 css 解决方案,它使用视口单元和变量自动缩放到设备(并适用于窗口调整大小)。我在 Alex 的解决方案中添加了以下内容:

                html,body {
                    width: 100%;
                    height: 100%;
                    position: fixed;/* prevents scrolling */
                    --innerheight: 100vh;/* variable 100% of viewport height */
                }
        
                body {
                    overflow: hidden; /* prevents scrolling */
                }
        
                .panel {
                    width: 100%;
                    height: var(--innerheight); /* viewport height */
        
                a[ id= "galeria" ]:target ~ #main article.panel {
                    -webkit-transform: translateY( calc(-1*var(--innerheight)) );
                    transform: translateY( calc(-1*var(--innerheight)) );
                }
        
                a[ id= "contacto" ]:target ~ #main article.panel {
                    -webkit-transform: translateY( calc(-2*var(--innerheight)) );
                    transform: translateY( calc(-2*var(--innerheight)) );
        

        【讨论】:

        • 为什么需要一个变量? 100vh 不应该无处不在吗?
        【解决方案9】:

        虽然有些答案非常有用且内容丰富,但我想我会写下我想出的答案。 Alex 的回答非常好,但是在 div 的高度需要在 CSS 中硬编码的意义上是有限的。

        所以我想出的解决方案使用 JS(没有 jQuery),实际上是一个精简版(几乎是最低限度)的过度解决方案,用于解决我在 Statckoverflow 上发现的类似问题:

        HTML

        <div class="header">
            <p class="menu"><a href="#S1" onclick="test('S1'); return false;">S1</a></p>
            <p class="menu"><a href="#S2" onclick="test('S2'); return false;">S2</a></p>
            <p class="menu"><a href="#S3" onclick="test('S3'); return false;">S3</a></p>
            <p class="menu"><a href="#S4" onclick="test('S4'); return false;">S3</a></p>
        </div>
        <div style="width: 100%;">
            <div id="S1" class="curtain">
            blabla
            </div>
            <div id="S2" class="curtain">
            blabla
            </div>
            <div id="S3" class="curtain">
            blabla
            </div>
            <div id="S4" class="curtain">
            blabla
            </div>
         </div>
        

        注意“返回错误;”在点击调用中。如果您想避免浏览器跳转到链接本身(并让效果由您的 JS 管理),这一点很重要。

        JS代码:

        <script>
        function scrollTo(to, duration) {
            if (document.body.scrollTop == to) return;
            var diff = to - document.body.scrollTop;
            var scrollStep = Math.PI / (duration / 10);
            var count = 0, currPos;
            start = element.scrollTop;
            scrollInterval = setInterval(function(){
                if (document.body.scrollTop != to) {
                    count = count + 1;
                    currPos = start + diff * (0.5 - 0.5 * Math.cos(count * scrollStep));
                    document.body.scrollTop = currPos;
                }
                else { clearInterval(scrollInterval); }
            },10);
        }
        
        function test(elID)
        {
            var dest = document.getElementById(elID);
            scrollTo(dest.offsetTop, 500);
        }
        </script>
        

        这非常简单。它使用其唯一 ID(在函数测试中)查找 div 在文档中的垂直位置。然后它调用 scrollTo 函数,传递起始位置 (document.body.scrollTop) 和目标位置 (dest.offsetTop)。它使用某种缓入曲线来执行过渡。

        感谢大家的帮助。

        了解一点编码可以帮助您避免(有时是繁重的)库,并为您(程序员)提供更多控制权。

        【讨论】:

        • 我必须通知你们,Alex 的解决方案似乎是最佳的......我将值更改并固定为百分比 (%) 并获得了理想的效果。小提琴:jsfiddle.net/YYPKM/1676 当“面板”类具有 100% 的高度和绝对位置时,它也可以正常工作(这样可以节省我们的时间.. 这是该小提琴的另一个版本:jsfiddle.net/YYPKM/1677
        • testscrollTo 可以合并为一个函数
        • @ymz - 我知道我参加聚会有点晚了,但我只是想插话。亚历克斯的解决方案低于这个的原因是亚历克斯的解决方案不是动态的. Alex 的解决方案要求您对 CSS 中的位置进行硬编码(无论您是否使用 %,您仍然是硬编码)。然而,这个答案创建了一个解决方案,当您向页面添加元素时,不需要您添加新的 CSS 规则。它只是简单的工作。
        • 这会产生错误element is undefined。所以请改用start = window.pageYOffset;
        • 此方法可能会产生“反弹”问题,但可以像this一样解决
        【解决方案10】:

        我想可以将某种硬核转换设置为#containerdivtop 样式,以便在单击锚点时将整个页面移动到所需的方向。比如添加一个有top:-2000px的类。

        我确实使用了 JQuery,因为我也懒得使用原生 JS,但我所做的没有必要。

        这可能不是最好的解决方案,因为顶部内容只是向顶部移动并且您无法轻松将其返回,如果您确实需要滚动动画,则绝对应该使用 JQuery。

        DEMO

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-12-08
          • 1970-01-01
          • 2017-11-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-08-06
          相关资源
          最近更新 更多