Tôi đang viết mã một trang mà tôi chỉ muốn sử dụng mã JavaScript thô cho giao diện người dùng mà không có bất kỳ sự can thiệp nào của các plugin hoặc khuôn khổ.
Và bây giờ tôi đang vật lộn với việc tìm cách cuộn trang mượt mà mà không cần jQuery.
Tôi đang viết mã một trang mà tôi chỉ muốn sử dụng mã JavaScript thô cho giao diện người dùng mà không có bất kỳ sự can thiệp nào của các plugin hoặc khuôn khổ.
Và bây giờ tôi đang vật lộn với việc tìm cách cuộn trang mượt mà mà không cần jQuery.
Câu trả lời:
Hãy thử bản trình diễn cuộn mượt mà này hoặc một thuật toán như:
self.pageYOffsetelement.offsetTopwindow.scrollToXem thêm câu trả lời phổ biến khác cho câu hỏi này.
Mã ban đầu của Andrew Johnson:
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++;
}
}
Liên kết liên quan:
Thao tác cuộn mượt mà của trình duyệt gốc trong JavaScript giống như sau:
// 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'
});
speedtùy chọn. Tốc độ cuộn hiện tại quá chậm so với luồng.
chỉnh sửa: câu trả lời này đã được viết vào năm 2013. Vui lòng kiểm tra bình luận của Cristian Traìna bên dưới về requestAnimationFrame
Tôi đã làm việc đó. Đoạn mã dưới đây không phụ thuộc vào bất kỳ khuôn khổ nào.
Giới hạn: Mỏ neo hoạt động không được viết trong url.
Phiên bản của mã: 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 ); } };( thông qua Smooth Scroll js của Chris Fernandi ) để ghi liên kết vào URL. Rất vui khi thấy mọi người làm điều này thay vì chỉ “sử dụng JQuery”! Đối với bản ghi, :targetbộ chọn cũng là một giải pháp gọn gàng.
Việc cuộn một phần tử yêu cầu thay đổi scrollTopgiá trị của nó theo thời gian. Đối với một thời điểm nhất định, hãy tính một scrollTopgiá trị mới . Để tạo hoạt ảnh mượt mà, hãy nội suy bằng thuật toán từng bước mượt mà .
Tính toán scrollTopnhư sau:
var point = smooth_step(start_time, end_time, now);
var scrollTop = Math.round(start_top + (distance * point));
Ở đâu:
start_time là thời gian hoạt ảnh bắt đầu;end_timelà khi hoạt ảnh sẽ kết thúc (start_time + duration);start_toplà scrollTopgiá trị ở đầu; vàdistancelà sự khác biệt giữa giá trị cuối mong muốn và giá trị bắt đầu (target - start_top).Một giải pháp mạnh mẽ sẽ phát hiện khi hoạt ảnh bị gián đoạn và hơn thế nữa. Đọc bài đăng của tôi về Smooth Scrolling mà không cần jQuery để biết chi tiết.
Xem JSFiddle .
Mật mã:
/**
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);
});
}
pageYOffsetvà scrollTo()trên elementthay vì scrollTop.
Tôi đã tạo một ví dụ mà không có jQuery tại đây: 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;
Các trình duyệt hiện đại có hỗ trợ thuộc tính CSS "scroll-behavior: Smooth". Vì vậy, chúng tôi thậm chí không cần bất kỳ Javascript nào cho việc này. Chỉ cần thêm cái này vào phần tử body và sử dụng các liên kết và neo thông thường. tài liệu MDN hành vi cuộn
Gần đây tôi đã đặt ra để giải quyết vấn đề này trong một tình huống mà jQuery không phải là một lựa chọn, vì vậy tôi ghi lại giải pháp của mình ở đây chỉ để lưu lại hậu thế.
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');
Các scroll()chức năng sử dụng các mẫu mô-đun Tiết lộ để vượt qua id yếu tố mục tiêu để nó scrolling()chức năng, thông qua scroll.To('id'), thiết lập giá trị sử dụng bởi các scroller()chức năng.
Phá vỡ
Trong scrolling():
el : đối tượng DOM đíchelPos : trả về một hàm qua elememtPosition() đó cung cấp vị trí của phần tử mục tiêu so với đầu trang mỗi khi nó được gọi.duration : thời gian chuyển đổi tính bằng mili giây.increment : chia vị trí bắt đầu của phần tử đích thành 40 bước.time : đặt thời gian của từng bước.prev: vị trí trước đó của phần tử mục tiêu trong scroller().E: giữ vị trí của phần tử mục tiêu trong scroller().Công việc thực tế được thực hiện bởi scroller()hàm này tiếp tục gọi chính nó (qua setTimeout()) cho đến khi phần tử đích nằm ở đầu trang hoặc trang không thể cuộn nữa.
Mỗi lần scroller()được gọi, nó sẽ kiểm tra vị trí hiện tại của phần tử đích (được giữ trong biến E) và nếu đó là > 1HOẶC < -1và nếu trang vẫn có thể cuộn thì sẽ dịch chuyển cửa sổ theo incrementpixel - lên hoặc xuống tùy thuộc vào Egiá trị dương hay âm. Khi nào Ekhông > 1HOẶC < -1hoặc E=== prevthì hàm dừng. Tôi đã thêm DOMElement.scrollTo()phương thức khi hoàn thành chỉ để đảm bảo rằng phần tử mục tiêu nằm trên đầu cửa sổ (không phải là bạn sẽ nhận thấy nó bị lệch ra ngoài một phần nhỏ của pixel!).
Câu iflệnh ở dòng 2 scroller()kiểm tra xem trang có đang cuộn hay không (trong trường hợp mục tiêu có thể ở cuối trang và trang không thể cuộn nữa) bằng cách kiểm tra Evị trí trước đó của nó ( prev).
Điều kiện bậc ba bên dưới nó giảm incrementgiá trị khi Etiến gần đến 0. Thao tác này sẽ dừng trang chụp quá mức theo một chiều và sau đó bật trở lại để chụp quá mức theo chiều kia, rồi bật trở lại để chụp quá mức một lần nữa, kiểu bóng bàn, đến vô cực và xa hơn nữa.
Nếu trang của bạn cao hơn c.4000px, bạn có thể muốn tăng giá trị trong điều kiện đầu tiên của biểu thức bậc ba (ở đây là +/- 20) và / hoặc số chia sẽ đặt incrementgiá trị (ở đây là 40).
Chơi với duration, số chia sẽ đặt incrementvà các giá trị trong điều kiện bậc ba của scroller()sẽ cho phép bạn điều chỉnh hàm cho phù hợp với trang của mình.
Đã được thử nghiệm trong các phiên bản cập nhật của Firefox và Chrome trên Lubuntu và Firefox, Chrome và IE trên Windows8.
Bạn cũng có thể sử dụng Thuộc tính Hành vi Cuộn . ví dụ thêm dòng dưới đây vào css của bạn
html{
scroll-behavior:smooth;
}
và điều này sẽ dẫn đến tính năng cuộn mượt mà. Đọc thêm về hành vi cuộn
Tôi đã làm một cái gì đó như thế này. Tôi không biết liệu nó có hoạt động trong IE8 hay không. Đã thử nghiệm trên 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();
});
}
Sự miêu tả
pointer—Get phần tử và chceck nếu nó có thuộc tính "href" nếu có, hãy loại bỏ "#"elem—Biến điểm con trỏ không có "#"elemOffset— Tập hợp phần tử "cuộn tới" từ đầu trangBạn có thể dùng
document.querySelector('your-element').scrollIntoView({behavior: 'smooth'});
Nếu bạn muốn cuộn lên đầu trang, bạn có thể chỉ cần đặt một phần tử trống ở trên cùng và cuộn mượt mà đến phần đó.
Bạn có thể sử dụng vòng lặp for với window.scrollTo và setTimeout để cuộn mượt mà với Javascript thuần. Để cuộn đến phần tử có scrollToSmoothlyhàm của tôi : scrollToSmoothly(elem.offsetTop)(giả sử elemlà phần tử DOM). Bạn có thể sử dụng điều này để cuộn mượt mà đến bất kỳ vị trí y nào trong tài liệu.
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);
}
}
Bản giới thiệu:
<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 của div được sử dụng để cuộn
Lưu ý:
Để làm cho phần thân cuộn, hãy cung cấp cho phần thân một ID.
Đây là giải pháp của tôi. Hoạt động trên hầu hết các trình duyệt
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>
Với việc sử dụng thao tác cuộn trơn tru sau đây hoạt động tốt:
html {
scroll-behavior: smooth;
}
Đây là mã phù hợp với tôi.
`$('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();
};
});
}
}
});
`