Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 20.05.2019, 22:56
Кандидат Javascript-наук
Отправить личное сообщение для drkrol Посмотреть профиль Найти все сообщения от drkrol
 
Регистрация: 09.10.2013
Сообщений: 114

Как устроена логика прокрутки колёсиком(скролла)?
Здравствуйте. Делаю свою кастомную прокрутку и столкнулся с такой проблемой: не могу понять, как работает скролл, а точнее "что происходит при кручении колёсика дважды и более".

Сейчас у меня есть такое:
https://jsfiddle.net/drkrol/mtvhrdgb/7/

Загвоздка вот в этом коде:
// условие при двойном кручении
    if (per1 == 1) {
     step = step + shag;
    } else {
     step = shag;
    }


Логика такая. Есть per1. При начале кручения он принимает значение 1, в конце анимации срабатывает код onComplete: function() {per1 = 0;}. При условии, когда per1 == 1 размер расстояния увеличивается на 1 шаг.

Проблема заключается в том, что если крутить колёсиком на 0.5 секунде (скорость анимации = 0.7), то через несколько таких прокруток вы улетите далеко вниз.

Если совсем убрать условие IF, то прокрутка будет рывками, а мне нужна плавность.

Вообще я стремлюсь вот к такому результату: http://maxlepinskih.com/prices , но я не понимаю, как это сделано у него. В его коде разобраться не смог.

И ещё нюанс один. Мне, чтобы оно плавно работало, нужно было убрать overflow у body, а в примере по ссылке у него плавно и сколлбар есть...

Скажите, как должно быть устроено условие IF, чтобы получилось, как у maxlepinskih?
Ответить с цитированием
  #2 (permalink)  
Старый 21.05.2019, 00:15
Аватар для рони
Профессор
Отправить личное сообщение для рони Посмотреть профиль Найти все сообщения от рони
 
Регистрация: 27.05.2010
Сообщений: 33,068

drkrol,
скролл по блочно
Ответить с цитированием
  #3 (permalink)  
Старый 21.05.2019, 03:17
Аватар для Malleys
Профессор
Отправить личное сообщение для Malleys Посмотреть профиль Найти все сообщения от Malleys
 
Регистрация: 20.12.2009
Сообщений: 1,714

рони, что это?
Сообщение от рони
    $(window).on("mousewheel DOMMouseScroll", function(c) {
        c.preventDefault();
К сожалению ваше решение блокирует прокрутку. Для прокрутки следует использовать событие scroll, которое происходит, когда содержимое элемента прокручивается, независимо от того, каким способом это происходит (вращение колёсика мыши, зажали среднюю кнопку мыши, перетаскиваем полосу прокрутки, жест прокрутка на сенсорном экране, клавиши Вверх/Вниз/Пробел/PgUp/PgDown/Home/End, прокрутка при помощи touchpad и ещё множество других способов). Вы же всё отменили, и ещё написали body { overflow: hidden; }. Это не прокрутка по блокам, а вращение колёсика мыши вызывает перемещение скрытой полосы прокрутки! Обратите внимание, что работает только при вращении колёсика мыши.

И ещё, чтобы избежать подёргивания и блокировки полосы прокрутки, не следует менять свойство scrollTop/ScrollLeft, прокрутка тогда очень не естественная получается. И странно получается, когда полоса прокрутки пытается вырваться из под курсора мыши. (поскольку происходит подмена scrollTop/ScrollLeft) Правильно делать так, чтобы при чтении scrollTop/ScrollLeft, менялся вид, а не сами эти свойства!

drkrol,
Сообщение от drkrol
Мне, чтобы оно плавно работало
Можно сделать так, чтобы сначала при прокрутке ничего не менялось, а затем при помощи своей формулы рассчитать положение содержимого
https://jsfiddle.net/u9xw6tjy/2/

Последний раз редактировалось Malleys, 21.05.2019 в 03:25.
Ответить с цитированием
  #4 (permalink)  
Старый 21.05.2019, 09:59
Аватар для рони
Профессор
Отправить личное сообщение для рони Посмотреть профиль Найти все сообщения от рони
 
Регистрация: 27.05.2010
Сообщений: 33,068

Malleys,
спасибо за науку.
Ответить с цитированием
  #5 (permalink)  
Старый 21.05.2019, 11:37
Аватар для рони
Профессор
Отправить личное сообщение для рони Посмотреть профиль Найти все сообщения от рони
 
Регистрация: 27.05.2010
Сообщений: 33,068

плавный scroll
Malleys,
насколько правильно, сделать например, так?


<!DOCTYPE html>
<html>
<head>
  <title>jQuery scroll example</title>

  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
  <style>
body {
  background: #20262E;
  font-family: Helvetica Neue;
}

.wrap-box {
  width: 290px;
  height: 290px;
  background: #777;
  margin: 10px;
}

body, html {
  height: 100%;
  margin: 0;
}

.wrap {
  overflow: auto;
  border: 1px solid red;
  box-sizing: border-box;
  height: 100%;
}

.wrap-content {
  overflow: hidden;
}

.boxes{
    transition: transform 1.8s cubic-bezier(0.47, 0, 0.745, 0.715);
}

  </style>
<script>
window.onload = function() {
  for (var i = 0; i < 100; i++) {
    $(".boxes").append('<div class="wrap-box">' + (1 + i) + "</div>");
  }
  var _top = 0;
  var top = 0;
  var scrollTop = 0;
  var timer;
  $(".wrap").scroll(function() {
    _top = this.scrollTop;
    scrollTop = _top - top;
    window.clearTimeout(timer);
    $(".boxes").css({"transform":"translateY(" + scrollTop + "px)", "transitionDuration":"0ms"});
    timer = window.setTimeout(function() {
      $(".boxes").css({"transform":"translateY(0px)", "transitionDuration":""});
      top = _top;
    }, 100);
  });
};
</script>

</head>
<body>
    <div class="wrap">
  <div class="wrap-content">
    <div class="boxes"></div>
  </div>
</div>
</body>
</html>
Ответить с цитированием
  #6 (permalink)  
Старый 23.05.2019, 02:05
Кандидат Javascript-наук
Отправить личное сообщение для drkrol Посмотреть профиль Найти все сообщения от drkrol
 
Регистрация: 09.10.2013
Сообщений: 114

Malleys,
Спасибо за код. Обычно меня пытаются научить уму разуму, тут же вы мне сразу готовое решение предоставили. Ещё раз спасибо, но я не совсем в нём разобрался. Скажите пожалуйста, как мне регулировать время анимации (скорость) и расстояние одного шага? И чёт и туда, и сюда...

Если у вас будет время и если вам не сложно, то не могли бы вы чуть подробнее разъяснить свой код? Я просто хотел вникнуть. Я понял, что всё держится на performance.now(), который возвращает время. Но как время помогает с плавностью я так и не понял...

upd. Проверил ваш вариант в Microsoft Edge. В начале анимации есть небольшое подёргивание, которое не наблюдается в Chrome и Opera

https://yadi.sk/i/YDtQ42kBiSpm4w - видео. Лучше скачать его (860 кб весит), иначе непонятно, где лаги

Последний раз редактировалось drkrol, 23.05.2019 в 03:53.
Ответить с цитированием
  #7 (permalink)  
Старый 23.05.2019, 17:13
Аватар для Malleys
Профессор
Отправить личное сообщение для Malleys Посмотреть профиль Найти все сообщения от Malleys
 
Регистрация: 20.12.2009
Сообщений: 1,714

Сообщение от drkrol
Скажите пожалуйста, как мне регулировать время анимации (скорость) и расстояние одного шага?
В этом примере вы можете только регулировать время анимации в той строчке, где написано...
var a = 1 - Math.exp(-0.005 * dt);
Число 0.005 поделите на нужное кол-во секунд, или умножьте на число, во сколько раз быстрее хотите!

Сообщение от drkrol
upd. Проверил ваш вариант в Microsoft Edge. В начале анимации есть небольшое подёргивание...
Да, я посмотрел ваше видео, формула, по которой вычисляется позиция, представлена гладкой кривой, поэтому при идеально правильно рендеринге не должно быть никакого подёргивания. Это ошибка/недочёт движка в Microsoft Edge. По крайней мере следующая версия Microsoft Edge будет основана на webkit и эта проблема, скорей всего, исчезнет! На самом деле, те, кто используют обычно этот браузер сейчас ничего особенного не заметят, поскольку и другие анимации слегка подёргиваются!

Сообщение от drkrol
Если у вас будет время и если вам не сложно, то не могли бы вы чуть подробнее разъяснить свой код? Я просто хотел вникнуть. Я понял, что всё держится на performance.now(), который возвращает время. Но как время помогает с плавностью я так и не понял...
На самом деле performance.now() нужен только для того, чтобы вычислять время между двумя отрисовками анимации. Время между двумя кадрами.

Все держится на формуле var a = 1 - Math.exp(-0.005 / T * dt);, которая и вычисляет, как быстро приближаться к конечной позиции прокрутки.

Сообщение от drkrol
Скажите пожалуйста, как мне регулировать время анимации (скорость) и расстояние одного шага?
Расстояние одного шага можно регулировать так: нужно уменьшить область прокрутки в A раз, тогда до конца области прокрутки мы будем добираться в A раз быстрее. Но само содержимое будет прокручиваться с такой же скоростью, как обычно. Поэтому мы его будем в A раз дальше продвигать вдоль оси прокрутки. Вот пример... вынес время анимации и во сколько раз ускорить обычную прокрутку в отдельные переменные в начале
<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width">
	<title>Document</title>
	<style>
		body {
			background: #20262E;
			font-family: "Helvetica Neue", system-ui;
		}

		.wrap-box {
			width: 290px;
			height: 290px;
			background: #777;
			margin: 10px;
		}

		body,
		html {
			height: 100%;
			margin: 0;
		}

		.wrap {
			overflow: auto;
			border: 1px solid red;
			box-sizing: border-box;
			height: 100%;
		}

		.wrap-content {
			overflow: hidden;
		}
	</style>
</head>

<body>
	<div class="wrap">
		<div class="wrap-content">
			<div class="boxes"></div>
		</div>
	</div>

	<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
	<script>
		$(function() {
			for (var i = 0; i < 100; i++) {
				$(".boxes").append(`<div class="wrap-box">${1 + i}</div>`);
			}

			var T = 0.75; // время до торможения (секунды)
			var A = 4; // во сколько раз уменьшить полосу прокрутки? Лучше всего [1;5]

			var _top = 0;
			var top = 0;
			var height;
			var originalHeight, wrapHeight;
			var scrollTop = 0;
			var lastTime = performance.now();
			var isScrolling = false;
			
			var wrap = $(".wrap");
			var wrapContent = $(".wrap-content");
			var boxes = $(".boxes");

			wrap.scroll(function() {
				_top = this.scrollTop;
				wrapHeight = wrap.height();
				if (!isScrolling) loop();
			});
			
			$(window).resize(function() {
				wrapHeight = wrap.height();
				originalHeight = wrapContent.css("height", "").height();
				height = Math.min(originalHeight - wrapHeight, (originalHeight - wrapHeight) / A) + wrapHeight;
				
				wrapContent.css("height", (height) + "px");
				wrap.scroll();
			});
			
			$(window).resize();

			function loop() {
				isScrolling = true;
				var dt = -lastTime + (lastTime = performance.now());

				var a = 1 - Math.exp(-0.005 / T * dt);
				var p = _top / (height - wrapHeight);

				top = (1 - a) * top + a * _top;

				scrollTop = _top - top;
				scrollTop = Math.abs(scrollTop) < 0.01 ? 0 : scrollTop;
				boxes.css("transform", "translateY(" + (scrollTop - p * (A - 1) / A * (originalHeight - wrapHeight)) + "px)");

				if (scrollTop !== 0)
					requestAnimationFrame(loop);
				else
					isScrolling = false;
			}
		});
	</script>
</body></html>


Сообщение от рони
насколько правильно, сделать например, так?
Всё ОК, коме того, что если крутануть колёсико мыши и не дождавшись конца анимации ещё раз крутануть, то это приведёт к сдвигу к начальной какой-то позиции, т. е. к тому моменту, с которого анимация начиналась, и уже с того места запустится новая анимация, вызванная кручением колёсика мыши. (ну или любым другим способом прокрутить, но не дождаться конца анимации)

Последний раз редактировалось Malleys, 23.05.2019 в 17:32.
Ответить с цитированием
  #8 (permalink)  
Старый 23.05.2019, 18:14
Аватар для рони
Профессор
Отправить личное сообщение для рони Посмотреть профиль Найти все сообщения от рони
 
Регистрация: 27.05.2010
Сообщений: 33,068

Сообщение от Malleys
ещё раз крутануть, то это приведёт к сдвигу
почему в вашем коде нет этого сдвига?
Ответить с цитированием
  #9 (permalink)  
Старый 23.05.2019, 19:04
Аватар для рони
Профессор
Отправить личное сообщение для рони Посмотреть профиль Найти все сообщения от рони
 
Регистрация: 27.05.2010
Сообщений: 33,068

Сообщение от Malleys
формула, по которой вычисляется позиция, представлена гладкой кривой,
не понимаю, как это работает, если
Сообщение от Malleys
var a = 1 - Math.exp(-0.005 / T * dt);
это формула, то зачем нужно Math.exp, почему например не a = 0.0023 * dt;
в целом плохо понимаю что происходит в фунции loop, кроме того что scrollTop стремится к нулю.

Последний раз редактировалось рони, 24.05.2019 в 06:51. Причина: dt
Ответить с цитированием
  #10 (permalink)  
Старый 24.05.2019, 00:28
Кандидат Javascript-наук
Отправить личное сообщение для drkrol Посмотреть профиль Найти все сообщения от drkrol
 
Регистрация: 09.10.2013
Сообщений: 114

чудеса какие-то... ничего не понял...

В firefox, кстати, шаг одного прокручивания в 2 раза меньше. Даже в 2.5...

Может если за место
"transform", "translateY"
сделать GSAP, то подёргиваний не будет?
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
как получить результат от ajax запроса (из отдельного файла) 1Feniks1 AJAX и COMET 0 12.03.2017 18:45
Открытие div блока при первом визите на сайт Nushaba Общие вопросы Javascript 28 20.12.2013 21:24
как накрыть дивом полосу прокрутки HelpeR Элементы интерфейса 9 17.01.2010 23:16
Логика скрипта как схема nicholas Общие вопросы Javascript 5 10.10.2009 22:19
Как правильно послать XML в POST запросе LowCoder AJAX и COMET 10 15.07.2009 23:20