見出し画像

SVG使うと素人でもデザインの幅が広がるよーって話

海外のお洒落なサイトとかみてると波線があったり、少し複雑な形のアイコンが浮かんでたり動いたり変形したり、普通png画像だと難しそうだしcssで書くのは記述量がめちゃくちゃ多くなりそう(ていうかcssでそんな複雑な形描けねーよ)。

で、どうやらSVGという拡張子の画像を使用してデザインしているようです。このSVG使ってみたら素人の知識でも色々出来そうで、学習コストもそこまで高くなさそうなので紹介したいと思います。ちなみにSVGを使うと素人でもhtmlとcssだけでここまで出来ちゃいます(有料のソフトは使っていません)。

なんか私のセンスの無さでオシャレとダサさが混在してますが、みなさんのセンスならもっとオシャレなものがいくらでも作れると思うのでやりたいデザインがあるんだけど技術的に悩んでいる人はこれをみて少しレベルアップしてみてください。

SVG

画像には大まかに2種類あります。ラスタ画像ベクタ画像です。
ラスタ画像はjpg, gif, pngといったみなさんがよく使っている画像の種類になります。これは画像内のドット(ピクセル)の一つ一つの色を指定することで画像を表現します。
対してベクタ画像は直線、曲線など図形を使って画像を表現します。今回扱うSVGはこのベクタ画像になります。

ラスタ画像はどんなに複雑な画像でもドットの数でデータサイズが決まります。なので、写真などはラスタ画像がよく使われます。しかし、拡大すると荒くなるデメリットがあります。一方、ベクタ画像は図形の集まりで画像を表現するので同じ画像のサイズでも複雑になればなるほどデータサイズが大きくなります。しかし、図形の集まりなので拡大しても荒くなることはありません。

つまりウェブサイトのロゴやアイコンのような図形の集まりで出来ているものに対してはSVGを使うと良いということです。
また、SVGはcssのようにカラーコードなどで色を指定することができるので読み込むページがタグごとに色を変えたりすることも出来ます。少し興味が出てきましたか?では早速SVGを作れるアプリを紹介したいと思います。

SVGソフト

有名どころでいけばやはりAdobeのIllustratorでしょう。しかし、Adobe製品は本業ではない素人の私たちが購入するには高すぎます。

そこで今回は私のオススメのフリーソフトを紹介します。それがVectornatorです。

このアプリケーションは元々iPad用に作られたアプリのようで、今はMacOS用にもPro版として出ています。私はあまり使いこなせてはいないですが無料で結構本格的なことができるみたいなので駆け出しのデザイナーさんにもオススメですし、本格的なことはやらないけど自分のサイトをもう少しお洒落にしたいなというエンジニアさんにもオススメです。

Windows対応はしていないと思うのでWindowsユーザーのみなさんには申し訳ないですがWindowsにはInkscapeという無料のソフトがあるみたいです。Macだとクラッシュして起動出来なかったので使用感はわかりませんが…

SVGを使ったサイトを作ってみる

ここからは、実際にサイトというかページを作ってみたいと思います。作るのは最初に紹介したページです。

SVG画像を作る

これがないと始まらないのでまずは画像を作りましょう。Vectornatorを起動してデザインしていってください。操作は普通のペイントソフトと変わりません。図形を自分の好きなように配置してデザインしていきます。

スクリーンショット 2020-09-09 10.52.34

写真なども扱えますが、前述した通り複雑になるとデータサイズが膨大になります。データサイズが大きくなるとユーザーがページを読み込む際の遅延が大きくなりUXを損なうことになるので、データサイズに気を付けながら扱うようにしてください。今回はロゴとフォルダのイラストを作成するので画像は使用しません。

デザインが完成したらSVGで書き出しましょう。

スクリーンショット 2020-09-09 10.52.25

書き出したらVS Codeなどのエディタでウェブページのフォルダを作成し、その中にSVGファイルを移動しましょう。今回はassetsフォルダの中に配置しています。

htmlファイルを作る

スクリーンショット 2020-09-09 13.01.34

このSVGファイルをエディタで開くとしたのようなHTMLのタグのような記述になっています。ベクタ画像は図形の集まりであり、SVGはそれをタグの中に記述することで画像を表現します。

スクリーンショット 2020-09-09 13.03.11

これをimgタグで読み込んでもいいですし、以下のようにそのままHTMLファイルの中にペーストしても使えます。

html

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Document</title>
   <link rel="stylesheet" href="styles.css" />
</head>
<body>
   <div class="head">
       <div class="icon">
           <svg height="100%" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="100%" xmlns:vectornator="http://vectornator.io" version="1.1" viewBox="0 0 1024 1024">
           <defs>
           <linearGradient y1="513.402" id="LinearGradient" x1="36.9601" y2="513.402" x2="782.824" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.718398 -0.0908642 0.102311 0.791352 189.746 125.298)">
           <stop stop-color="#0b83ff" offset="0"/>
           <stop stop-color="#9e00b2" offset="1"/>
           </linearGradient>
           <linearGradient y1="504.203" id="LinearGradient_2" x1="45.6562" y2="504.203" x2="791.531" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.34234 0 0 1.36018 -45.9668 -171.882)">
           <stop stop-color="#0b83ff" offset="0"/>
           <stop stop-color="#9e00b2" offset="1"/>
           </linearGradient>
           </defs>
           <g id="レイヤー 2" vectornator:layerName="レイヤー 2">
           <path d="M274.196+569.769L371.607+218.723L708.482+221.042L819.27+573.52L550.867+789.045L274.196+569.769Z" opacity="1" fill="url(#LinearGradient)"/>
           </g>
           <g id="レイヤー 1" vectornator:layerName="レイヤー 1">
           <path d="M277.831+12.5075L15.3195+585.357L471.841+1015.34L1016.54+708.284L896.608+88.5077L277.831+12.5075ZM585.982+93.0983L906.466+391.063L708.429+804.686L265.582+762.351L189.908+322.544L585.982+93.0983Z" opacity="1" fill="url(#LinearGradient_2)"/>
           </g>
           </svg>
       </div>
       <h1 class="title">恐怖のペンタゴン教会</h1>
   </div>
</body>
</html>

css

body {
   background: #b92b27;  /* fallback for old browsers */
   background: -webkit-linear-gradient(to right, #1565C0, #b92b27);  /* Chrome 10-25, Safari 5.1-6 */
   background: linear-gradient(to right, #1565C0, #b92b27); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
   display: flex;
   justify-content: center;
   align-items: center;
   flex-flow: column;
   height: 50vh;
   margin: 0;
   padding: 0;
}
/* icon */
.head {
   position: relative;
   width: 100%;
   display: flex;
   justify-content: center;
   align-items: center;
   flex-flow: column;
}
.icon {
   position: absolute;
   width: 200px;
   height: 200px;
}
.title {
   position: absolute;
   color: aliceblue;
   font-size: 64px;
}

配置を整えたりして色々記述していますが、iconクラスを付けているタグの部分です。親のdivに合わせてsvgも変形するので親要素をしっかり整えればOKです。今回はやっていませんがjavaScriptで操作に合わせた動きをつけることも出来ます。

スクリーンショット 2020-09-09 13.11.36

さらにコンテンツの部分はSVGでデザインしたちょっとお洒落なフォルダを背景にしてあげます。

html

<div class="outer">
       <div id="folder1" class="container">
           <div class="folder">
               <svg height="100%" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="100%" xmlns:vectornator="http://vectornator.io" version="1.1" viewBox="0 0 1024 768">
               <defs>
               <linearGradient y1="211.118" id="LinearGradient" x1="17.6823" y2="216.521" x2="1020.24" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.02156 0 0 1.87008 -1.02855 -12.9135)">
               <stop stop-color="#2389ff" offset="0.015811"/>
               <stop stop-color="#5100f5" offset="0.978237"/>
               </linearGradient>
               <radialGradient cx="514.55" r="500.704" id="RadialGradient" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 1 -7 12)" cy="383.29">
               <stop stop-color="#ffffff" offset="0"/>
               <stop stop-color="#bdbdbd" offset="1"/>
               </radialGradient>
               </defs>
               <g id="レイヤー 1 copy" vectornator:layerName="レイヤー 1 copy" visibility="hidden">
               <path d="M20.9763+56.147L204.534+56.147L511.782+56.147L763.441+56.147L1002.59+56.147C1008.23+56.147+1013.35+58.8048+1017.05+63.1019C1020.75+67.399+1023.04+73.3354+1023.04+79.8925L1023.04+412.329L1023.04+744.766C1023.04+751.323+1020.75+757.259+1017.05+761.556C1013.35+765.853+1008.23+768.511+1002.59+768.511L511.782+768.511L20.9763+768.511C15.3299+768.511+10.218+765.853+6.51768+761.556C2.81739+757.259+0.528712+751.323+0.528712+744.766L0.528712+412.329L0.528712+79.8925C0.528712+73.3354+2.81739+67.399+6.51768+63.1019C10.218+58.8048+15.3299+56.147+20.9763+56.147Z" opacity="1" fill="#f3ff00"/>
               </g>
               <g id="レイヤー 2" vectornator:layerName="レイヤー 2">
               <path d="M17.035+14.8417L1018.2+14.8417L1018.2+748.949L17.035+748.949L17.035+14.8417Z" opacity="1" fill="url(#LinearGradient)"/>
               <path d="M6.8465+27.9157C90.0143+27.9157+173.203+26.0745+256.35+27.9157C274.983+28.3284+185.466+28.2127+255.724+28.2661C325.981+28.3194+376.63+79.0681+410.544+98.8753C442.005+117.249+456.84+114.31+484.246+114.409C675.934+115.101+819.489+112.395+1008.25+112.322C1007.23+437.903+1007.23+437.903+1006.21+763.483C506.526+763.483+506.526+763.483+6.8465+763.483C6.8465+395.7+6.8465+395.7+6.8465+27.9157Z" opacity="1" fill="url(#RadialGradient)"/>
               </g>
               </svg>
           </div>
           <div class="tag">
               tag1
           </div>
           <div class="contents">
               <h1>title</h1>
               <p>this is article.this is article.this is article.this is article.this is article.this is article.</p>
           </div>
       </div>
       <div id="folder2" class="container">
           <div class="folder">
               // svg
           </div>
           <div class="tag">
               tag2
           </div>
           <div class="contents">
               <h1>title</h1>
               <p>this is article.this is article.this is article.this is article.this is article.this is article.</p>
           </div>
       </div>
       <div id="folder3" class="container">
           <div class="folder">
               // svg
           </div>
           <div class="tag">
               tag3
           </div>
           <div class="contents">
               <h1>title</h1>
               <p>this is article.this is article.this is article.this is article.this is article.this is article.</p>
           </div>
       </div>
       <div id="folder4" class="container">
           <div class="folder">
               // svg
           </div>
           <div class="tag">
               tag4
           </div>
           <div class="contents">
               <h1>title</h1>
               <p>this is article.this is article.this is article.this is article.this is article.this is article.</p>
           </div>
       </div>
       <div id="folder5" class="container">
           <div class="folder">
               // svg
           </div>
           <div class="tag">
               tag5
           </div>
           <div class="contents">
               <h1>title</h1>
               <p>this is article.this is article.this is article.this is article.this is article.this is article.</p>
           </div>
       </div>
</div>

css

/* folder */
.outer {
   position: relative;
   top: 0%;
   width: 765px;
}
.container {
   position: absolute;
   top: calc(300px * 0.5); 
   width: 300px;
   height: calc(300px * 0.75);
   transform: rotate3d(2, 20, 0, 50deg);
   opacity: 0.8;
   transition: all 0.3s ease-in;
}
.container:hover {
   top: 0;
   transform: rotate3d(0, 0, 0, 50deg);
   opacity: 1;
   transition: all 0.3s ease-in;
}
.folder {
   position: absolute;
   width: 100%;
   top: 0;
   left:0;
   filter: drop-shadow(0px 0px 5px #555555);
}
.tag {
   position: relative;
   padding: 0.5rem 1rem;
   color: #000000;
}
.contents {
   position: relative;
   padding: 0 1rem;
   color: #000000;
}
#folder1 {
   left: 0;
   z-index: 5;
}
#folder2 {
   left: 15%;
   z-index: 4;
}
#folder3 {
   left: 30%;
   z-index: 3;
}
#folder4 {
   left: 45%;
   z-index: 2;
}
#folder5 {
   left: 60%;
   z-index: 1;
}

背景と言いましたが、background-imageに設定するのではなく、divタグをposition: abusoluteでコンテンツの後ろに配置しています。hoverを設定して動きを付けています。

スクリーンショット 2020-09-09 13.24.39

どうでしたか?日頃html, cssを触られている方からするとすごく簡単だったのではないでしょうか?これを期にSVGにも手を出してみてはいかがでしょうか?

Reactでロゴコンポーネントを作る。

ついでなのでReactでSVGを使ったコンポーネントを作ろうかなと思います。というか最初はReactで作っていました。

Logo.jsx

import React from "react";
export const Logo = ({ size, color, rotate, shadowColor }) => {
   return (
       <div style={{ 
           width: `${size}px`, 
           height: `${size}px`, 
           transform: `rotate(${rotate}deg)`,
           filter: `drop-shadow(0px 0px 2px ${ shadowColor || "rgba(0, 0, 0, 0)" })`,
       }}>
           // svg
       </div>
   );
}

Logo.tsx

import React from "react";
export const Logo:React.FC<{
   size: number,
   color: string,
   rotate: number,
   shadowColor?: string,
}> = ({ size, color, rotate, shadowColor }) => {
   return (
       <div style={{ 
           width: `${size}px`, 
           height: `${size}px`, 
           transform: `rotate(${rotate}deg)`,
           filter: `drop-shadow(0px 0px 2px ${ shadowColor || "rgba(0, 0, 0, 0)" })`,
       }}>
           // svg
       </div>
   );
}

svgと書いているところに任意のSVGを挿入してください。そしてこれを次のように別のコンポーネントから読み込んでください。

import { Logo } from "./logo";
import Styles from "../styles/header.module.css";
export default function Header() {
   return (
       <>
           <header className={ Styles.container }>
               <div>
                   <div>
                       <Logo 
                           size={ 50 }
                           color={ "#ffffff" }
                           rotate={ 0 }
                           shadowColor={ "#fff" }
                       />
                   </div>
                   <h1>bookmark-manager(仮)</h1>
               </div>
           </header>
       </>
   );
}

プロップスを渡すことでいくつか設定を与えることが出来ます。
sizeはそのなの通り大きさを指定します。SVG画像の縦横のアスペクト比1:1を想定して作っているので違う場合はLogoコンポーネントの方を書き換えてください。
colorはロゴの色を指定します。カラーコードで渡すことを想定して作っています。
rotateは画像を回転させる角度です。数を入れてください。マイナスを与えることで逆回転も可能です。
shadowColorは影の色を指定します。これは入力しなくても大丈夫です。入力しなかった場合は影はつきましせん。

実行するとこんな感じです。

スクリーンショット 2020-09-09 14.28.25

Logoコンポーネントに子要素としてSVGを渡すのもアリかもしれません。

この記事が気に入ったらサポートをしてみませんか?