Koduję stronę, na której chcę używać tylko surowego kodu JavaScript dla interfejsu użytkownika bez żadnych ingerencji wtyczek lub frameworków.
A teraz walczę ze znalezieniem sposobu na płynne przewijanie strony bez jQuery.
Koduję stronę, na której chcę używać tylko surowego kodu JavaScript dla interfejsu użytkownika bez żadnych ingerencji wtyczek lub frameworków.
A teraz walczę ze znalezieniem sposobu na płynne przewijanie strony bez jQuery.
Odpowiedzi:
Wypróbuj to demo z płynnym przewijaniem lub algorytm, taki jak:
self.pageYOffset
element.offsetTop
window.scrollTo
Zobacz także inną popularną odpowiedź na to pytanie.
Oryginalny kod Andrew Johnsona:
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++;
}
}
Powiązane linki:
Natywne płynne przewijanie w przeglądarce w JavaScript wygląda następująco:
// 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'
});
speed
opcji. Bieżąca prędkość przewijania jest zbyt niska dla przepływu.
edycja: ta odpowiedź została napisana w 2013 roku. Sprawdź poniższy komentarz Cristiana Traìny na temat requestAnimationFrame
Ja to zrobiłem. Poniższy kod nie zależy od żadnej platformy.
Ograniczenie: aktywna kotwica nie jest zapisana w adresie URL.
Wersja kodu: 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;
};
})();
var _updateURL = function ( anchor, url ) { if ( (url === true || url === 'true') && history.pushState ) { history.pushState( {pos:anchor.id}, '', anchor ); } };
( przez Smooth Scroll js Chrisa Fernandi ), aby zapisać kotwicę do adresu URL. Miło widzieć, że ludzie robią to, a nie tylko „używają JQuery”! Dla przypomnienia, :target
selektory są również zgrabnym rozwiązaniem.
Przewijanie elementu wymaga zmiany jego scrollTop
wartości w czasie. Oblicz nową scrollTop
wartość dla danego punktu w czasie . Aby animować płynnie, interpoluj za pomocą algorytmu płynnego kroku .
Oblicz scrollTop
w następujący sposób:
var point = smooth_step(start_time, end_time, now);
var scrollTop = Math.round(start_top + (distance * point));
Gdzie:
start_time
to czas rozpoczęcia animacji;end_time
kiedy animacja się zakończy (start_time + duration)
;start_top
jest scrollTop
wartością na początku; idistance
jest różnicą między żądaną wartością końcową a wartością początkową (target - start_top)
.Solidne rozwiązanie powinno wykrywać przerwanie animacji i nie tylko. Przeczytaj mój post o płynnym przewijaniu bez jQuery, aby uzyskać szczegółowe informacje.
Zobacz JSFiddle .
Kod:
/**
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);
});
}
pageYOffset
i scrollTo()
na element
zamiast scrollTop
.
Zrobiłem przykład bez jQuery tutaj: 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;
}
}
}
window.setTimeout = window.setTimeout;
Nowoczesne przeglądarki obsługują właściwość CSS „scroll-zachowanie: płynne”. Więc nawet nie potrzebujemy do tego żadnego Javascript. Po prostu dodaj to do elementu body i użyj zwykłych kotwic i linków. Dokumentacja MDN z zachowaniem przewijania
Niedawno postanowiłem rozwiązać ten problem w sytuacji, gdy jQuery nie wchodziło w grę, więc loguję tutaj moje rozwiązanie tylko dla potomności.
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');
scroll()
Funkcja używa ujawniających modułu Wzorzec przekazać identyfikator docelowy element do jej scrolling()
funkcji, za pośrednictwem scroll.To('id')
, którego ustawia wartości wykorzystywane przez scroller()
funkcję.
Awaria
W scrolling()
:
el
: docelowy obiekt DOMelPos
: zwraca funkcję, poprzez elememtPosition()
którą podaje pozycję elementu docelowego względem początku strony za każdym razem, gdy jest on wywoływany.duration
: czas przejścia w milisekundach.increment
: dzieli pozycję początkową elementu docelowego na 40 kroków.time
: ustawia czas każdego kroku.prev
: poprzednia pozycja elementu docelowego w scroller()
.E
: utrzymuje pozycję elementu docelowego w scroller()
.Właściwa praca jest wykonywana przez scroller()
funkcję, która kontynuuje wywoływanie siebie (przez setTimeout()
), dopóki element docelowy nie znajdzie się na górze strony lub strona nie może się już przewijać.
Za każdym razem, gdy scroller()
jest wywoływana, sprawdza aktualną pozycję elementu docelowego (trzymanego w zmiennej E
) i jeśli jest to > 1
OR < -1
i jeśli strona jest nadal przewijalna, przesuwa okno o increment
piksele - w górę lub w dół w zależności od tego, czy E
jest to wartość dodatnia czy ujemna. Jeśli E
nie ma > 1
OR < -1
lub E
===, prev
funkcja zatrzymuje się. Dodałem tę DOMElement.scrollTo()
metodę po zakończeniu, aby upewnić się, że element docelowy uderzył w górę okna (nie żebyś zauważył, że jest wychylony o ułamek piksela!).
if
Oświadczenie na linii 2 scroller()
sprawdza, czy strona jest przewijanie (w przypadkach, w których cel może być w kierunku dolnej części strony, a strona nie może przejść dalej) za sprawdzenie E
przeciwko swojej poprzedniej pozycji ( prev
).
Warunek trójskładnikowy poniżej tego zmniejsza increment
wartość E
zbliżając się do zera. Dzięki temu strona nie przeskakuje w jedną stronę, a następnie odbija się z powrotem i przeskakuje w drugą stronę, a następnie odbija się, by ponownie prześcignąć drugą stronę, w stylu ping-ponga, do nieskończoności i dalej.
Jeśli Twoja strona ma wysokość ponad 4000 pikseli, możesz chcieć zwiększyć wartości w pierwszym warunku wyrażenia trójskładnikowego (tutaj +/- 20) i / lub dzielnik, który ustawia increment
wartość (tutaj 40).
Zabawa z duration
dzielnikiem, który ustawia increment
i wartościami w warunku trójskładnikowym, scroller()
powinno pozwolić na dostosowanie funkcji do strony.
NB Przetestowano w aktualnych wersjach przeglądarek Firefox i Chrome na Lubuntu oraz Firefox, Chrome i IE na Windows8.
Możesz także użyć właściwości zachowania przewijania . na przykład dodaj poniższą linię do swojego css
html{
scroll-behavior:smooth;
}
a to spowoduje natywną funkcję płynnego przewijania. Przeczytaj więcej o zachowaniu przewijania
Zrobiłem coś takiego. Nie mam pojęcia, czy działa w IE8. Przetestowano w 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();
});
}
Opis
pointer
- pobierz element i sprawdź, czy ma atrybut „href”, jeśli tak, usuń „#”elem
- zmienna wskaźnika bez znaku „#”elemOffset
- odsunięcie elementu „przewiń do” od góry stronyMożesz użyć
document.querySelector('your-element').scrollIntoView({behavior: 'smooth'});
Jeśli chcesz przewijać u góry strony, możesz po prostu umieścić pusty element na górze i płynnie przewinąć do tego.
Możesz użyć pętli for z window.scrollTo i setTimeout, aby płynnie przewijać za pomocą zwykłego JavaScript. Aby przewinąć do elementu z moją scrollToSmoothly
funkcją: scrollToSmoothly(elem.offsetTop)
(zakładając, że elem
jest to element DOM). Możesz użyć tego do płynnego przewijania do dowolnej pozycji Y w dokumencie.
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);
}
}
Próbny:
<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 elementu div używanego do przewijania
Uwaga:
Aby przewinąć treść, nadaj jej identyfikator.
Oto moje rozwiązanie. Działa w większości przeglądarek
document.getElementById("scrollHere").scrollIntoView({behavior: "smooth"});
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>
Przy użyciu następującego płynnego przewijania działa dobrze:
html {
scroll-behavior: smooth;
}
Oto kod, który zadziałał dla mnie.
`$('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();
};
});
}
}
});
`