0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

スクロールを手動で作ってみる

Posted at

はじめに

 簡単な見出しスクロールを作ってみる。デフォルトではscroll-behaviorというのがあるが、それに頼らず作ってみる。イメージ的には指数関数的に減衰して見出しのところで止まるものを作る。

こっちで遊べるみたいです。

完成イメージ

 こんな風に、クリックすると同じ色の見出しが上に来るようにする。

wwrt3t3t3.png

なお文章の内容はロレムイプサムだと無味乾燥でつまんないので、以前書いた数学関連のPDFを雑にコピペしたものを使っている。色々面倒なので。

コードはこんな感じ

index.html
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8" />
		<script src="https://cdn.jsdelivr.net/npm/p5@1.11.9/lib/p5.js"></script>
		<script src="https://cdn.jsdelivr.net/npm/p5@1.11.9/lib/addons/p5.sound.min.js"></script>
		<script src="mySketch.js"></script>
		<link rel="stylesheet" type="text/css" href="style.css">
	</head>

	<body>   
		<main>
			<div id="main">
			  <!-- メインコンテンツ -->
			  <h2 class="index" id="index-top">おれは!げんき!!!</h2>

              <!-- なんか長い文章 -->

			  <h2 class="index" id="index0">おれは!げんき!!!</h2>

              <!-- なんか長い文章 -->

			  <h2 class="index" id="index1">おれは!げんき!!!</h2>
              
              <!-- なんか長い文章 -->

			  <h2 class="index" id="index2">おれは!げんき!!!</h2>
              
              <!-- なんか長い文章 -->

			  <h2 class="index" id="index3">おれは!げんき!!!</h2>
              
              <!-- なんか長い文章 -->
              
			</div>
			<div id="index">
				<a class="link" id="link-top" href="#index-top">みだし</a>
				<a class="link" id="link0" href="#index0">みだし</a>
				<a class="link" id="link1" href="#index1">みだし</a>
				<a class="link" id="link2" href="#index2">みだし</a>
				<a class="link" id="link3" href="#index3">みだし</a>
			</div>
		</main>
	</body>
</html>
style.css
html,
body {
	margin: 0;
	padding: 0;
}
main{
	display:flex;
	flex-direction:row;
}
#main{
	width:100%;
	max-width:900px;
	height:100dvh;
	overflow:scroll;
}
.link{
	display:block;
	margin:10px;
	color:black;
}
#index-top, #link-top{
	background-color:#ffcc44;
}
#index0, #link0{
	background-color:#ff44cc;
}
#index1, #link1{
	background-color:#44ff44;
}
#index2, #link2{
	background-color:#44ccff;
}
#index3, #link3{
	background-color:#cc44ff;
}
mySketch.js
// いろいろ

普通にリンクを張ってみる

 まず、スクロール位置だけ合わせるようにしてみる。次のようにする。

mySketch.js
function setup() {
	createCanvas(100,100);
	background(100);
}

function draw() {
	circle(mouseX, mouseY, 20);
}

function createJump(){

	// step1. classがlinkのすべての要素を取得
	const indices = document.querySelectorAll(".link");
	// step2. それぞれにaddEventListener. preventDefaultでデフォルトの挙動を殺す
	for(const index of indices){
		index.addEventListener("click", (e) => {
			e.preventDefault();
			// step3. メインタグを取得
			const mainTag = document.querySelector("#main");
			// step4. スクロールの限界を計算
			const scrollLimit = mainTag.scrollHeight - mainTag.offsetHeight;
			// step5. targetのDOMをhrefから計算する
			const targetDom = document.querySelector(index.getAttribute("href"));
			// step6. 目標値を計算
			const targetValue = Math.min(scrollLimit, targetDom.offsetTop);
			// step7. 設定する
			mainTag.scrollTop = targetValue;
		});
	}
}

document.addEventListener("DOMContentLoaded", createJump);

 まずquerySelectorAllでリンクをすべて取得します。それらにclickのイベントリスナーを付けるんですが、これらはaタグなのですでにデフォルトでhrefに「#」で指定したidのところにジャンプする機能が備わっています。これを「preventDefault」でつぶします。
 次に、今回のスクロールの対象はidがmainのタグのエリアなので(overflowをscrollにしてある)、それのタグを取得します。スクロールの限界は、これの実際の高さであるscrollHeightから表示上の高さであるoffsetHeightを引いた値となります。
 さらに、リンク先である p タグを取得します。これはリンクのDOMのhrefをquerySelectorに入れれば自動的に手に入るよう作られています。これとoffsetTopを比較して小さい方を取ります。
 そこで、それをscrollTopに代入すれば、ひとまず完成です。クリックするとその位置に飛びます。

滑らかに動くようにする

 今のままだと遷移がダイレクトすぎるので、滑らかに移動するようにします。

mySketch.js
function setup() {
	createCanvas(100,100);
	background(100);
}

function draw() {
	circle(mouseX, mouseY, 20);
}

function createJump(){
	const damper = {
		id:-1,
		factor:0.8,
		threshold:1,
		target:0,
		value:0
	}
	// step1. classがlinkのすべての要素を取得
	const indices = document.querySelectorAll(".link");
	// step2. それぞれにaddEventListener. preventDefaultでデフォルトの挙動を殺す
	for(const index of indices){
		index.addEventListener("click", (e) => {
			e.preventDefault();
			// step3. メインタグを取得
			const mainTag = document.querySelector("#main");
			// step4. スクロールの限界を計算
			const scrollLimit = mainTag.scrollHeight - mainTag.offsetHeight;
			// step5. targetのDOMをhrefから計算する
			const targetDom = document.querySelector(index.getAttribute("href"));
			// step6. 目標値を計算
			const targetValue = Math.min(scrollLimit, targetDom.offsetTop);

			if(damper.id >= 0) return;
			damper.value = mainTag.scrollTop - targetValue;
			damper.target = targetValue;
			damper.id = setInterval(() => {
				damper.value *= damper.factor;
				mainTag.scrollTop = damper.target + damper.value;
				if(Math.abs(damper.value) < damper.threshold){
					mainTag.scrollTop = damper.target;
					clearInterval(damper.id);
					damper.id = -1;
				}
			}, 16);
		});
	}
}

document.addEventListener("DOMContentLoaded", createJump);

 damper(減衰機)を使います。まず目標値との差分を設定し、一定の割合で掛け算を施して小さくしていきます。それを目標値に足してその都度scrollTopに設定します。これで滑らかに変化します。閾値より小さくなったら0にしてclearIntervalで終了します。

 以上です。

おわりに

 Firefoxとかだとあんま滑らかじゃないですね...スマホだときれいでした。横幅を変化させてもきちんとスクロールするのを確かめてみてください。多分もっときれいな方法があると思います。

 拝読感謝。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?