ウェブサイトに独自のスタイルを取り入れるカスタムカーソルは、ユーザー体験を向上させる効果的な手法の一つです。
今回は案件で実装したマウスカーソルを記事にまとめてみました。
※JavaScriptコードは、クラス構文とES Modules形式で書いています。
1. HTML:カーソル要素の準備
まず、カスタムカーソルとして表示する要素をHTMLに用意します。ここでは、シンプルにドットとアウトラインの2つのdiv要素を使用します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom Cursor Guide</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="js-cursor-dot"></div>
<div class="js-cursor-dot-outline"></div>
<h1>カスタムカーソルデモ</h1>
<p><a href="#">リンク1</a></p>
<p class="js-link">カスタムリンク要素</p>
<p><a href="#">リンク2</a></p>
<script type="module" src="main.js"></script>
</body>
</html>
このHTMLでは、**js-cursor-dotとjs-cursor-dot-outline**というクラス名を持つdiv要素を作成しました。JavaScriptからこれらの要素にアクセスするため、特定のクラス名を付与しておくと便利です。
また、<script>タグに**type="module"**を追加し、ES Modules形式でJavaScriptファイルを読み込んでいます。
2. CSS:カーソルの見た目と動作設定
次に、HTMLで作成した要素にスタイルを適用します。ここでは、カーソルの見た目だけでなく、重要な動作設定もCSSで行います。
/* デフォルトのカーソルを非表示にする */
body {
cursor: none;
height: 200vh; /* スクロールテスト用 */
}
/* カスタムカーソル要素の共通設定 */
.js-cursor-dot,
.js-cursor-dot-outline {
/* 常にビューポートに固定する */
position: fixed;
top: 0;
left: 0;
/* transformの起点と中央揃え */
transform: translate(-50%, -50%);
border-radius: 50%;
/* カーソル下の要素のクリックを妨げない */
pointer-events: none;
/* 最前面に表示 */
z-index: 9999;
/* サイズと不透明度の変化をアニメーションさせる */
transition: transform 0.2s ease-out, opacity 0.2s ease-out;
}
/* ドットのスタイル */
.js-cursor-dot {
width: 8px;
height: 8px;
background-color: #333;
}
/* アウトラインのスタイル */
.js-cursor-dot-outline {
width: 40px;
height: 40px;
border: 2px solid #333;
}
/* インタラクティブ要素(リンクなど)にホバーした時のデフォルトカーソルも非表示にする */
a, .js-link {
cursor: none;
color: blue;
text-decoration: underline;
margin: 20px;
display: inline-block;
}
このCSSで最も重要な設定は以下の3つです。
body { cursor: none; }: これにより、ブラウザのデフォルトカーソルを非表示にし、カスタムカーソルだけが表示されるようになります。position: fixed;: カーソル要素がスクロールしても画面上の同じ位置に留まるように設定します。JavaScriptと連携して、スクロールに追従する動きを実現するために不可欠です。pointer-events: none;: この設定により、カーソル要素自体がマウスイベントをブロックしなくなります。カーソルの下にあるリンクやボタンなどの要素が、正常にクリックできるようになります。
3. JavaScript:カーソルの動きとインタラクション
最後に、カーソルをマウスの動きに追従させたり、ホバー時に見た目を変えたりするロジックをJavaScriptで実装します。
Cursor.js
/**
* Cursor.js
*
* カスタムカーソルを実装し、インタラクティブな要素(リンクなど)にホバーした際に
* カーソルの見た目を変更する機能を提供します。
*/
class Cursor {
constructor() {
this.delay = 2;
this._x = 0;
this._y = 0;
this.endX = window.innerWidth / 2;
this.endY = window.innerHeight / 2;
this.cursorVisible = true;
this.cursorEnlarged = false;
this.$dot = null;
this.$outline = null;
this.animateDotOutline = this.animateDotOutline.bind(this);
}
/**
* カーソルモジュールの初期化
*/
init() {
this.$dot = document.querySelector('.js-cursor-dot');
this.$outline = document.querySelector('.js-cursor-dot-outline');
if (!this.$dot || !this.$outline) {
console.warn('カーソル要素が見つかりません。HTMLを確認してください。');
return;
}
this.dotSize = this.$dot.offsetWidth;
this.outlineSize = this.$outline.offsetWidth;
this.setupEventListeners();
this.animateDotOutline();
}
/**
* イベントリスナーの設定
*/
setupEventListeners() {
const interactiveElements = document.querySelectorAll('a, .js-link');
interactiveElements.forEach(el => {
el.addEventListener('mouseover', () => this.toggleCursorSize(true));
el.addEventListener('mouseout', () => this.toggleCursorSize(false));
});
document.addEventListener('mousedown', () => this.toggleCursorSize(true));
document.addEventListener('mouseup', () => this.toggleCursorSize(false));
document.addEventListener('mousemove', e => {
this.cursorVisible = true;
// ビューポート座標を使用することでスクロールに追従
this.endX = e.clientX;
this.endY = e.clientY;
this.$dot.style.top = `${this.endY}px`;
this.$dot.style.left = `${this.endX}px`;
this.toggleCursorVisibility();
});
document.addEventListener('mouseenter', () => this.toggleCursorVisibility(true));
document.addEventListener('mouseleave', () => this.toggleCursorVisibility(false));
}
/**
* ドットアウトラインのアニメーション
*/
animateDotOutline() {
this._x += (this.endX - this._x) / this.delay;
this._y += (this.endY - this._y) / this.delay;
this.$outline.style.top = `${this._y}px`;
this.$outline.style.left = `${this._x}px`;
requestAnimationFrame(this.animateDotOutline);
}
/**
* カーソルサイズの切り替え
*/
toggleCursorSize(enlarged) {
const scaleDot = enlarged ? 0.75 : 1;
const scaleOutline = enlarged ? 1.5 : 1;
this.$dot.style.transform = `translate(-50%, -50%) scale(${scaleDot})`;
this.$outline.style.transform = `translate(-50%, -50%) scale(${scaleOutline})`;
}
/**
* カーソル表示の切り替え
*/
toggleCursorVisibility(visible) {
const opacity = visible ? 1 : 0;
this.$dot.style.opacity = opacity;
this.$outline.style.opacity = opacity;
}
}
/**
* カスタムカーソルを開始
*/
export function cursorStart() {
const customCursor = new Cursor();
customCursor.init();
}
main.js
import { cursorStart } from './Cursor.js';
document.addEventListener('DOMContentLoaded', () => {
cursorStart();
});
特に重要なポイントは、mousemoveイベントで**e.clientXとe.clientY**というビューポート座標を使用している点です。これにより、CSSのposition: fixedと連携して、スクロール中もマウスに正確に追従する動きを実現しています。
animateDotOutlineメソッドではrequestAnimationFrameと慣性の計算を組み合わせることで、アウトラインがドットに少し遅れて追従する滑らかなアニメーションを生み出しています。





