Desplazamiento a un ancla usando Transition / CSS3


Tengo una serie de enlaces que están usando un mecanismo de anclaje:

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

Estoy usando el siguiente CSS:

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

Está funcionando bien. Pero, por supuesto, está saltando de una sección a la siguiente cuando hacemos clic en el enlace. Así que me gustaría tener una transición suave, utilizando un desplazamiento de algún tipo al comienzo de seleccionado apartado.

Creo que leí en Stackoverflow que esto no es posible (todavía) con CSS3, pero me gustaría una confirmación y también me gustaría saber qué 'podría' ser solución. Estoy feliz de usar JS pero no puedo usar jQuery. Traté de usar una función on click en el enlace, recuperar la "posición vertical" del div que debe mostrarse, pero no tuve éxito. Todavía estoy aprendiendo JS y no lo conozco lo suficientemente bien como para llegar a una solución propia.

Cualquier ayuda/idea sería muy apreciada.

 32
Author: user18490, 2014-07-29

8 answers

Puede encontrar la respuesta a su pregunta en la siguiente página:

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

Aquí está el JSFiddle que fue dado:

Http://jsfiddle.net/YYPKM/3 /

Tenga en cuenta la sección de desplazamiento al final del CSS, específicamente:

/*
 *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>
 26
Author: Alex Podworny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-10 18:38:57

Si bien algunas de las respuestas fueron muy útiles e informativas, pensé en escribir la respuesta que se me ocurrió. La respuesta de Alex fue muy buena, sin embargo, es limitada en el sentido de que la altura del div necesita ser codificada en el CSS.

Así que la solución que se me ocurrió usa JS (no jQuery) y en realidad es una versión reducida (casi al mínimo) de soluciones excesivas para resolver problemas similares que encontré en 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>

NOTA EL " RETURN FALSE;" en la llamada on click. Esto es importante si desea evitar que su navegador salte al enlace en sí (y deje que el efecto sea administrado por su JS).

Código 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>

Es increíblemente simple. Encuentra la posición vertical del div en el documento usando su ID único (en la prueba de función). Luego llama a la función scrollTo pasando la posición inicial (documento.cuerpo.scrollTop) y la posición de destino (dest.offsetTop). Realiza la transición usando algún tipo de curva de ease-inout.

Gracias a todos por su ayuda.

Saber un poco de codificación puede ayudarte a evitar bibliotecas (a veces pesadas) y darte (el programador) más control.

 17
Author: user18490,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-22 03:57:39

Usando el scroll-behavior Propiedad CSS:

(que es compatible en los navegadores modernos, pero no 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>
 11
Author: vsync,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-15 08:52:21

Solo mozzila implementa una propiedad simple en css : http://caniuse.com/#search=scroll-behavior

Tendrás que usar al menos JS.

Personalmente uso esto porque es fácil de usar (use JQ pero supongo que puede adaptarlo):

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

Simplemente agregue la clase toscroll a su etiqueta a

 4
Author: Reign.85,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-27 09:08:25

Si alguien es como yo dispuesto a usar jQuery, pero todavía se encontró mirando a esta pregunta, entonces esto puede ayudarlos:

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>
 2
Author: Kalin Krastev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-30 09:40:23

Supongo que podría ser posible establecer algún tipo de transición hardcore al estilo top de a #container div para mover toda la página en la dirección deseada al hacer clic en el ancla. Algo así como agregar una clase que tiene top:-2000px.

Usé jQuery porque soy demasiado perezoso para usar JS nativos, pero no es necesario para lo que hice.

Esta no es probablemente la mejor solución posible porque el contenido superior solo se mueve hacia la parte superior y no se puede volver fácilmente, usted definitivamente debería usar jQuery si realmente necesita esa animación de desplazamiento.

DEMO

 0
Author: Sir Celsius,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-29 17:00:20

Aquí hay una solución css pura que utiliza unidades y variables viewport que se escala automáticamente al dispositivo (y funciona en el cambio de tamaño de la ventana). He añadido lo siguiente a la solución de 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
Author: user7915231,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-25 15:59:32

Implementé la respuesta sugerida por @user18490 pero me encontré con dos problemas:

  • Primero rebotando cuando el usuario hace clic en varias pestañas/enlaces varias veces en una breve sucesión
  • En segundo lugar, el undefined error mencionado por @krivar

Desarrollé la siguiente clase para sortear los problemas mencionados, y funciona 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)
    }
}

ACTUALIZACIÓN

Hay un problema con Firefox como se mencionó aquí. Por lo tanto, para que funcione en Firefox, implementé el siguiente código. Funciona bien en navegadores basados en Chromium y también 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)
    }
}
 0
Author: user3405291,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-28 06:07:19