CSSのスゴ技!美しいグラデーションのボーダー、角丸や半円のテクニックを使用したチケットを実装する方法
Post on:2020年11月10日
CSSで実装された美しいグラデーションのボーダー、さらにはそれを角丸し、半円を加えたチケットを実装するスタイルシートのテクニックを紹介します。
グラデーションのボーダーは割と簡単に実装できますが、角丸にするのは目から鱗でした。また、簡単なJavaScriptで3Dも再現されています。
Gradient borders with curves and 3d movement in CSS
by Medhat Dawoud
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
はじめに
2020年10月27日にNext.jsの初のグローバルユーザーカンファレンスが開催され、私はReactデベロッパーとしてとても楽しみにしていました。知ってすぐに登録したのですが、登録してからの出来事も非常に面白いものでした。
主催者から確認メッセージが届き、そこにあったURLをクリックすると、インタラクティブなチケットが表示され、気持ちよくアニメーションしていたのです!
このチケットを実装したデザイナーとデベロッパーに感謝します。そして、私は学習目的でこのチケットを実装してみようと思いました。
実装のポイント
このチケットを実装するには、解決すべき課題がかなりあります。
- チケット自体の構築(事前に作成されたもので始めます)
- グラデーションのボーダーを実装、しかも角丸で
- 左右の半円を実装、グラデーションと同じカラーで
- カーソルの動きに応じた3Dのアニメーションを実装
実装は段階的に解説します。
最終的なコードはGitHubにアップしてあるので、ご覧ください。
追記
フォローアップして、他の実装方法の解説記事も公開されました。
1. チケット自体の構築
前述したように、HTMLは事前に作成されたもので始めます。コンテンツは省略しており、全体のコードはGitHubでご覧ください。
1 2 3 4 5 6 7 |
<div class="ticket-visual_visual" id="ticket"> <div class="left"></div> <div class="right"></div> <div class="ticket-visual-wrapper"> <!-- チケットのコンテンツ --> </div> </div> |
ポイントは、入れ子のdiv要素(.ticket-visual_visualと.ticket-visual-wrapper)と半円用のdiv要素(.leftと.right)です。
まずは、基本的なCSSです。
※コンテンツは省略しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
:root { --size: 1; --background: #000; } body { background: var(--background); color: white; font-family: Arial, Helvetica, sans-serif; } * { box-sizing: border-box; } .ticket-visual_visual { width: 650px; height: 320px; margin: 100px auto; position: relative; transition: all 300ms cubic-bezier(0.03, 0.98, 0.53, 0.99) 0s; border: 5px solid #fff; } .ticket-visual-wrapper { width: 100%; height: 100%; } |
ここまでは非常に簡単で、以下のように表示されます。
デモの表示
2. グラデーションのボーダーを実装
グラデーションや画像をボーダーにするためのプロパティはborder-imageで、MDNによるとIE11を含むすべてのブラウザでサポートされています。
しかし、このチケットを実装するには問題点があります。それは角丸を実装するborder-radiusをサポートしていないため、残念ながら使用できません。この角丸を実装するには、いくつかのテクニックがあります。
テクニックの1つは、divを入れ子にしてそれぞれに背景と角丸を適用することです。親divと子divと呼ぶことにします。親divに背景としてグラデーションを適用し、子divには単色を適用します。このチケットはブラック一色で必要なボーダーの幅は5pxなので、親divにpaddingを与えます。このpaddingは要素のボーダーとコンテンツの間にスペースを作るので、上下左右の全方向から子divを5px押し、親divから子divのボーダーが5pxであるかのように表示されます。
このテクニックを使用して、実装してみます。
親divの.ticket-visual_visualがあり、グラデーションの背景を与えます。CSSの変数で定義しておくと便利です、この変数は半円の実装にも使用します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
:root { // rest of variable --color1: #d25778; --color2: #ec585c; --color3: #e7d155; --color4: #56a8c6; } .ticket-visual_visual { // other code here background: linear-gradient( to right, var(--color1), var(--color2), var(--color3), var(--color4) ); } |
メモ: 線形グラデーションを使用していることに注目してください。左から右へのグラデーションが必要なため、最初のパラメーターは右です。
次に、子divの.ticket-visual-wrapperに、背景を定義します。
1 2 3 |
.ticket-visual-wrapper { background: var(--background); // --background is #000 } |
これで、グラデーションのボーダーが実装できました。
続いて、ボーダーの角丸を実装してみましょう。子divの.ticket-visual-wrapperに、border-radiusを定義します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
.ticket-visual_visual { // other styles background: linear-gradient( to right, var(--color1), var(--color2), var(--color3), var(--color4) ); border-radius: 20px; } .ticket-visual-wrapper { // other styles background: var(--background); border-radius: 15px; } |
ここまでで、以下のように表示されます。
グラデーションのボーダーと角丸を実装
3. 左右の半円を実装
注: 同じ結果を得るために、他にもいくつかの異なる方法があります。
左右の半円を実装するために前述と同じアイデアで、親divの疑似要素を親要素として、子divの疑似要素を子要素として使用します。
まずは、正円を:beforeと:afterで実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
.ticket-visual_visual:before { content: ""; display: block; position: absolute; top: 130px; left: -30px; width: 60px; height: 60px; border-radius: 50%; background: var(--color1); z-index: 2; } .ticket-visual_visual:after { content: ""; display: block; position: absolute; top: 130px; right: -30px; width: 60px; height: 60px; border-radius: 50%; background: var(--color4); z-index: 2; } |
この2つの正円をカードの真ん中の左右に配置し、グラデーションのカラーに揃うようにカラーを定義します。ここが変数の役立つポイントです。左には--color1を、右には--color4を定義します。
これで下記のように表示されます。
カードの真ん中の左右に、正円を配置
2つの正円に一回り小さいブラックの子円を追加します。
.ticket-visual-wrapperの疑似要素に子円を追加し、position: relative;を忘れずに加えましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
.ticket-visual-wrapper { width: 100%; height: 100%; background: var(--background); border-radius: 15px; position: relative; } .ticket-visual-wrapper:before { content: ""; display: block; position: absolute; top: 130px; left: -30px; width: 50px; height: 50px; border-radius: 50%; background: var(--background); z-index: 3; } .ticket-visual-wrapper:after { content: ""; display: block; position: absolute; top: 130px; right: -30px; width: 50px; height: 50px; border-radius: 50%; background: var(--background); z-index: 3; } |
2つの子円を50x50pxにし、親円を60x60pxで作成しました。両方の背景はブラックにしています。最後のポイントは、z-index: 3;にすることです。これで親の疑似要素の上に表示されます。
ここまでで、以下のように表示されます。
ボーダーの正円が完成
あとは、この正円の半分を非表示にするだけです。カバーのようなものがあれば解決することが分かったので、.ticket-visual_visualの中にカバーとして使用できる2つのdivを追加することにしました。
1 2 |
<div class="left"></div> <div class="right"></div> |
CSSでは、position: relative;のdiv内にあるため、position: absolute;を与えることで、適切に配置されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
.left { position: absolute; top: 110px; left: -50px; width: 50px; height: 100px; background: var(--background); z-index: 4; } .right { position: absolute; top: 110px; right: -50px; width: 50px; height: 100px; background: var(--background); z-index: 4; } |
背景はブラック(--background)にし、z-index: 4;で正円をカバーして半円すると、以下のように表示されます。
ボーダーの半円が完成
これでカンファレンスにあるチケットと同じデザインが完成しました。
4. カーソルの動きに応じたアニメーションを実装
最後は、JavaScriptです。
カーソルが移動する度にその位置を変数で計算するために、mousemoveイベントリスナーを使用します。
1 2 3 |
window.addEventListener("mousemove", e => { // ユーザーがカーソルを動かす度に実行するコード }) |
外部ファイルにする必要がないので、同じHTMLファイルにインラインのscriptタグで記述します。
リスニングの前に、ticket要素の外接矩形を取得して、ticket要素の中心点を計算する必要があります。
1 2 3 |
const ticketElm = document.getElementById("ticket") const { x, y, width, height } = ticketElm.getBoundingClientRect() const centerPoint = { x: x + width / 2, y: y + height / 2 } |
次に、mousemoveイベントリスナー内に、そのチケットを変換するためコードを追加します。下記のように、回転に使用する度数の計算を追加します。
1 2 |
const degreeX = (e.clientY - centerPoint.y) * 0.008 const degreeY = (e.clientX - centerPoint.x) * -0.008 |
この計算式は、現在のマウスの位置と中心点との差を取得し、その差に小さな数字0.008と-0.008を掛けています。この数値は最適に感じるまで、私が試行錯誤して取得したものです。
次に、これらの計算された度数を使用して変換します。
1 2 3 4 5 6 |
window.addEventListener("mousemove", e => { const degreeX = (e.clientY - centerPoint.y) * 0.008 const degreeY = (e.clientX - centerPoint.x) * -0.008 ticketElm.style.transform = `perspective(1000px) rotateX(${degreeX}deg) rotateY(${degreeY}deg)` }) |
L.5ではperspectiveを1000pxに設定しており、要素を回転させずに非常にスムーズに移動できることが分かります。また、計算された度数に基づいてx軸とy軸の回転を使用しています。
これで完成です。
完成したデモ
マウスを動かすとグラデーションに光沢があるように感じられると思いますが、それはチケットに光沢感を与えるためのものです。
この実装で学んだこと
私はこの記事を楽しんで書き、また読むことを楽しんでもらえたらと思います。私はこの実装で複数のことを学びました。
- border-radiusを使用したグラデーションのボーダーを実装する方法
- グラデーションのボーダーを持つ半円を実装する方法
- 3Dアニメーションの実装でperspectiveを使用する方法
- 変数の計算についての考え方
- すべてのコードはGithubにあります。チェックして、フォークし、クローンを作成してください😉
sponsors