はじめに
決まった要素にマウスポインタが重なったときだけ、カーソルを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を使っています。
<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)。
#custom-pointer {
position: fixed;
color: #3b5998;
z-index: 1000;
font-size: 2rem;
}
図001■左カラムに加わったカスタムポインタのSVG
カスタムポインタの位置を動かす
position
をfixed
にした要素は、CSSのleft
とtop
プロパティで位置が動かせます。JavaScriptコードで用いるのはstyle
プロパティです。プロパティ値は文字列で、CSSと同じく単位が要ります。たとえば、DOMContentLoaded
イベントのリスナーにつぎの3行のコードを加えて、カスタムポインタの位置が変わることを確かめてください(図002)。
document.addEventListener('DOMContentLoaded', () => {
const customPointer = document.getElementById('custom-pointer');
customPointer.style.left = '200px';
customPointer.style.top = '100px';
});
図002■カスタムポインタの位置が動いた
要素の上でポインタが動いたことを捉えるのは、mousemove
イベントです。要素に加えたイベントリスナーで、カスタムポインタをマウス座標に動かせばよいでしょう。これでSVGを収めた要素(customPointer
)がマウスポインタに追随します。
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)。なぜかというと、マウス直下のカスタムポインタがイベントを奪って、親の左カラム要素に伝わらないからです。このあと使うつもりのmouseenter
やmouseleave
イベントも、意図どおり渡らなくなります。
図003■左カラムのスクロールバーがドラッグできない
イベントを邪魔するなと伝えるCSSの設定が、pointer-events: none;
です。これで、マウスイベントはカスタムポインタの要素をスルーするので、スクロールバーも操作できるようになります。
#custom-pointer {
pointer-events: none;
}
要素の外ではカスタムポインタを消す
左カラム内のカスタムポインタの動きは、これでよいでしょう。でも、マウスポインタが要素の外に出たとき、その直前の位置にカスタムポインタが取り残されてしまいます。左カラムの外では、SVGアイコンは消すべきです。
要素の上にマウスポインタが重なったか、外に出たかは、それぞれマウスイベントmouseenter
とmouseleave
で捉えられます。表示/非表示を切り替えるのは、CSSのdisplay
プロパティです。
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)。
#custom-pointer {
text-shadow: 1px 0 0 white,
0 1px 0 white,
-1px 0 0 white,
0 -1px 0 white;
}
図004■カスタムアイコンのSVGに白い縁取りが加わった
filter
プロパティにdrop-shadow()
関数を使う
テキストやSVG以外の画像も含めた要素に、そのかたちに沿った縁を加えたい場合には、filter
プロパティにdrop-shadow()
関数が使えます(「box-shadowだけじゃない!CSSでできる色々な影の表現と意外に知らない落とし穴」参照)。3つ目の数値がぼけ幅で、関数は複数定められますので、text-shadow
と同じように用いることができるのです。ただし、まだ仕様は「草案(WD)」であることにご注意ください。
仕上げ
左カラムの要素の上でだけマウスポインタをSVGのアイコンと置き替えるというお題からは、標準のマウスポインタは消してしまうのがよいでしょう。その場合のCSSの定めはcursor: none;
です。
でも、カスタムポインタが本当のカーソルから少し遅れることもありえます。また、サイズを大きくしたので、ホットスポット(クリックなどのマウス操作の基準点)がわかりにくくなりそうです。そこで、細いプラス(+)表示を選びました。
#left-column {
cursor: crosshair;
}
あとは、ロードしたときのカスタムポインタの表示です。display
プロパティで非表示にしてしまうと、マウスポインタが左カラム上にあるままロードしたとき、mouseenter
イベントが起こらず表示されません。かといって、mousemove
のたびに表示し直すというのも冗長です。
そこで、はじめはカスタムポインタをウィンドウの外に出してしまうことにしました。見かけ上、ロード時にポインタは表示されません。左カラムにmouseenter
、あるいははじめから要素の上にポインタがあったときも、少しでもマウスを動かしたときmousemove
で表れることになります。
document.addEventListener('DOMContentLoaded', () => {
customPointer.style.left = '-100px';
customPointer.style.top = '-100px';
});
こうしてでき上がったのが、冒頭のサンプル001です。具体的なコードの記述や動きについては、こちらでお確かめください。