web-dev-qa-db-fra.com

Faire défiler jusqu'à une ancre en utilisant Transition/CSS3

J'ai une série de liens qui utilisent un mécanisme d'ancrage:

<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>

J'utilise les CSS suivants:

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

Ça fonctionne bien. Mais bien sûr, on saute d’une section à l’autre quand on clique sur le lien. Je voudrais donc avoir une transition en douceur, en utilisant un rouleau de quelque sorte au début de la section sélectionnée

Je pense avoir lu sur Stackoverflow que cela n’était pas (encore) possible avec CSS3 mais je voudrais une confirmation et j'aimerais aussi savoir quelle solution pourrait être. Je suis heureux d'utiliser JS mais je ne peux pas utiliser jQuery. J'ai essayé d'utiliser une fonction sur le lien sur le lien, récupérer la "position verticale" de la div qui doit être affichée, mais j'ai échoué. J'apprends encore JS et je ne le connais pas assez bien pour proposer une solution personnelle.

Toute aide/idées serait grandement appréciée.

34
user18490

Bien que certaines des réponses aient été très utiles et instructives, j'ai pensé écrire la réponse que j'ai trouvée. La réponse d’Alex a été très bonne. Elle est toutefois limitée au sens où la hauteur de la div doit être codée en dur dans le CSS.

Donc, la solution que j'ai proposée utilise JS (no jQuery) et est en fait une version simplifiée (presque au minimum) de solutions permettant de résoudre des problèmes similaires que j'ai trouvés sur 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>

NOTE LE "RETOUR FAUX;" dans l'appel sur clic. Ceci est important si vous voulez éviter que votre navigateur ne saute sur le lien lui-même (et que l'effet soit géré par votre JS).

Code 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>

C'est incroyablement simple. Il trouve la position verticale de la div dans le document en utilisant son identifiant unique (dans la fonction test). Il appelle ensuite la fonction scrollTo en passant la position de départ (document.body.scrollTop) et la position de destination (dest.offsetTop). Il effectue la transition en utilisant une sorte de courbe d’ajustement.

Merci à tous pour votre aide.

Connaître un peu de code peut vous aider à éviter les bibliothèques (parfois lourdes) et à vous donner (au programmeur) plus de contrôle.

18
user18490

Vous pouvez trouver la réponse à votre question à la page suivante:

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

Voici le JSFiddle qui a été donné:

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

Notez la section de défilement à la fin du CSS, plus précisément:

/*
 *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>

29
Alex Podworny

Utilisation de la propriété scroll-behavior CSS:

(qui est supporté dans les navigateurs modernes mais pas Edge):

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>

14
vsync

Seul mozzila implémente une propriété simple en css: http://caniuse.com/#search=scroll-behavior

vous devrez au moins utiliser JS.

Personnellement, je l’utilise parce que c’est facile à utiliser (utilisez JQ mais je pense que vous pouvez l’adapter):

/*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;
});

il suffit d'ajouter la classe toscroll à votre balise

5
Reign.85

Si quelqu'un comme moi est prêt à utiliser jQuery, mais qu'il se trouve toujours à la recherche de cette question, cela peut vous aider:

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>

3
Kalin Krastev

J'ai implémenté la réponse suggéré par @ user18490 mais j'ai rencontré deux problèmes: 

  • Premier rebondissant lorsque l’utilisateur clique plusieurs fois sur plusieurs onglets/liens
  • Deuxièmement, l'erreur undefined mentionnée par @krivar

J'ai développé le cours suivant pour contourner les problèmes mentionnés et cela fonctionne bien:

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)
    }
}

METTRE À JOUR

Il y a un problème avec Firefox comme mentionné ici . Par conséquent, pour que cela fonctionne avec Firefox, j'ai implémenté le code suivant. Cela fonctionne très bien sur les navigateurs à base de chrome et aussi 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)
    }
}
1
user3405291

Voici une solution purement css utilisant des unités de visualisation et des variables qui s'adapte automatiquement au périphérique (et fonctionne avec le redimensionnement de fenêtre). J'ai ajouté ce qui suit à la solution d'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)) );
0
user7915231

J'imagine qu'il serait peut-être possible de définir une sorte de transition difficile vers le style top d'un #containerdiv pour déplacer toute la page dans la direction souhaitée lorsque vous cliquez sur votre ancre. Quelque chose comme ajouter une classe qui a top:-2000px.

JQuery, j’ai utilisé parce que je suis trop fainéant, j’utilise aussi JS en natif, mais ce n’est pas nécessaire pour ce que j’ai fait.

Ce n'est probablement pas la meilleure solution possible car le contenu du haut se déplace simplement vers le haut et vous ne pouvez pas le récupérer facilement. Vous devez absolument utiliser JQuery si vous avez vraiment besoin de cette animation par défilement.

DEMO

0
Sir Celsius