サイトにCSSだけで魔法陣を散りばめて新しい表現を模索してみる(前編)
UI開発者 板垣突然ですが皆さん、魔法陣はお好きですか?
特に男性のみなさんは、一度くらいは紙や地面に描いたことがあるのではないでしょうか?
あの童心をくすぐるデザインをどうにかしてサイトに取り入れることはできないかと考え、サイトにある様々なパーツと組み合わせて新しい表現を模索していきたいと思います!
今回は、記事を前編と後編に分けて魔法陣の実装方法からサイトのパーツと組み合わせた際の表現までをご紹介いたします!
実装方法
作成する魔法陣はこちらです。
マウスホバー時に円の端から魔法陣が光りだす仕様になっています。
それでは早速この魔法陣の実装方法を見ていきましょう!
HTML
マークアップです。
<p class="magic-square-content__txt--xx">hoge</p>には解読不能文字の元となる文字を入れます。
レイアウト崩れの原因になるので一要素、一文字にしましょう!
<div class="magic-square">
<div class="magic-square-content">
<div class="magic-square-content__inner">
<p class="magic-square-content__txt--top">ミ</p>
<p class="magic-square-content__txt--top-right">ツ</p>
<p class="magic-square-content__txt--right">エ</p>
<p class="magic-square-content__txt--bottom-right">ー</p>
<p class="magic-square-content__txt--bottom">リ</p>
<p class="magic-square-content__txt--bottom-left">ン</p>
<p class="magic-square-content__txt--left">ク</p>
<p class="magic-square-content__txt--top-left">ス</p>
</iv>
</div>
</div>
CSS
指定しているCSSです。
CSS
*{margin:0;padding:0}body{background:black;padding:30px}.magic-square{box-sizing:border-box;position:relative;max-width:200px;width:100%;border:1px solid #29B6F6;border-radius:50%;box-shadow:0 0 15px 2px #29B6F6, 0 0 3px 0 #29B6F6 inset;transform:perspective(400px) rotateX(60deg);transition:all 0.3s;overflow:hidden}.magic-square:hover{box-shadow:0 0 30px 10px #B3E5FC, 0 0 3px 0 #B3E5FC inset}.magic-square:hover .magic-square-content::before,.magic-square:hover .magic-square-content::after{border:1px solid #E1F5FE;box-shadow:0 0 2px 1px #E1F5FE, 0 0 2px 1px #E1F5FE inset}.magic-square:hover .magic-square-content__inner::before,.magic-square:hover .magic-square-content__inner::after{border:1px solid #E1F5FE;box-shadow:0 0 6px 4px #E1F5FE, 0 0 3px 1px #E1F5FE inset}.magic-square:hover .magic-square-content .magic-square-content__txt--top,.magic-square:hover .magic-square-content .magic-square-content__txt--top-right,.magic-square:hover .magic-square-content .magic-square-content__txt--right,.magic-square:hover .magic-square-content .magic-square-content__txt--bottom-right,.magic-square:hover .magic-square-content .magic-square-content__txt--bottom,.magic-square:hover .magic-square-content .magic-square-content__txt--bottom-left,.magic-square:hover .magic-square-content .magic-square-content__txt--left,.magic-square:hover .magic-square-content .magic-square-content__txt--top-left{color:#E1F5FE}.magic-square .magic-square-content{padding-top:100%;animation:magicSquare 10s linear infinite}@keyframes magicSquare{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.magic-square .magic-square-content::before,.magic-square .magic-square-content::after{display:block;content:"";transition:all 0.3s 1.7s;box-sizing:border-box;position:absolute;top:50%;left:50%;border:1px solid #29B6F6;border-radius:50%}.magic-square .magic-square-content::before{width:80%;margin-top:-40%;margin-left:-40%;padding-top:80%}.magic-square .magic-square-content::after{width:74%;margin-top:-37%;margin-left:-37%;padding-top:74%}.magic-square .magic-square-content__inner{position:absolute;top:0;left:0;width:100%;height:100%}.magic-square .magic-square-content__inner::before,.magic-square .magic-square-content__inner::after{box-sizing:border-box;position:absolute;left:25%;top:25%;width:50%;height:50%;border:1px solid #29B6F6;box-shadow:0 0 3px 0 #29B6F6, 0 0 1px 0 #29B6F6 inset;transition:all 0.6s 2s;content:""}.magic-square .magic-square-content__inner::after{transform:rotate(45deg)}.magic-square .magic-square-content .magic-square-content__txt--top,.magic-square .magic-square-content .magic-square-content__txt--top-right,.magic-square .magic-square-content .magic-square-content__txt--right,.magic-square .magic-square-content .magic-square-content__txt--bottom-right,.magic-square .magic-square-content .magic-square-content__txt--bottom,.magic-square .magic-square-content .magic-square-content__txt--bottom-left,.magic-square .magic-square-content .magic-square-content__txt--left,.magic-square .magic-square-content .magic-square-content__txt--top-left{position:absolute;color:#29B6F6;font-size:14px;font-weight:bold}.magic-square .magic-square-content .magic-square-content__txt--top::before,.magic-square .magic-square-content .magic-square-content__txt--top-right::before,.magic-square .magic-square-content .magic-square-content__txt--right::before,.magic-square .magic-square-content .magic-square-content__txt--bottom-right::before,.magic-square .magic-square-content .magic-square-content__txt--bottom::before,.magic-square .magic-square-content .magic-square-content__txt--bottom-left::before,.magic-square .magic-square-content .magic-square-content__txt--left::before,.magic-square .magic-square-content .magic-square-content__txt--top-left::before,.magic-square .magic-square-content .magic-square-content__txt--top::after,.magic-square .magic-square-content .magic-square-content__txt--top-right::after,.magic-square .magic-square-content .magic-square-content__txt--right::after,.magic-square .magic-square-content .magic-square-content__txt--bottom-right::after,.magic-square .magic-square-content .magic-square-content__txt--bottom::after,.magic-square .magic-square-content .magic-square-content__txt--bottom-left::after,.magic-square .magic-square-content .magic-square-content__txt--left::after,.magic-square .magic-square-content .magic-square-content__txt--top-left::after{position:absolute;top:0;left:0}.magic-square .magic-square-content .magic-square-content__txt--top::before,.magic-square .magic-square-content .magic-square-content__txt--top-right::before,.magic-square .magic-square-content .magic-square-content__txt--right::before,.magic-square .magic-square-content .magic-square-content__txt--bottom-right::before,.magic-square .magic-square-content .magic-square-content__txt--bottom::before,.magic-square .magic-square-content .magic-square-content__txt--bottom-left::before,.magic-square .magic-square-content .magic-square-content__txt--left::before,.magic-square .magic-square-content .magic-square-content__txt--top-left::before{content:"ホ"}.magic-square .magic-square-content .magic-square-content__txt--top::after,.magic-square .magic-square-content .magic-square-content__txt--top-right::after,.magic-square .magic-square-content .magic-square-content__txt--right::after,.magic-square .magic-square-content .magic-square-content__txt--bottom-right::after,.magic-square .magic-square-content .magic-square-content__txt--bottom::after,.magic-square .magic-square-content .magic-square-content__txt--bottom-left::after,.magic-square .magic-square-content .magic-square-content__txt--left::after,.magic-square .magic-square-content .magic-square-content__txt--top-left::after{content:"ノ"}.magic-square .magic-square-content__txt--top{top:0;left:50%;transform:translateX(-50%);transition:all 0.2s 0.3s}.magic-square .magic-square-content__txt--top-right{top:12.5%;right:15%;transition:all 0.2s 0.5s}.magic-square .magic-square-content__txt--right{top:50%;right:1.5%;transform:translateY(-50%);transition:all 0.3s 0.7s}.magic-square .magic-square-content__txt--bottom-right{right:15%;bottom:12.5%;transition:all 0.3s 0.9s}.magic-square .magic-square-content__txt--bottom{bottom:0;left:50%;transform:translateX(-50%);transition:all 0.3s 1.1s}.magic-square .magic-square-content__txt--bottom-left{bottom:12.5%;left:15%;transition:all 0.3s 1.3s}.magic-square .magic-square-content__txt--left{top:50%;left:1.5%;transform:translateY(-50%);transition:all 0.3s 1.5s}.magic-square .magic-square-content__txt--top-left{top:12.5%;left:15%;transition:all 0.3s 1.7s}
Sass
*{margin:0;padding:0}body{background:black;padding:30px}.magic-square{box-sizing:border-box;position:relative;max-width:200px;width:100%;border:1px solid #29B6F6;border-radius:50%;box-shadow:0 0 15px 2px #29B6F6,0 0 3px 0 #29B6F6 inset;transform:perspective(400px) rotateX(60deg);transition:all .3s;overflow:hidden;&:hover{box-shadow:0 0 30px 10px #B3E5FC,0 0 3px 0 #B3E5FC inset;.magic-square-content{&::before,&::after{border:1px solid #e1f5fe;box-shadow:0 0 2px 1px #e1f5fe,0 0 2px 1px #e1f5fe inset}&__inner{&::before,&::after{border:1px solid #e1f5fe;box-shadow:0 0 6px 4px #e1f5fe,0 0 3px 1px #e1f5fe inset}}%magic-square__txt{color:#e1f5fe}}}.magic-square-content{padding-top:100%;animation:magicSquare 10s linear infinite;@keyframes magicSquare{0{transform:rotate(0)}100%{transform:rotate(360deg)}}&::before,&::after{display:block;content:"";transition:all .3s 1.7s;box-sizing:border-box;position:absolute;top:50%;left:50%;border:1px solid #29b6f6;border-radius:50%}&::before{width:80%;margin-top:-40%;margin-left:-40%;padding-top:80%}&::after{width:74%;margin-top:-37%;margin-left:-37%;padding-top:74%}&__inner{position:absolute;top:0;left:0;width:100%;height:100%;&::before,&::after{box-sizing:border-box;position:absolute;left:25%;top:25%;width:50%;height:50%;border:1px solid #29b6f6;box-shadow:0 0 3px 0 #29b6f6,0 0 1px 0 #29b6f6 inset;transition:all .6s 2s;content:""}&::after{transform:rotate(45deg)}}%magic-square__txt{position:absolute;color:#29B6F6;font-size:14px;font-weight:bold;&::before,&::after{position:absolute;top:0;left:0}&::before{content:"ホ"}&::after{content:"ノ"}}&__txt{&--top{top:0;left:50%;transform:translateX(-50%);transition:all .2s .3s;@extend %magic-square__txt}&--top-right{top:12.5%;right:15%;transition:all .2s .5s;@extend %magic-square__txt}&--right{top:50%;right:1.5%;transform:translateY(-50%);transition:all .3s .7s;@extend %magic-square__txt}&--bottom-right{right:15%;bottom:12.5%;transition:all .3s .9s;@extend %magic-square__txt}&--bottom{bottom:0;left:50%;transform:translateX(-50%);transition:all .3s 1.1s;@extend %magic-square__txt}&--bottom-left{bottom:12.5%;left:15%;transition:all .3s 1.3s;@extend %magic-square__txt}&--left{top:50%;left:1.5%;transform:translateY(-50%);transition:all .3s 1.5s;@extend %magic-square__txt}&--top-left{top:12.5%;left:15%;transition:all .3s 1.7s;@extend %magic-square__txt}}}}
今回はポイントとなる箇所をSCSS記法でいくつか説明いたします。
レイアウト
基本的に装飾には疑似要素を使用して、位置調整は移動させたい要素にposition:absoluteを付与し、%指定で要素を動かします。
例えば、円の中にある星型の模様は以下のように疑似要素で作成しています。
&__inner {
...
&::before, &::after {
box-sizing: border-box;
position: absolute;
left: 25%;
top: 25%;
width: 50%;
height: 50%;
border: 1px solid #29B6F6;
box-shadow: 0 0 3px 0 #29B6F6, 0 0 1px 0 #29B6F6 inset;
transition: all 0.6s 2s;
content: "";
}
&::after {
transform: rotate(45deg);
}
また、周りの文字はそれぞれposition:ansoluteとtransformプロパティを使用して位置決めをしています。
&--top {
top: 0;
left: 50%;
transform: translateX(-50%);
transition: all 0.2s 0.3s;
@extend %magic-square__txt;
}
&--top-right {
top: 12.5%;
right: 15%;
transition: all 0.2s 0.5s;
@extend %magic-square__txt;
}
レスポンシブ化
様々な画面幅のデバイスが存在している中、絶対値で幅を指定するわけにはいきません。
この魔法陣はしっかりレスポンシブ対応していて、幅が200px以下になると1:1の比率を保ったまま縮んでいくようになっています。
実装方法はいたって簡単です。
下記のコードのように.magic-squareの子要素である.magic-square-contentにpadding-top:100%を付与するだけで1:1の比率を保ったままサイズを縮めることができます。
というのも、paddingとmarginの%指定は上下左右にかかわらず要素の横幅が基準になるという仕様があるので、要素の幅が小さくなるにつれてpaddingの値(縦幅)も小さくなり比率を保ってくれるのです。
.magic-square-content {
padding-top: 100%;
...
}
解読不能文字
HTML上にはミツエーリンクスと書かれているのに、実際に画面に描画されているのは解読不能な謎の文字になっていますが、実はこれもCSSで作成しています。
%magic-square__txt {
...
&::before, &::after {
position: absolute;
top: 0;
left: 0;
}
&::before {
content: "ホ";
}
&::after {
content: "ノ";
}
}
この文字を作成するには解読不能文字にしたい要素(今回の場合magic-square-content__txt--xx)の疑似要素にposition:absoluteをかけて要素同士を重ね、contnetプロパティに適当な文字を入れます。
ちなみに重ねる文字にカタカナを使うと文字をつぶすことなく、より魔法陣らしい文字を作成することができます。
要素を立体的に見せる
要素を立体的に見せるにはtransformプロパティのrotateXとperspectiveを使用します。
.magic-square {
...
transform: perspective(400px) rotateX(60deg);
...
上記のコードではtransformプロパティのrotateX(60deg)で要素を傾けて、perspective(400px)で遠近効果を出しています。 ちなみにperspective()の数値は低いほど遠近感が強くなり、逆に大きくなると遠近感が弱くなります。
アニメーション
この魔法陣には常時回転のアニメーションとホバー時のアニメーションがあります。
回転のアニメーションは.magic-square-contentにanimationプロパティを付与して動かしています。
.magic-square-content {
padding-top: 100%;
animation: magicSquare 10s linear infinite;
@keyframes magicSquare {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
...
なぜ魔法陣のルート要素である.magic-squareではなく子要素の.magic-square-contentにアニメーションを付与しているのかと疑問を持つ方もいらっしゃると思いますが、現在の幅よりも大きくなった時に、回転のアニメーションがあると要素が画面からはみ出してスクロールバーが出てしまう可能性があるからです。
そのため、子要素である.magic-square-contentに回転のアニメーションを付与し、その親要素である.magic-squareにoverflow:hiddenを付与することによって回転時にスクロールバーが出てしまう可能性を未然に防いでいるのです。
続いて、ホバー時のアニメーションについてです。
こちらに関しては基本的にはホバー時に各要素の色を変えているだけですが、各要素にtransition-delayを付与することによって、アニメーションに時間差をつけて魔法陣ならではの格好よさを表現しています。
テキスト部分を例に上げると、0.2sずつdelayの数値を増やしてアニメーションをずらしています。
&__txt {
&--top {
...
transition: all 0.2s 0.3s;
...
}
&--top-right {
...
transition: all 0.2s 0.5s;
...
}
...
以上が、CSSのポイント箇所です。
おわりに
いかがでしたか? 今回作成した魔法陣ですが、borderの色を一つ変えるだけでも雰囲気がグっと変わるので是非自分だけの魔法陣を作ってみてくださいね!
後編では魔法陣を使ったサイトの新しい表現方法をご紹介いたしますので、そちらもご期待ください。
それでは!