前提条件
ワークショップにあたって必要な知識
- JavaScript(ES6)の基本的な構文
- HTML/CSSの基本的な構文
はじめに
本ワークショップのゴール
最終的な成果物
指定した色の補色と、その色の組み合わせを利用したUIのプレビューが見れるアプリケーション
使用ツール / ライブラリ
ツール/ライブラリ名 | 説明 |
---|---|
CodeSandbox | Web上で利用できるブラウザIDE(統合開発環境) |
React | フロントエンドライブラリ |
Chakra UI | Reactで利用できるUIコンポーネントライブラリ |
補色のカラーコードを教えてくれるアプリを作ろう!
補色とは
混合して無彩色を作れる2色の有彩色の組み合わせを互いに補色(ほしょく、英: complementary color[1])であるという
補色同士の色の組み合わせは、互いの色を引き立て合う相乗効果があり、これは「補色調和」といわれる[3]。
『フリー百科事典 ウィキペディア日本語版』
本セクションで実装するもの
Reactプロジェクトの作成
テキストを書き換えてみる
-
App.jsというファイルにある
<h1>
タグや<h2>
タグで囲まれたテキストを好きな言葉に変更する
import "./styles.css";
export default function App() {
return (
<div className="App">
{/* 「Hello CodeSandbox」を書き換える */}
<h1>好きな言葉でいいよ!</h1>
{/* 「Start editing to see some magic happen!」を書き換える */}
<h2>これから補色調べっ子ちゃんを作るよ!</h2>
</div>
);
}
入力フォームを作る
色を入力してもらうための入力フォームをつくる
-
<input>
タグを書くApp.jsimport "./styles.css"; export default function App() { return ( <div className="App"> <h1>好きな言葉でいいよ!</h1> <h2>これから補色調べっ子ちゃんを作るよ!</h2> {/* inputを追加する type属性はcolorを選択する */} <input type="color" /> </div> ); }
-
ユーザーが入力したデータを取得し、状態を保存する
Reactで画面の状態を管理するためのuseStateフックを用いる
useState
は引数に任意の値を入れることで、
その値で初期化した変数と、その変数を更新するための関数をペアで取得することができる今回は返却されたペアの名前を、それぞれcolor, setColorとし、
ユーザーが入力した色情報を保存するために、setColor
をinput要素のonChange属性に渡すApp.jsimport { useState } from "react"; import "./styles.css"; export default function App() { /* 状態を管理するための、値と更新用の関数をペアで取得する */ const [color, setColor] = useState(""); return ( <div className="App"> <h1>好きな言葉でいいよ!</h1> <h2>これから補色調べっ子ちゃんを作るよ!</h2> {/* 入力フォームの値が変わった時に、その値を保存する */} <input type="color" onChange={(event) => setColor(event.target.value)} /> </div> ); }
保存した色を表示してみる
-
変数colorを
<h2>
タグで表示してみるJSX記法(以下コードのHTMLを書いている部分)では、
ブラケット({ })で囲むことにより、JavaScriptを書くことができるので、
変数colorを{ }で囲み、<h2>
タグ内に展開するApp.jsimport { useState } from "react"; import "./styles.css"; export default function App() { const [color, setColor] = useState(""); return ( <div className="App"> <h1>好きな言葉でいいよ!</h1> <h2>これから補色調べっ子ちゃんを作るよ!</h2> {/* 変数colorを展開し、保存した色情報を表示する */} <h2>選択中の色: {color}</h2> <input type="color" onChange={(event) => setColor(event.target.value)} /> </div> ); }
保存した色情報から補色を計算し表示する
-
新しくutils.jsというファイルを作成し、以下の補色を計算する関数をコピペする
(ネットから拾ってきたものなので他の箇所と書き方が異なりますがご容赦ください)utils.jsexport const hexToComplimentary = (hex) => { // Convert hex to rgb // Credit to Denis http://stackoverflow.com/a/36253499/4939630 var rgb = "rgb(" + (hex = hex.replace("#", "")) .match(new RegExp("(.{" + hex.length / 3 + "})", "g")) .map(function (l) { return parseInt(hex.length % 2 ? l + l : l, 16); }) .join(",") + ")"; // Get array of RGB values rgb = rgb.replace(/[^\d,]/g, "").split(","); var r = rgb[0], g = rgb[1], b = rgb[2]; // Convert RGB to HSL // Adapted from answer by 0x000f http://stackoverflow.com/a/34946092/4939630 r /= 255.0; g /= 255.0; b /= 255.0; var max = Math.max(r, g, b); var min = Math.min(r, g, b); var h, s, l = (max + min) / 2.0; if (max == min) { h = s = 0; //achromatic } else { var d = max - min; s = l > 0.5 ? d / (2.0 - max - min) : d / (max + min); if (max == r && g >= b) { h = (1.0472 * (g - b)) / d; } else if (max == r && g < b) { h = (1.0472 * (g - b)) / d + 6.2832; } else if (max == g) { h = (1.0472 * (b - r)) / d + 2.0944; } else if (max == b) { h = (1.0472 * (r - g)) / d + 4.1888; } } h = (h / 6.2832) * 360.0 + 0; // Shift hue to opposite side of wheel and convert to [0-1] value h += 180; if (h > 360) { h -= 360; } h /= 360; // Convert h s and l values into r g and b values // Adapted from answer by Mohsen http://stackoverflow.com/a/9493060/4939630 if (s === 0) { r = g = b = l; // achromatic } else { var hue2rgb = function hue2rgb(p, q, t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; return p; }; var q = l < 0.5 ? l * (1 + s) : l + s - l * s; var p = 2 * l - q; r = hue2rgb(p, q, h + 1 / 3); g = hue2rgb(p, q, h); b = hue2rgb(p, q, h - 1 / 3); } r = Math.round(r * 255); g = Math.round(g * 255); b = Math.round(b * 255); // Convert r b and g values to hex rgb = b | (g << 8) | (r << 16); return "#" + (0x1000000 | rgb).toString(16).substring(1); }
-
- で作成した関数をApp.jsで読み込み、先ほどの入力フォームから取得した値から補色を計算して表示する
App.jsimport { useState } from "react"; import "./styles.css"; /* さっき作ったutils.jsの関数を読み込む */ import { hexToComplimentary } from "./utils"; export default function App() { const [color, setColor] = useState(""); return ( <div className="App"> <h1>好きな言葉でいいよ!</h1> <h2>これから補色調べっ子ちゃんを作るよ!</h2> <h2>選択中の色: {color}</h2> {/* 補色情報を表示する */} <h2>補色: {hexToComplimentary(color)}</h2> <input type="color" onChange={(event) => setColor(event.target.value)} /> </div> ); }
コンポーネントを分ける
Reactを含むコンポーネント指向のフロントエンドライブラリでは、
作った要素をまとまった一つの単位(コンポーネント)として分割することができる
コンポーネントを分割することで、同じデザインや機能を持ったUIを使い回すことができるうえ、
元のファイルを変更すれば、使いまわした箇所全てに適用されるためメンテナンスも容易になる
今回は、「色の入力フォーム」と「結果の表示部分」を別のコンポーネントに切り出してみる
-
新しくColorInput.jsというファイルを作成し、App.jsの以下の行を切り取って移動する
(この時点ではエラーになって大丈夫、画像左のような黄や赤の波線が出ている状態でOK)ColorInput.js<input type="color" onChange={(event) => setColor(event.target.value)} />
-
Reactのコンポーネントを作成する
(この時点ではエラーになって大丈夫)コンポーネントはJSXを返却する関数なので、ColorInput.jsを以下のように変更する
Reactのコンポーネントは頭文字を大文字にするというルールがあるので、関数名はColorInputとする
ついでに、ラベルも表示しておくColorInput.jsexport const ColorInput = () => { return ( <div> <label>色を選択してください</label> <input type="color" onChange={(event) => setColor(event.target.value)} /> </div> ) }
-
setColorを受け取れるようにする
(この時点ではエラーになって大丈夫)Reactのコンポーネントでは、コンポーネント外からの値を
props(propertiesの略)と呼ばれる変数を利用して受け取ることができる
今回、setColorはこのファイルには存在せず、App.jsに存在しているので、その値を受け取る必要がある
外部から受け取った値は、propsの中に自動的に格納されるColorInput.js/* 外部からの値をpropsという変数で受け取れる */ export const ColorInput = (props) => { /* propsからsetColorを取り出す */ const setColor = props.setColor return ( <div> <label>色を選択してください</label> <input type="color" onChange={(event) => setColor(event.target.value)} /> </div> ) }
-
ColorInput.jsで作ったColorInputコンポーネントをApp.jsで使用する
作成したコンポーネントは他のHTML要素のように使用することができる
propsとして渡したい値は、他の属性と同じように渡すことができるApp.jsimport { useState } from "react"; /* さっき作ったColorInput.jsのColorInputコンポーネントを読み込む */ import { ColorInput } from "./ColorInput"; import "./styles.css"; import { hexToComplimentary } from "./utils"; export default function App() { const [color, setColor] = useState(""); return ( <div className="App"> <h1>好きな言葉でいいよ!</h1> <h2>これから補色調べっ子ちゃんを作るよ!</h2> <h2>選択中の色: {color}</h2> <h2>補色: {hexToComplimentary(color)}</h2> {/* 作ったコンポーネントはHTML要素のように使用することができる */} {/* setColorを渡す */} <ColorInput setColor={setColor} /> </div> ); }
-
同様に、新しくResult.jsファイルを作り、App.jsの14, 15行目をResultコンポーネントとして切り出す
注意する点として、コンポーネントでは返却するJSXは必ず1つの要素になっていなければいけない
そこで、14, 15行目をそのまま返却すると2つ要素になってしまうので、
React.Fragmentと呼ばれる空のタグ<></>
で囲む
divタグで囲むこともできるが、このFragmentであればHTMLタグとしては判定されないので、
無駄なタグを作らずに済み、パフォーマンスが良くなるResult.js/* utils.jsの関数を読み込む */ import { hexToComplimentary } from "./utils" export const Result = (props) => { /* 外から変数colorを受け取る */ const color = props.color return ( /* 一つの要素しか返却できないので<></>で囲み一つにする */ <> <h2>選択中の色: {color}</h2> <h2>補色: {hexToComplimentary(color)}</h2> </> ) }
App.js(ついでに不要な行を削除しています)import { useState } from "react"; import { ColorInput } from "./ColorInput"; /* さっき作ったResult.jsのResultコンポーネントを読み込む */ import { Result } from "./Result"; import "./styles.css"; export default function App() { const [color, setColor] = useState(""); return ( <div className="App"> {/* 結果の表示部分をResultに置き変える */} <Result color={color} /> <ColorInput setColor={setColor} /> </div> ); }
UIコンポーネントライブラリを利用してオシャレにしよう!
本セクションで実装するもの
Chakra UI(UIコンポーネントライブラリ)をインストールする
-
Chakra UI の Getting Started にアクセスする
-
Getting Startedの「Installation」にある、以下のコマンドの
npm i
以降に書かれた各パッケージ名を、
CodeSandboxの「Dependencies」というエリアの入力フォームに入力して検索し、
表示されるパッケージ名を押下する
4つすべてインストールし、以下のように表示されればOK
-
Getting Startedの「Set up Provider」を参考に、以下のようにindex.jsでChakraProviderをimportし、
<App />
を<ChakraProvider></ChakraProvider>
で囲むindex.jsimport { StrictMode } from "react"; import ReactDOM from "react-dom"; import App from "./App"; /* ChakraProviderを読み込む */ import { ChakraProvider } from "@chakra-ui/react"; const rootElement = document.getElementById("root"); ReactDOM.render( <StrictMode> {/* ChakraProviderでAppを囲む */} <ChakraProvider> <App /> </ChakraProvider> </StrictMode>, rootElement );
Chakra UIのコンポーネントを表示してみる
Chakra UIを含むUIコンポーネントライブラリを利用すると、既に完成したコンポーネントを利用することができる
UIコンポーネントライブラリを使用することで、CSSなどのデザインや凝ったUIのJSの実装を省けるうえ、統一されたデザインのアプリケーションを簡単に作ることができる
Chakra UIのドキュメントを参考に、ボタンを表示してみる
-
Result.jsにChakra UIのボタンを追加する
Result.jsimport { hexToComplimentary } from "./utils" /* ドキュメントを参考に、importする */ import { Button } from "@chakra-ui/react" export const Result = (props) => { const color = props.color return ( <> <h2>選択中の色: {color}</h2> <h2>補色: {hexToComplimentary(color)}</h2> <Button colorScheme="blue">Button</Button> </> ) }
Chakra UIのコンポーネントにpropsを渡してみる
UIコンポーネントライブラリのドキュメントには、そのコンポーネントで利用できるpropsの情報が記載されていることが多い
Chakra UIのドキュメントにもコンポーネントのpropsについて記載がある
-
isLoadingというpropsを渡すとともに、colorSchemeというpropsの値を変更してみる
Result.jsimport { hexToComplimentary } from "./utils"; import { Button } from "@chakra-ui/react"; export const Result = (props) => { const color = props.color; return ( <> <h2>選択中の色: {color}</h2> <h2>補色: {hexToComplimentary(color)}</h2> {/* isLoadingを追加 colorSchemeの値をblueからorangeに変更 */} <Button colorScheme="orange" isLoading={true} >Button</Button> </> ); };
以下のようにブラウザに表示されればOK
isLoadingにtrue
を入れたので、ローディングスピナー(ぐるぐる)が表示され、
colorSchemeをorange
に変更したので、ボタンの色がオレンジになったことがわかる
補色を利用したデザインのプレビューを表示する
Chakra UIのコンポーネントには、色を簡単に指定できるpropsが用意されているため、
それを利用して指定した補色を使った場合のUIのプレビューを表示してみる
-
Result.jsのButtonのpropsを変更し、ユーザーが指定した色とその補色を渡す
colorというpropsでメインの色、bgというpropsで背景色を指定することができるResult.jsimport { hexToComplimentary } from "./utils"; import { Button } from "@chakra-ui/react"; export const Result = (props) => { const color = props.color; return ( <> <h2>選択中の色: {color}</h2> <h2>補色: {hexToComplimentary(color)}</h2> {/* 文字色を指定するcolorにユーザーが選択したcolorを 背景色を指定するbgに補色を入れる */} <Button color={color} bg={hexToComplimentary(color)}> Button </Button> </> ); };
-
Chakra UIのボタン以外のコンポーネントも表示してみる
基本的にはドキュメントからコピペするだけで、コンポーネントを使うことができる
また、CSS in JSという記法でCSSをJSの中に書くこともできるResult.jsimport { hexToComplimentary } from "./utils"; /* 使いたいコンポーネントをimportする */ import { Box, Button, Circle, Container, Radio, RadioGroup, Slider, SliderFilledTrack, SliderThumb, SliderTrack, Square, Stack, Text } from "@chakra-ui/react"; export const Result = (props) => { const color = props.color; // 補色を変数に格納する const complimentaryColor = hexToComplimentary(color); return ( <> <h2>選択中の色: {color}</h2> <h2>補色: {complimentaryColor}</h2> {/* 外枠のコンポーネント */} <Container borderColor={color} style={{ borderWidth: 1, borderColor: color, borderRadius: 20, padding: 20 }}> {/* ボタンのコンポーネント */} <Button color={color} bg={complimentaryColor}> Button </Button> {/* スライダーのコンポーネント */} <Slider aria-label="slider-ex-4" defaultValue={30}> <SliderTrack bg="gray.200"> <SliderFilledTrack bg={color} /> </SliderTrack> <SliderThumb boxSize={6}> <Box color={complimentaryColor} /> </SliderThumb> </Slider> {/* ラジオボタンのコンポーネント */} <RadioGroup> <Stack direction="row"> <Radio value="1" bg={complimentaryColor}><Text color={color}>First</Text></Radio> <Radio value="2" bg={complimentaryColor}><Text color={color}>Second</Text></Radio> <Radio value="3" bg={complimentaryColor}><Text color={color}>Third</Text></Radio> </Stack> </RadioGroup> {/* 図形のコンポーネント */} <Circle size="40px" bg={color}> <Square size="20px" bg={complimentaryColor} /> </Circle> <Square size="40px" bg={complimentaryColor}> <Circle size="20px" bg={color} /> </Square> </Container> </> ); };
おわりに
Reactの機能は意外と多くありません。
今回紹介したpropsの受け渡しや、Hooksを利用した状態管理が主な機能です。
なので、実際にReactでアプリケーションを開発する場合には、
ページ遷移を行うためのライブラリや、
APIリクエストをするためのライブラリ、
プログラムのテストを行うライブラリなどを組み合わせることになります。
それぞれの役割を分けて考えると、Reactの開発は理解しやすくなると思います。
この記事が少しでも誰かのお役に立てると幸いです。
間違いを見つけた場合や、もっとこうするとわかりやすいなどのご意見があれば、
ぜひコメントしていただけると嬉しいです。