Reibungsloser Bildlauf ohne Verwendung von jQuery

81

Ich codiere eine Seite, auf der ich nur rohen JavaScript-Code für die Benutzeroberfläche verwenden möchte, ohne dass Plugins oder Frameworks gestört werden.

Und jetzt habe ich Probleme damit, einen Weg zu finden, wie ich ohne jQuery reibungslos über die Seite scrollen kann.

Paolo Forgia
quelle
3
Genauso wie jQuery es tun würde. Teilen Sie die Animation in eine Reihe sehr kleiner Schritte auf. Verwenden Sie einen Intervall-Timer in einem kleinen Intervall, um jeden dieser Schritte nacheinander auszuführen, bis sie abgeschlossen sind.
Tvanfosson
Ich habe am Anfang an diese Lösung gedacht und der einzige Punkt, den ich verpasst habe, ist tatsächlich in Kamals Link unten, wie man die Y-Positionen von Objekten berechnet. Danke tvanfosson :)
Bitte werfen Sie einen Blick auf meine Antwort zum reibungslosen Scrollen mit einfachen js
surfmuggle
Dies ähnelt einer meiner Fragen. Schauen Sie sich stackoverflow.com/questions/40598955/… an .
Cannicide

Antworten:

27

Probieren Sie diese Demo mit reibungslosem Bildlauf oder einen Algorithmus wie den folgenden aus:

  1. Holen Sie sich den aktuellen Top-Standort mit self.pageYOffset
  2. Ermitteln Sie die Position des Elements, bis zu der Sie scrollen möchten: element.offsetTop
  3. Führen Sie eine for-Schleife aus, um dorthin zu gelangen, was sehr schnell sein wird, oder verwenden Sie einen Timer, um mit dieser Position sanft bis zu dieser Position zu scrollen window.scrollTo

Siehe auch die andere beliebte Antwort auf diese Frage.


Andrew Johnsons Originalcode:

function currentYPosition() {
    // Firefox, Chrome, Opera, Safari
    if (self.pageYOffset) return self.pageYOffset;
    // Internet Explorer 6 - standards mode
    if (document.documentElement && document.documentElement.scrollTop)
        return document.documentElement.scrollTop;
    // Internet Explorer 6, 7 and 8
    if (document.body.scrollTop) return document.body.scrollTop;
    return 0;
}


function elmYPosition(eID) {
    var elm = document.getElementById(eID);
    var y = elm.offsetTop;
    var node = elm;
    while (node.offsetParent && node.offsetParent != document.body) {
        node = node.offsetParent;
        y += node.offsetTop;
    } return y;
}


function smoothScroll(eID) {
    var startY = currentYPosition();
    var stopY = elmYPosition(eID);
    var distance = stopY > startY ? stopY - startY : startY - stopY;
    if (distance < 100) {
        scrollTo(0, stopY); return;
    }
    var speed = Math.round(distance / 100);
    if (speed >= 20) speed = 20;
    var step = Math.round(distance / 25);
    var leapY = stopY > startY ? startY + step : startY - step;
    var timer = 0;
    if (stopY > startY) {
        for ( var i=startY; i<stopY; i+=step ) {
            setTimeout("window.scrollTo(0, "+leapY+")", timer * speed);
            leapY += step; if (leapY > stopY) leapY = stopY; timer++;
        } return;
    }
    for ( var i=startY; i>stopY; i-=step ) {
        setTimeout("window.scrollTo(0, "+leapY+")", timer * speed);
        leapY -= step; if (leapY < stopY) leapY = stopY; timer++;
    }
}

Ähnliche Links:

Kamal
quelle
2
Gute Gliederung, aber bitte erstellen Sie keine Zeichenfolge, die für das Timeout ausgeführt werden soll. Erstellen Sie eine anonyme Funktion, die die eigentliche Funktion aufruft.
Tvanfosson
@nness es ist eine Schande. Ich wollte den Code unbedingt sehen.
dzny
37

Das reibungslose Scrollen des nativen Browsers in JavaScript sieht folgendermaßen aus:

// scroll to specific values,
// same as window.scroll() method.
// for scrolling a particular distance, use window.scrollBy().
window.scroll({
  top: 2500, 
  left: 0, 
  behavior: 'smooth' 
});

// scroll certain amounts from current position 
window.scrollBy({ 
  top: 100, // negative value acceptable
  left: 0, 
  behavior: 'smooth' 
});

// scroll to a certain element
document.querySelector('.hello').scrollIntoView({ 
  behavior: 'smooth' 
});
ZachB
quelle
können Sie auf Demo-Link setzen
DV Yogesh
2
@Vishu Rana, Verhaltensunterstützung geht nur an Firefox, wie auf MDN
Evandro Cavalcate Santos
2
@ EvandroCavalcateSantos: nicht mehr! ;-) developer.mozilla.org/en-US/docs/Web/API/Element/…
Pedro Ferreira
1
Dieser moderne Standard ist schön und lesbar, benötigt aber eine speedOption. Die aktuelle Bildlaufgeschwindigkeit ist zu langsam für den Fluss.
Nils Lindemann
31

Bearbeiten: Diese Antwort wurde 2013 geschrieben. Bitte überprüfen Sie den Kommentar von Cristian Traìna unten zu requestAnimationFrame

Ich habe es gemacht. Der folgende Code hängt von keinem Framework ab.

Einschränkung: Der aktive Anker ist nicht in die URL geschrieben.

Version des Codes: 1.0 | Github: https://github.com/Yappli/smooth-scroll

(function() // Code in a function to create an isolate scope
{
var speed = 500;
var moving_frequency = 15; // Affects performance !
var links = document.getElementsByTagName('a');
var href;
for(var i=0; i<links.length; i++)
{   
    href = (links[i].attributes.href === undefined) ? null : links[i].attributes.href.nodeValue.toString();
    if(href !== null && href.length > 1 && href.substr(0, 1) == '#')
    {
        links[i].onclick = function()
        {
            var element;
            var href = this.attributes.href.nodeValue.toString();
            if(element = document.getElementById(href.substr(1)))
            {
                var hop_count = speed/moving_frequency
                var getScrollTopDocumentAtBegin = getScrollTopDocument();
                var gap = (getScrollTopElement(element) - getScrollTopDocumentAtBegin) / hop_count;

                for(var i = 1; i <= hop_count; i++)
                {
                    (function()
                    {
                        var hop_top_position = gap*i;
                        setTimeout(function(){  window.scrollTo(0, hop_top_position + getScrollTopDocumentAtBegin); }, moving_frequency*i);
                    })();
                }
            }

            return false;
        };
    }
}

var getScrollTopElement =  function (e)
{
    var top = 0;

    while (e.offsetParent != undefined && e.offsetParent != null)
    {
        top += e.offsetTop + (e.clientTop != null ? e.clientTop : 0);
        e = e.offsetParent;
    }

    return top;
};

var getScrollTopDocument = function()
{
    return document.documentElement.scrollTop + document.body.scrollTop;
};
})();
Gabriel
quelle
2
Ich denke, Sie würden einfach die Anker-URL mit etwas wie var _updateURL = function ( anchor, url ) { if ( (url === true || url === 'true') && history.pushState ) { history.pushState( {pos:anchor.id}, '', anchor ); } };( über Chris Fernandis Smooth Scroll js ) in den Verlaufsstapel verschieben , um den Anker in die URL zu schreiben. Schön zu sehen, dass Leute dies tun, anstatt nur "JQuery zu verwenden"! Für die Aufzeichnung sind :targetSelektoren auch eine ordentliche Lösung.
Louis Maddox
1
Diese Antwort stammt aus dem Jahr 2013, als bereits geschrieben wurde, requestAnimationFrame, was die Leistung steigert. Ich weiß nicht, warum hier nicht verwendet wird.
Cristian Traìna
23

Algorithmus

Das Scrollen eines Elements erfordert das Ändern seines scrollTopWerts im Laufe der Zeit. Berechnen Sie für einen bestimmten Zeitpunkt einen neuen scrollTopWert. Um reibungslos zu animieren, interpolieren Sie mit einem Smooth-Step-Algorithmus .

Berechnen Sie scrollTopwie folgt:

var point = smooth_step(start_time, end_time, now);
var scrollTop = Math.round(start_top + (distance * point));

Wo:

  • start_time ist die Zeit, zu der die Animation gestartet wurde;
  • end_timeist, wenn die Animation endet (start_time + duration);
  • start_topist der scrollTopWert am Anfang; und
  • distanceist die Differenz zwischen dem gewünschten Endwert und dem Startwert (target - start_top).

Eine robuste Lösung sollte erkennen, wenn die Animation unterbrochen wird, und vieles mehr. Lesen Sie meinen Beitrag über Smooth Scrolling ohne jQuery für Details.

Demo

Siehe die JSFiddle .

Implementierung

Der Code:

/**
    Smoothly scroll element to the given target (element.scrollTop)
    for the given duration

    Returns a promise that's fulfilled when done, or rejected if
    interrupted
 */
var smooth_scroll_to = function(element, target, duration) {
    target = Math.round(target);
    duration = Math.round(duration);
    if (duration < 0) {
        return Promise.reject("bad duration");
    }
    if (duration === 0) {
        element.scrollTop = target;
        return Promise.resolve();
    }

    var start_time = Date.now();
    var end_time = start_time + duration;

    var start_top = element.scrollTop;
    var distance = target - start_top;

    // based on http://en.wikipedia.org/wiki/Smoothstep
    var smooth_step = function(start, end, point) {
        if(point <= start) { return 0; }
        if(point >= end) { return 1; }
        var x = (point - start) / (end - start); // interpolation
        return x*x*(3 - 2*x);
    }

    return new Promise(function(resolve, reject) {
        // This is to keep track of where the element's scrollTop is
        // supposed to be, based on what we're doing
        var previous_top = element.scrollTop;

        // This is like a think function from a game loop
        var scroll_frame = function() {
            if(element.scrollTop != previous_top) {
                reject("interrupted");
                return;
            }

            // set the scrollTop for this frame
            var now = Date.now();
            var point = smooth_step(start_time, end_time, now);
            var frameTop = Math.round(start_top + (distance * point));
            element.scrollTop = frameTop;

            // check if we're done!
            if(now >= end_time) {
                resolve();
                return;
            }

            // If we were supposed to scroll but didn't, then we
            // probably hit the limit, so consider it done; not
            // interrupted.
            if(element.scrollTop === previous_top
                && element.scrollTop !== frameTop) {
                resolve();
                return;
            }
            previous_top = element.scrollTop;

            // schedule next frame for execution
            setTimeout(scroll_frame, 0);
        }

        // boostrap the animation process
        setTimeout(scroll_frame, 0);
    });
}
hasen
quelle
Dies scheint eine wirklich gute Lösung zu sein. Aber wie können Sie es auf einem Fensterobjekt anstelle eines Elements implementieren, wie Sie es in Ihrer Geige haben?
Mudlabs
Egal, ich habe es herausgefunden. Musste verwenden pageYOffsetund scrollTo()weiter elementstatt scrollTop.
Mudlabs
6

Ich habe hier ein Beispiel ohne jQuery gemacht: http://codepen.io/sorinnn/pen/ovzdq

/**
    by Nemes Ioan Sorin - not an jQuery big fan 
    therefore this script is for those who love the old clean coding style  
    @id = the id of the element who need to bring  into view

    Note : this demo scrolls about 12.700 pixels from Link1 to Link3
*/
(function()
{
      window.setTimeout = window.setTimeout; //
})();

      var smoothScr = {
      iterr : 30, // set timeout miliseconds ..decreased with 1ms for each iteration
        tm : null, //timeout local variable
      stopShow: function()
      {
        clearTimeout(this.tm); // stopp the timeout
        this.iterr = 30; // reset milisec iterator to original value
      },
      getRealTop : function (el) // helper function instead of jQuery
      {
        var elm = el; 
        var realTop = 0;
        do
        {
          realTop += elm.offsetTop;
          elm = elm.offsetParent;
        }
        while(elm);
        return realTop;
      },
      getPageScroll : function()  // helper function instead of jQuery
      {
        var pgYoff = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;
        return pgYoff;
      },
      anim : function (id) // the main func
      {
        this.stopShow(); // for click on another button or link
        var eOff, pOff, tOff, scrVal, pos, dir, step;

        eOff = document.getElementById(id).offsetTop; // element offsetTop

        tOff =  this.getRealTop(document.getElementById(id).parentNode); // terminus point 

        pOff = this.getPageScroll(); // page offsetTop

        if (pOff === null || isNaN(pOff) || pOff === 'undefined') pOff = 0;

        scrVal = eOff - pOff; // actual scroll value;

        if (scrVal > tOff) 
        {
          pos = (eOff - tOff - pOff); 
          dir = 1;
        }
        if (scrVal < tOff)
        {
          pos = (pOff + tOff) - eOff;
          dir = -1; 
        }
        if(scrVal !== tOff) 
        {
          step = ~~((pos / 4) +1) * dir;

          if(this.iterr > 1) this.iterr -= 1; 
          else this.itter = 0; // decrease the timeout timer value but not below 0
          window.scrollBy(0, step);
          this.tm = window.setTimeout(function()
          {
             smoothScr.anim(id);  
          }, this.iterr); 
        }  
        if(scrVal === tOff) 
        { 
          this.stopShow(); // reset function values
          return;
        }
    }
 }
SorinN
quelle
1
Bitte posten Sie hier die entsprechenden Code-Schnipsel oder eine Beschreibung der Funktionsweise. Ihre Antwort sollte sinnvoll sein, ohne dass ich auf etwas klicken muss. Vielen Dank!
Edward
danke - aber nein - codeopen ist für das Teilen von Beispielen gemacht - hier ist nicht der richtige Ort, um den gesamten css / js / html-Code abzulegen - util stackoverflow fügt die erforderlichen Bits hinzu - die meisten Leute senden Sie an codepen / jsfiddle / oder anderswo.
SorinN
1
Hier ist ein Teil des Gesprächs darüber, warum wir danach fragen: meta.stackexchange.com/q/149890
Edward
2
Ich verstehe die Notwendigkeit vonwindow.setTimeout = window.setTimeout;
pmrotule
6

Moderne Browser unterstützen die CSS-Eigenschaft "Bildlaufverhalten: glatt". Wir brauchen dafür also überhaupt kein Javascript. Fügen Sie dies einfach dem Körperelement hinzu und verwenden Sie die üblichen Anker und Verbindungen. MDN-Dokumente mit Bildlaufverhalten

KostaShah
quelle
Sowohl Edge als auch Safari unterstützen diese Eigenschaft noch nicht. Ich hoffe sie werden es bald tun.
6754534367
Es funktioniert nicht gut .. sogar in Firefox. Es ist also keine Lösung.
huseyin39
5

Ich habe mich kürzlich vorgenommen, dieses Problem in einer Situation zu lösen, in der jQuery keine Option war. Deshalb protokolliere ich meine Lösung hier nur für die Nachwelt.

var scroll = (function() {

    var elementPosition = function(a) {
        return function() {
            return a.getBoundingClientRect().top;
        };
    };

    var scrolling = function( elementID ) {

        var el = document.getElementById( elementID ),
            elPos = elementPosition( el ),
            duration = 400,
            increment = Math.round( Math.abs( elPos() )/40 ),
            time = Math.round( duration/increment ),
            prev = 0,
            E;

        function scroller() {
            E = elPos();

            if (E === prev) {
                return;
            } else {
                prev = E;
            }

            increment = (E > -20 && E < 20) ? ((E > - 5 && E < 5) ? 1 : 5) : increment;

            if (E > 1 || E < -1) {

                if (E < 0) {
                    window.scrollBy( 0,-increment );
                } else {
                    window.scrollBy( 0,increment );
                }

                setTimeout(scroller, time);

            } else {

                el.scrollTo( 0,0 );

            }
        }

        scroller();
    };

    return {
        To: scrolling
    }

})();

/* usage */
scroll.To('elementID');

Die scroll()Funktion verwendet das Revealing Module Pattern , um die ID des Zielelements an seine scrolling()Funktion zu übergeben, über scroll.To('id')die die von der scroller()Funktion verwendeten Werte festgelegt werden .

Nervenzusammenbruch

In scrolling():

  • el : das Ziel-DOM-Objekt
  • elPos: gibt eine Funktion zurück, über elememtPosition()die bei jedem Aufruf die Position des Zielelements relativ zum oberen Rand der Seite angegeben wird.
  • duration : Übergangszeit in Millisekunden.
  • increment : unterteilt die Startposition des Zielelements in 40 Schritte.
  • time : Legt das Timing für jeden Schritt fest.
  • prev: die vorherige Position des Zielelements in scroller().
  • E: hält die Position des Zielelements in scroller().

Die eigentliche Arbeit erledigt die scroller()Funktion, die sich weiterhin (via setTimeout()) aufruft, bis sich das Zielelement oben auf der Seite befindet oder die Seite nicht mehr scrollen kann.

Bei jedem scroller()Aufruf wird die aktuelle Position des Zielelements (in Variable gehalten E) überprüft. Wenn dies > 1OR ist < -1und die Seite noch scrollbar ist, wird das Fenster um incrementPixel verschoben - nach oben oder unten, je nachdem, ob Ees sich um einen positiven oder einen negativen Wert handelt. Wenn Eweder > 1ODER < -1noch E=== ist , stoppt prevdie Funktion. Ich habe die DOMElement.scrollTo()Methode nach Abschluss hinzugefügt , um sicherzustellen, dass das Zielelement oben im Fenster angezeigt wird (nicht, dass Sie bemerken würden, dass es um einen Bruchteil eines Pixels entfernt ist!).

Die ifAnweisung in Zeile 2 von scroller()prüft, ob die Seite gescrollt wird (in Fällen, in denen sich das Ziel möglicherweise am unteren Rand der Seite befindet und die Seite nicht weiter scrollen kann), indem sie mit Eihrer vorherigen Position verglichen wird ( prev).

Die darunter liegende ternäre Bedingung verringert den incrementWert, wenn er Esich Null nähert. Dies verhindert, dass die Seite in eine Richtung überschießt und dann zurückspringt, um die andere zu überschießen, und dann zurückspringt, um die andere wieder zu überschießen, im Ping-Pong-Stil, bis ins Unendliche und darüber hinaus.

Wenn Ihre Seite mehr als ca. 4000 Pixel hoch ist, möchten Sie möglicherweise die Werte in der ersten Bedingung des ternären Ausdrucks (hier bei +/- 20) und / oder im Divisor, der den incrementWert festlegt (hier bei 40) , erhöhen .

Wenn Sie mit durationdem Divisor spielen, der festgelegt wird increment, und den Werten im ternären Zustand von scroller()sollten Sie die Funktion an Ihre Seite anpassen können .

  • JSFiddle

  • NBTest in aktuellen Versionen von Firefox und Chrome unter Lubuntu sowie Firefox, Chrome und IE unter Windows8.

Brian Peacock
quelle
3

Sie können auch die Scroll Behaviour- Eigenschaft verwenden. Fügen Sie beispielsweise die folgende Zeile zu Ihrem CSS hinzu

html{
scroll-behavior:smooth;
}

Dies führt zu einer nativen Funktion zum reibungslosen Scrollen. Lesen Sie mehr über das Bildlaufverhalten

Shakil Alam
quelle
2

Ich habe so etwas gemacht. Ich habe keine Ahnung, ob es in IE8 funktioniert. Getestet in IE9, Mozilla, Chrome, Edge.

function scroll(toElement, speed) {
  var windowObject = window;
  var windowPos = windowObject.pageYOffset;
  var pointer = toElement.getAttribute('href').slice(1);
  var elem = document.getElementById(pointer);
  var elemOffset = elem.offsetTop;

  var counter = setInterval(function() {
    windowPos;

    if (windowPos > elemOffset) { // from bottom to top
      windowObject.scrollTo(0, windowPos);
      windowPos -= speed;

      if (windowPos <= elemOffset) { // scrolling until elemOffset is higher than scrollbar position, cancel interval and set scrollbar to element position
        clearInterval(counter);
        windowObject.scrollTo(0, elemOffset);
      }
    } else { // from top to bottom
      windowObject.scrollTo(0, windowPos);
      windowPos += speed;

      if (windowPos >= elemOffset) { // scroll until scrollbar is lower than element, cancel interval and set scrollbar to element position
        clearInterval(counter);
        windowObject.scrollTo(0, elemOffset);
      }
    }

  }, 1);
}

//call example

var navPointer = document.getElementsByClassName('nav__anchor');

for (i = 0; i < navPointer.length; i++) {
  navPointer[i].addEventListener('click', function(e) {
    scroll(this, 18);
    e.preventDefault();
  });
}

Beschreibung

  • pointer- Element und chceck abrufen, wenn es das Attribut "href" hat. Wenn ja, "#" entfernen
  • elem- Zeigervariable ohne "#"
  • elemOffset- Versatz des Elements "Scrollen zu" oben auf der Seite
Skjal
quelle
2

Sie können verwenden

document.querySelector('your-element').scrollIntoView({behavior: 'smooth'});

Wenn Sie oben auf der Seite scrollen möchten, können Sie einfach ein leeres Element oben platzieren und sanft zu diesem Element scrollen.

Antonio Brandao
quelle
0

Sie können eine for-Schleife mit window.scrollTo und setTimeout verwenden, um mit einfachem Javascript reibungslos zu scrollen. So scrollen Sie mit meiner scrollToSmoothlyFunktion zu einem Element : scrollToSmoothly(elem.offsetTop)(vorausgesetzt, es elemhandelt sich um ein DOM-Element). Sie können dies verwenden, um reibungslos zu einer beliebigen y-Position im Dokument zu scrollen.

function scrollToSmoothly(pos, time){
/*Time is only applicable for scrolling upwards*/
/*Code written by hev1*/
/*pos is the y-position to scroll to (in pixels)*/
     if(isNaN(pos)){
      throw "Position must be a number";
     }
     if(pos<0){
     throw "Position can not be negative";
     }
    var currentPos = window.scrollY||window.screenTop;
    if(currentPos<pos){
    var t = 10;
       for(let i = currentPos; i <= pos; i+=10){
       t+=10;
        setTimeout(function(){
        window.scrollTo(0, i);
        }, t/2);
      }
    } else {
    time = time || 2;
       var i = currentPos;
       var x;
      x = setInterval(function(){
         window.scrollTo(0, i);
         i -= 10;
         if(i<=pos){
          clearInterval(x);
         }
     }, time);
      }
}

Demo:

Jota
quelle
0
<script>
var set = 0;

function animatescroll(x, y) {
    if (set == 0) {
        var val72 = 0;
        var val73 = 0;
        var setin = 0;
        set = 1;

        var interval = setInterval(function() {
            if (setin == 0) {
                val72++;
                val73 += x / 1000;
                if (val72 == 1000) {
                    val73 = 0;
                    interval = clearInterval(interval);
                }
                document.getElementById(y).scrollTop = val73;
            }
        }, 1);
    }
}
</script>

x = scrollTop
y = ID des Div, mit dem gescrollt wird

Hinweis: Geben Sie dem Body eine ID, damit der Körper gescrollt wird .

Jacob Mathen Alex
quelle
0

Hier ist meine Lösung. Funktioniert in den meisten Browsern

document.getElementById("scrollHere").scrollIntoView({behavior: "smooth"});

Docs

document.getElementById("end").scrollIntoView({behavior: "smooth"});
body {margin: 0px; display: block; height: 100%; background-image: linear-gradient(red, yellow);}
.start {display: block; margin: 100px 10px 1000px 0px;}
.end {display: block; margin: 0px 0px 100px 0px;}
<div class="start">Start</div>
<div class="end" id="end">End</div>

Edvard Åkerberg
quelle
0

Mit der folgenden Funktion funktioniert das reibungslose Scrollen einwandfrei:

html {
  scroll-behavior: smooth;
}

Meghnath Das
quelle
-1

Hier ist der Code, der für mich funktioniert hat.

`$('a[href*="#"]')

    .not('[href="#"]')
    .not('[href="#0"]')
    .click(function(event) {
     if (
       location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '')
       &&
       location.hostname == this.hostname
        ) {

  var target = $(this.hash);
  target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
  if (target.length) {

    event.preventDefault();
    $('html, body').animate({
      scrollTop: target.offset().top
    }, 1000, function() {

      var $target = $(target);
      $target.focus();
      if ($target.is(":focus")) { 
        return false;
      } else {
        $target.attr('tabindex','-1'); 
        $target.focus(); 
      };
    });
    }
   }
  });

`

Syed Anique
quelle
Können Sie etwas mehr darüber erklären, was Ihr Code tut?
Rhavelka
Willkommen bei stackoverflow. Vielen Dank für Ihre Antwort. Bei der Frage ging es jedoch nicht um die Verwendung von jquery.
Stephane L