ウェブサイトに独自のスタイルを取り入れるカスタムカーソルは、ユーザー体験を向上させる効果的な手法の一つです。
今回は案件で実装したマウスカーソルを記事にまとめてみました。
※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この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;}JavaScriptこの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();}JavaScriptmain.js
import { cursorStart } from './Cursor.js';document.addEventListener('DOMContentLoaded', () => { cursorStart();});JavaScript特に重要なポイントは、mousemoveイベントで**e.clientXとe.clientY**というビューポート座標を使用している点です。これにより、CSSのposition: fixedと連携して、スクロール中もマウスに正確に追従する動きを実現しています。
animateDotOutlineメソッドではrequestAnimationFrameと慣性の計算を組み合わせることで、アウトラインがドットに少し遅れて追従する滑らかなアニメーションを生み出しています。