縦線が走るローディングアニメーション

1px の縦線5本が、ばらばらの速さで左から右へ走り抜けるローディングアニメーション。本数・色・濃さ・速さは、数字の変更だけで調整できる。このサイトでも現在設定中。(変更するかもしれないけど)

線の動き

状態線の動き
読み込み中右へ抜けた線が、また左から走る。速さ・濃さ・加速のしかたは毎回変わる
読み込み完了新しい線は走らない。走っている線が右へ抜けたら終わり
5秒たっても読み込み中読み込み完了と同じく、走っている線が右へ抜けたら終わり

※ どの線も最低1回は走る。読み込みが一瞬で終わるページでも、アニメーションが見えるようにするため。
※ 線の上からでもリンクを押せる。

jQuery・WordPress は不要。ページの HTML は変更しない(線は JavaScript が作る)。

CSS と JavaScript

<head> 内に配置。CSS と JavaScript をまとめて1つにしてある。

<!-- 縦線が走るローディングアニメーション:<head> 内に配置 -->
<style>
/* ===== ここから CSS ===== */
/* 線の入れ物:画面全体に重ねる(クリックは下のページに通す) */
.line-sweep {
  --ls-color: #2b2d39;   /* ★ 線の色 */
  --ls-width: 1px;       /* ★ 線の太さ */
  position: fixed;
  inset: 0;
  z-index: 9999;         /* 重なり順(既存のローディング画面より前に表示する場合は大きくする) */
  overflow: hidden;
  pointer-events: none;  /* 線の上からでもリンクを押せるように */
}
/* 線1本 */
.line-sweep__line {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: var(--ls-width);
  background: var(--ls-color);
  will-change: transform;
}
/* ===== ここまで CSS ===== */
</style>
<script>
/* ===== JavaScript =====
   - 縦線を COUNT 本、画面の左端の外から右端の外まで走らせる
   - 線ごとに「速さ・出だしの遅れ・加速のしかた・濃さ」を毎回ランダムに決める
   - 読み込み中      :右へ抜けた線を、また左から走らせる(速さなどは毎回決め直す)
   - 読み込み完了(load) :新しく走らせない。走っている線が右へ抜けたら終わり
   - MAX_WAIT を過ぎても読み込み中:読み込み完了と同じく、走っている線が右へ抜けたら終わり
   - どの線も最低1回は走る(読み込みが一瞬で終わるページでも、アニメーションが見えるように)
   - 全部の線が抜けたら、線の入れ物ごと削除する
   - 線の入れ物は body の外(<html> の直下)に作る
     body を隠して表示する設定(body { display: none; } からのフェードインなど)があっても、線が一緒に隠れないため
   - 動かすのは transform だけ(ブラウザが画面を描き直さずに動かせるので軽い)
   - 動きを減らす設定(prefers-reduced-motion)のブラウザ、Web Animations API が使えないブラウザでは何もしない
*/
( function () {
	'use strict';

	var MODE        = 'loop'; /* ★ 'loop'=読み込みが終わるまで繰り返す/'once'=開いたときに1回だけ走らせる */
	var MAX_WAIT    = 5000;   /* ★ 読み込みが終わらなくても、繰り返しをやめるまでの時間(ミリ秒) */
	var COUNT       = 5;      /* ★ 線の本数 */
	var DUR_MIN     = 300;    /* ★ 一番速い線が抜けるまでの時間(ミリ秒) */
	var DUR_MAX     = 1600;   /* ★ 一番遅い線が抜けるまでの時間(ミリ秒) */
	var DELAY_MAX   = 450;    /* ★ 線ごとの出だしの遅れの最大(ミリ秒) */
	var OPACITY_MIN = 0.15;   /* ★ 一番薄い線の濃さ(OPACITY_MAX と同じにすると全部同じ濃さ) */
	var OPACITY_MAX = 0.3;    /* ★ 一番濃い線の濃さ(1 で --ls-color の色そのまま) */

	/* 加速のしかた(等速・だんだん速く・だんだん遅く・急に動く など)から毎回1つ選ぶ */
	var EASES = [
		'linear',
		'ease-in',
		'ease-out',
		'cubic-bezier(.7,0,.3,1)',
		'cubic-bezier(.2,.9,.3,1)',
		'cubic-bezier(.9,.1,1,.4)'
	];

	if ( window.matchMedia && window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches ) {
		return;
	}
	if ( ! Element.prototype.animate ) {
		return;
	}

	var loaded = false; /* ページの読み込みが終わったか */
	var startedAt;      /* 走り始めた時刻 */

	function rnd( min, max ) {
		return min + Math.random() * ( max - min );
	}

	/* 線をもう一度走らせるか:'loop' で、読み込み中かつ MAX_WAIT 以内のときだけ */
	function shouldRepeat() {
		return 'loop' === MODE && ! loaded && ( Date.now() - startedAt ) < MAX_WAIT;
	}

	/* 線を1本、左端の外から右端の外まで走らせる。抜けたら done() を呼ぶ */
	function runLine( line, done ) {
		var endX = window.innerWidth + 4; /* 走らせるたびに測る(画面の幅が変わっても右端まで抜けるように) */
		line.style.opacity = rnd( OPACITY_MIN, OPACITY_MAX ).toFixed( 2 );

		line.animate(
			[
				{ transform: 'translateX(-4px)' },
				{ transform: 'translateX(' + endX + 'px)' }
			],
			{
				duration: rnd( DUR_MIN, DUR_MAX ),
				delay: rnd( 0, DELAY_MAX ),
				easing: EASES[ Math.floor( Math.random() * EASES.length ) ],
				fill: 'both'
			}
		).finished.then( function () {
			if ( shouldRepeat() ) {
				runLine( line, done ); /* 読み込み中なら、また左から */
			} else {
				done();
			}
		} );
	}

	function run() {
		var wrap = document.createElement( 'div' );
		var rest = COUNT; /* まだ走っている線の数 */

		wrap.className = 'line-sweep';
		wrap.setAttribute( 'aria-hidden', 'true' );
		document.documentElement.appendChild( wrap ); /* body の外に作る(理由は冒頭) */
		startedAt = Date.now();

		for ( var i = 0; i < COUNT; i++ ) {
			var line = document.createElement( 'span' );
			line.className = 'line-sweep__line';
			wrap.appendChild( line );

			runLine( line, function () {
				rest--;
				if ( 0 === rest ) {
					wrap.remove(); /* 全部抜けたら入れ物ごと削除 */
				}
			} );
		}
	}

	/* 読み込み完了の印(すでに終わっていればすぐ) */
	if ( 'complete' === document.readyState ) {
		loaded = true;
	} else {
		window.addEventListener( 'load', function () {
			loaded = true;
		} );
	}

	if ( 'loop' === MODE ) {
		run(); /* 読み込みが始まった直後から走らせる */
	} else if ( 'loading' === document.readyState ) {
		document.addEventListener( 'DOMContentLoaded', run );
	} else {
		run();
	}
} )();
</script>

※ <head> 内に配置するのは、ページの読み込みが始まった直後から線を走らせるため。</body> の直前に配置すると、ページの HTML を最後まで読み込むまで線が走らない。
※ 読み込みが終わらなくても、5秒で繰り返しをやめる。画像の多いページで、線がいつまでも走り続けないようにするため。

変更する場合

変えたいもの変更箇所
線の本数JavaScript の COUNT
線の色CSS の --ls-color
線の太さCSS の --ls-width
線の濃さJavaScript の OPACITY_MIN(一番薄い線)・OPACITY_MAX(一番濃い線)
線の速さJavaScript の DUR_MIN(一番速い線)・DUR_MAX(一番遅い線)
線ごとの出だしの遅れJavaScript の DELAY_MAX
繰り返しをやめるまでの時間JavaScript の MAX_WAIT

※ 背景が暗いサイトでは、--ls-color を明るい色に変更すること。

WordPress で設定する場合

CSS と JavaScript を1つずつファイルにしてテーマに格納し、functions.php で <head> に読み込む。

格納するファイル

  • css/line-sweep.css:コードの <style> と </style> の間(CSS)
  • js/line-sweep.js:コードの <script> と </script> の間(JavaScript)

functions.php に追記。

/**
 * 縦線が走るローディングアニメーション
 * ・css/line-sweep.css と js/line-sweep.js を <head> で読み込む
 * ・JavaScript を <head> で読み込むのは、ページの読み込みが始まった直後から線を走らせるため
 */
function line_sweep_enqueue() {
	// CSS(線の色・太さ・重なり順)
	wp_enqueue_style(
		'line-sweep',
		get_theme_file_uri( 'css/line-sweep.css' ),
		array(),
		filemtime( get_theme_file_path( 'css/line-sweep.css' ) ) // 更新したら自動でキャッシュ更新
	);

	// JavaScript(jQuery 不要なので依存なし)
	wp_enqueue_script(
		'line-sweep',
		get_theme_file_uri( 'js/line-sweep.js' ),
		array(),
		filemtime( get_theme_file_path( 'js/line-sweep.js' ) ), // 更新したら自動でキャッシュ更新
		false // false=<head> で読み込む(true だと </body> の直前)
	);
}
add_action( 'wp_enqueue_scripts', 'line_sweep_enqueue' );

※ バージョンにファイルの更新日時を指定している。ファイルを更新したときに、ブラウザが古いファイルを使い続けないようにするため。
※ get_theme_file_uri() は、子テーマ・親テーマのどちらに格納しても見つける。

既存の設定がある場合

ページ全体を隠して表示する設定(body { display: none; } からのフェードインなど)がある場合も、削除しなくていい。線は body の外に作るため、一緒に隠れない。

たとえばフェードインでページを表示している場合は、線が走る下からページがじわっと表示される。

別のローディング画面がすでにある場合は、重なり順に注意。線をローディング画面より前に表示するには、CSS の .line-sweep の z-index を、既存のローディング画面より大きくする。

表示環境によって動きの見え方が異なる場合があるため、本番に反映する前に確認してね。

NOTICES

  • 記事内容は実装させたものがほとんどですが自己責任で参考にしてください。