SVGでレイティングに使用するスター(星形)を実装するテクニックを解説
Post on:2021年8月31日
Webページやスマホアプリで見かけるレイティングに使用するスター(星形)を実装するSVGのテクニックを紹介します。
一口にスターといってもさまざまな状態があり、オン・オフ、半分、アウトライン、サイズ変更など、さまざまな実装テクニックが解説されています。
Star Rating: An SVG Solution
by Ahmad Shadeed
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
- はじめに
- 実装の要件
- スターを実装する基本のHTML
- アクセシビリティ
- SVGを再利用する方法
- スターのスタイル
- 部分的なスターを実装する
- 1. SVGのmaskを使用した実装
- 2. SVGグラデーションを使用した実装
- アウトラインスタイルのスターを実装する
- 1. SVGのmaskを使用した実装
- 2. SVGグラデーションを使用した実装
- スターのサイズ変更
- 終わりに
はじめに
ユーザーにコンテンツやレビューを提供するWebサイトやプラットフォームでは、星(スター)による評価を提供することが重要です。私は最近、プロジェクトで星評価(スターレーティング)のコンポーネントを実装する必要がありました。
要件は下記の通りです。
- パフォーマンス(画像は含まれません)
- サイズ変更可能
- アクセシブル
- 部分的な評価(3.5や3.2など)を動的に表示
- CSSでメンテナンスが簡単
スターをSVGで実装したところ、期待通りに機能しました。この記事では、星評価(スターレーティング)のコンポーネントを実装する方法、さまざまなシナリオでどのように機能するかについて解説します。
もしSVG以外の解決策を探しているのであれば、CSS Tricksの記事を読むことをお勧めします。
実装の要件
本題に入る前にコンテキストを整理して、要件と考慮しなければならないすべてのケースをお見せしたいと思います。
実装の要件
今回の要件は、塗りつぶしやストロークの変更、サイズの変更、ハーフスターの表示など、カスタマイズ可能な星を作ることに重点を置きます。
それでは、始めましょう!
スターを実装する基本のHTML
まず、ブラウザで使用できるように、スターの形を選択してSVGとして保存します。
1 2 3 |
<svg width="32" height="32" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"> <path d="..."/> </svg> |
このHTMLで、ブラウザに幅と高さのサイズが32pxのブラックのスターが表示されます。いい感じですね。
アクセシビリティ
スクリーンリーダーを使用するユーザーがこの情報にアクセスできるように、aria-label属性に評価に関する情報を含めます。
1 2 3 4 5 |
<p aria-label="Rating is 4.5 out of 5"> <svg width="32" height="32" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"> <path d="..."/> </svg> </p> |
SVGを再利用する方法
スターは5個必要なので、上記のコードを5回コピーして使用することもできますが、こここではパスデータを抽出して保存しコードを重複させずに再利用します。それでは実際にやってみましょう。
まず、幅と高さが0のSVGを作成します。これはスペースを確保しないためです。
1 2 3 |
<svg width="0" height="0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <!-- Content --> </svg> |
SVG内で、パスのデータを<symbol>要素内に含める必要があります。
MDNによると、
symbol要素は、<use>要素によってインスタンス化されるグラフィカルなテンプレートのオブジェクトを定義するために使用されます。
<symbol>要素内にあるのは、アイコンと同じコンテンツです。また、後でそのシンボルを参照できるようにidを加えておくことも重要です。
1 2 3 4 5 |
<svg width="0" height="0" xmlns="http://www.w3.org/2000/svg"> <symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" id="star"> <path d="..."/> </symbol> </svg> |
これで、<use>要素を使ってスターのシンボルを再利用できるようになりました。考え方としては、href属性の値としてidを使う必要があるということです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<p class="c-rate"> <svg class="c-icon" width="32" height="32"> <use xlink:href="#star"></use> </svg> <svg class="c-icon" width="32" height="32"> <use xlink:href="#star"></use> </svg> <svg class="c-icon" width="32" height="32"> <use xlink:href="#star"></use> </svg> <svg class="c-icon" width="32" height="32"> <use xlink:href="#star"></use> </svg> <svg class="c-icon" width="32" height="32"> <use xlink:href="#star"></use> </svg> </p> |
スターのスタイル
5個のスターを実装できたので、CSSの部分に入りましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<p class="c-rate"> <svg class="c-icon active" width="32" height="32"> <use xlink:href="#star"></use> </svg> <svg class="c-icon active" width="32" height="32"> <use xlink:href="#star"></use> </svg> <svg class="c-icon active" width="32" height="32"> <use xlink:href="#star"></use> </svg> <svg class="c-icon active" width="32" height="32"> <use xlink:href="#star"></use> </svg> <svg class="c-icon" width="32" height="32"> <use xlink:href="#star"></use> </svg> </p> |
スターのカラーにはイエローとグレーを定義しました。
1 2 3 4 5 6 7 8 9 |
.c-icon { --star-active: #fece3c; --star-inactive: #6c6962; fill: var(--star-inactive); } .c-icon.active { fill: var(--star-active); } |
上記のHTMLとCSSで、下記のように表示されます。
スターにカラーを定義
部分的なスターを実装する
.5などの評価に使用する部分的なスターを実装するには、ありがたいことにSVGでは2つの優れたソリューションがあります。1つ目は<mask>を使用、2つ目はSVGグラデーションを使用します。
1. SVGのmaskを使用した実装
<mask>を使用する目的は、スターの一部を消して、残りを半透明のカラーでペイントする効果をエミュレートすることです。そのためには、SVGの<mask>の力を借りなければなりません。
<mask>でスターの一部を消して、残りを半透明のカラーでペイント
上の図では、スター形と矩形があります。必要なのはそれらの交点で、交点によりスターを半分にします。
仕組みが分かったので、実装コードを見てましょう。
- 再利用可能なSVGのテンプレートを作成する。
- x=50%に配置された矩形の<mask>要素を追加する。
- スター形にマスクを適用する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<!-- 再利用可能なSVGのテンプレート --> <svg viewBox="0 0 32 32" id="star"> <defs> <mask id="half"> <rect x="50%" y="0" width="32" height="32" fill="white" /> </mask> <symbol viewBox="0 0 32 32" id="star"> <path d="..." /> </symbol> </defs> </svg> <!-- 使用例 --> <svg class="c-icon" width="32" height="32" viewBox="0 0 32 32"> <use xlink:href="#star" mask="url(#half)" fill="green"></use> </svg> |
結果は前述のように、スターが半分になります。
あとは、マスクされているときに半透明のスターを表示するにはどうすればよいかということです。SVGのおかげで<mask>の中に複数の要素を含めることができます。
別の要素を含めると、SVGの<mask>は下記のようになります。
1 2 3 4 |
<mask id="half"> <rect x="0" y="0" width="32" height="32" fill="white" /> <rect x="50%" y="0" width="32" height="32" fill="black" /> </mask> |
マスクは、白い要素が見せたいもの、黒い要素が隠したいものを表します。これを組み合わせることで、カットアウトの効果を生み出すことができます。
参考: 画像の一部を切り取るカットアウトを実装するCSSとSVGのテクニック
マスクの各要素を視覚的に説明した図をご覧ください。
白い矩形と黒い矩形に注目
白い矩形は0,0に配置され、黒の矩形は50,0に配置されていることに注目してください。視覚的には下記のようになります。
斜線の部分が表示される
何が起きているか分かりますか? 斜線の部分は、最終的にスターを半分にします。ここでもうひとつの半透明のスターを追加して、よりはっきりさせることができないかと考えているかもしれません。
ピュアブラックではなく明るい色を使用することで、半透明の効果が得られます。つまり、現在隠されている部分には、スターの色の明るい色合いになります。
1 2 3 4 |
<mask id="half"> <rect x="0" y="0" width="32" height="32" fill="white" /> <rect x="50%" y="0" width="32" height="32" fill="grey" /> </mask> |
明るい色を使用すると、半透明の効果が得られる
SVGのマークアップの完全版は下記のとおりです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<svg style="width: 0; height: 0;" viewBox="0 0 32 32"> <defs> <mask id="half"> <rect x="0" y="0" width="32" height="32" fill="white" /> <rect x="50%" y="0" width="32" height="32" fill="grey" /> </mask> <symbol viewBox="0 0 32 32" id="star"> <path d="..." /> </symbol> </defs> </svg> <p class="c-rate"> <svg class="c-icon" width="32" height="32" viewBox="0 0 32 32"> <use xlink:href="#star" mask="url(#half)" fill="green"></use> </svg> <!-- 4 more stars --> </p> |
これで、部分的に塗りつぶされたスターが実装できました。このソリューションの素晴らしいところは、2つの色合いを用意する必要がないことです。マスクがその役割を果たしてくれます。
1 2 3 4 |
<mask id="half"> <rect x="0" y="0" width="32" height="32" fill="white" /> <rect x="50%" y="0" width="32" height="32" fill="grey" /> </mask> |
2. SVGグラデーションを使用した実装
それでは、2つ目の実装方法を解説します。
これはStackoverflowでの回答にインスパイアされました。
マスクと同様に、<defs>要素にグラデーションを定義する必要があります。
1 2 3 4 5 6 7 8 |
<svg style="width: 0; height: 0;" viewBox="0 0 32 32"> <defs> <linearGradient id="half" x1="0" x2="100%" y1="0" y2="0"> <stop offset="50%" stop-color="#f7efc5"></stop> <stop offset="50%" stop-color="#fed94b"></stop> </linearGradient> </defs> </svg> |
カラーストップが2つあることに注目してください。1つ目のストップは前半を、2つ目のストップは明るい色合いを表しています。このソリューションでは、2つの色を手動で用意する必要があります。
1つ目は前半、2つ目は明るい色合いに
グラデーションの使い方は次のとおりです。
1 2 3 4 5 |
<p class="c-rate"> <svg class="c-icon" width="32" height="32" viewBox="0 0 32 32"> <use xlink:href="#star" fill="url(#half)"></use> </svg> </p> |
アウトラインスタイルのスターを実装する
アウトラインのスターも含めるようにカスタマイズできるバリエーションが必要です。一見、簡単そうに見えますね。では、実装方法を解説します。
アウトラインのスター
1. SVGのmaskを使用した実装
アウトラインのストロークを追加するには、SVG要素にstrokeを追加するだけです。これは、スターの完全形ではうまくいきます。しかし、部分的なスターでは、マスクのためにカットされてしまいます。
部分的なスターにもアウトラインのストロークを適用させたい
これを実現するには、アウトライン用に別のスター形を用意する必要があります。そのためには、<use>要素を複製して、マスクを除去します。
1 2 3 4 5 6 |
<p class="c-rate"> <svg class="c-icon" width="32" height="32" viewBox="0 0 32 32"> <use xlink:href="#star" mask="url(#half)" fill="green"></use> <use xlink:href="#star" fill="none" stroke="grey"></use> </svg> </p> |
<use>要素が2つあることに注目してください。マスクを使用したものとストロークのみのものです。これが、SVGのmaskを使用した実装です。
2. SVGグラデーションを使用した実装
グラデーションの場合は、マスクがないため、アイコンを複製する必要はありません。SVG要素にstrokeを追加するだけで完了です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<svg style="width: 0; height: 0;" viewBox="0 0 32 32"> <defs> <linearGradient id="half" x1="0" x2="100%" y1="0" y2="0"> <stop offset="50%" stop-color="#f7efc5"></stop> <stop offset="50%" stop-color="#fed94b"></stop> </linearGradient> </defs> </svg> <p class="c-rate"> <svg class="c-icon" width="32" height="32" viewBox="0 0 32 32"> <use xlink:href="#star" fill="url(#half)" stroke="grey"></use> </svg> </p> |
スターのサイズ変更
SVGにviewBox属性を正しく定義していれば、CSSの変数を使用して簡単にサイズを変更できます。
1 2 3 4 5 6 7 8 9 10 11 12 |
.c-icon { width: var(--size, 24px) height: var(--size, 24px); } .c-icon--md { --size: 40px; } .c-icon--lg { --size: 64px; } |
終わりに
この記事があなたのお役に立てれば幸いです。
コメントや提案があれば、@shadeed9までお願いします。
sponsors