1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

JavaScript + CSS: 決まった要素の上でだけマウスポインタをSVGのアイコンと置き替える

Last updated at Posted at 2020-12-14

はじめに

決まった要素にマウスポインタが重なったときだけ、カーソルをSVGのアイコンに置き替える作例です(サンプル001)。目立つポインタにすれば、プレゼンテーションなどに使えるでしょう。CSSのcursorプロパティでも、ポインタのかたちは変えられます。でも、SVGアイコンは好きなデザインや大きさが選べますし、プラットフォームやブラウザにかかわらず、見た目が同じになるというのは利点です。

しかも今回は、決まった要素の上でだけポインタが置き替わるという縛りが加わりました。これは要素に対するマウスイベントを組み合わせて対応します。つぎのサンプル001が作例です。左カラム(Left column)のうえでだけ、ポインタがSVGアイコンに変わります。

サンプル001■JavaScript + CSS: Setting custom SVG pointer to element

See the Pen JavaScript + CSS: Setting custom SVG pointer to element by Fumio Nonaka (@FumioNonaka) on CodePen.

この作例のおもなポイントはつぎのとおりです。
  • 要素の位置をマウスポインタに追随させる
  • ポインタ直下の要素にマウスイベントがさえぎられないようスルーさせる
  • 決まった要素にだけ対応するようにマウスイベントを組み合わせて使う
  • SVGにCSSで縁取りを加える

前に書いた記事「JavaScript + CSS: ヘッダを上部に固定してカラムの中身はスクロールさせる」のサンプル001に手を加えるかたちで進めましょう。

Font Awesomeの読み込み

SVGアイコンはFont Awesomeから使わせてもらいます。CDNで読み込むのが簡単です。

<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.1/css/all.css" />

カスタムポインタを加える

カスタムポインタは、左カラム(id属性left-column)上で出すことにします。同じ階層に加えた<div>要素(id属性custom-pointer)が、SVGのポインタです。なお、作例のスタイルにはBootstrapを使っています。

body要素
<div class="container-fluid d-flex px-0">
	<div id="custom-pointer">
		<i class="fas fa-hand-pointer fa-lg"></i>
	</div>
	<div id="left-column" class="bg-secondary text-light p-2">

	</div>

</div>

カスタムポインタのCSSはposition: fixed;とすることで、位置を自由に変えられるようにします。z-indexは手前にし、必要な設定を加えてください。これでまずは、左カラムの左上角にカスタムポインタが表れるはずです(図001)。

style要素
#custom-pointer {
	position: fixed;
	color: #3b5998;
	z-index: 1000;
	font-size: 2rem;
}

図001■左カラムに加わったカスタムポインタのSVG

2012002_001.png

カスタムポインタの位置を動かす

positionfixedにした要素は、CSSのlefttopプロパティで位置が動かせます。JavaScriptコードで用いるのはstyleプロパティです。プロパティ値は文字列で、CSSと同じく単位が要ります。たとえば、DOMContentLoadedイベントのリスナーにつぎの3行のコードを加えて、カスタムポインタの位置が変わることを確かめてください(図002)。

script要素
document.addEventListener('DOMContentLoaded', () => {

	const customPointer = document.getElementById('custom-pointer');
	customPointer.style.left = '200px';
	customPointer.style.top = '100px';
});

図002■カスタムポインタの位置が動いた

2012002_002.png

要素の上でポインタが動いたことを捉えるのは、mousemoveイベントです。要素に加えたイベントリスナーで、カスタムポインタをマウス座標に動かせばよいでしょう。これでSVGを収めた要素(customPointer)がマウスポインタに追随します。

script要素
document.addEventListener('DOMContentLoaded', () => {

	const leftColumn = document.getElementById('left-column');

	const mouseAdjust = { x: -13, y: -7 };
	leftColumn.addEventListener('mousemove', ({ clientX, clientY }) => {
		customPointer.style.left = (clientX + mouseAdjust.x) + 'px';
		customPointer.style.top = (clientY + mouseAdjust.y) + 'px';
	});

});

手前の要素にマウスイベントをスルーさせる

ここで、不便なことに気づきます。左カラムのスクロールバーが動かせません(図003)。なぜかというと、マウス直下のカスタムポインタがイベントを奪って、親の左カラム要素に伝わらないからです。このあと使うつもりのmouseentermouseleaveイベントも、意図どおり渡らなくなります。

図003■左カラムのスクロールバーがドラッグできない

2012002_003.png

イベントを邪魔するなと伝えるCSSの設定が、pointer-events: none;です。これで、マウスイベントはカスタムポインタの要素をスルーするので、スクロールバーも操作できるようになります。

style要素
#custom-pointer {

	pointer-events: none;
}

要素の外ではカスタムポインタを消す

左カラム内のカスタムポインタの動きは、これでよいでしょう。でも、マウスポインタが要素の外に出たとき、その直前の位置にカスタムポインタが取り残されてしまいます。左カラムの外では、SVGアイコンは消すべきです。

要素の上にマウスポインタが重なったか、外に出たかは、それぞれマウスイベントmouseenter mouseleave で捉えられます。表示/非表示を切り替えるのは、CSSのdisplayプロパティです。

script要素
document.addEventListener('DOMContentLoaded', () => {

	leftColumn.addEventListener('mouseenter', (event) => {
		customPointer.style.display = 'block';
	});
	leftColumn.addEventListener('mouseleave', (event) => {
		customPointer.style.display = 'none';
	});
});

SVGに白い縁取りを加える

カスタムポインタの色を暗めの青にしたため、背景色が濃かったり暗いと見づらくなります。白い縁取りを加えることにしましょう。用いるのはtext-shadowプロパティです。3つ目の数値がぼけ幅で、0にすればくっきりした影になります。とはいえ、水平または垂直にずらさなければ見えません。

幸い、影は複数の設定をカンマ区切りで重ねることができます。上下左右4つの設定を1pxずつずらして組み合わせれば、くっきりとした縁取りができ上がるのです(図004)。

style要素
#custom-pointer {

	text-shadow: 1px 0 0 white,
		0 1px 0 white,
		-1px 0 0 white,
		0 -1px 0 white;
}

図004■カスタムアイコンのSVGに白い縁取りが加わった

2012002_004.png

filterプロパティにdrop-shadow()関数を使う

テキストやSVG以外の画像も含めた要素に、そのかたちに沿った縁を加えたい場合には、filterプロパティにdrop-shadow()関数が使えます(「box-shadowだけじゃない!CSSでできる色々な影の表現と意外に知らない落とし穴」参照)。3つ目の数値がぼけ幅で、関数は複数定められますので、text-shadowと同じように用いることができるのです。ただし、まだ仕様は「草案(WD)」であることにご注意ください。

仕上げ

左カラムの要素の上でだけマウスポインタをSVGのアイコンと置き替えるというお題からは、標準のマウスポインタは消してしまうのがよいでしょう。その場合のCSSの定めはcursor: none;です。

でも、カスタムポインタが本当のカーソルから少し遅れることもありえます。また、サイズを大きくしたので、ホットスポット(クリックなどのマウス操作の基準点)がわかりにくくなりそうです。そこで、細いプラス(+)表示を選びました。

style要素
#left-column {

	cursor: crosshair;
}

あとは、ロードしたときのカスタムポインタの表示です。displayプロパティで非表示にしてしまうと、マウスポインタが左カラム上にあるままロードしたとき、mouseenterイベントが起こらず表示されません。かといって、mousemoveのたびに表示し直すというのも冗長です。

そこで、はじめはカスタムポインタをウィンドウの外に出してしまうことにしました。見かけ上、ロード時にポインタは表示されません。左カラムにmouseenter、あるいははじめから要素の上にポインタがあったときも、少しでもマウスを動かしたときmousemoveで表れることになります。

style要素
document.addEventListener('DOMContentLoaded', () => {

	customPointer.style.left = '-100px';
	customPointer.style.top = '-100px';
});

こうしてでき上がったのが、冒頭のサンプル001です。具体的なコードの記述や動きについては、こちらでお確かめください。

1
3
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
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?