Reactアプリの続きです。前回までは「ジャンプ率ジェネレーター」をNext.jsで作り直していました。今回は「アスペクト比ジェネレーター」に取り組みます。以前こちらでまとめた主要なアスペクト比による画像サイズのCSS数値を調べられるツールにります。それではいきましょう!
【目次】
※参考:前回記事
【React】ジャンプ率ジェネレーターをNext.jsで作り直した(Next.js + TypeScript + CSS in JS) - クモのようにコツコツと
※参考:ReactでWebアプリを作るシリーズまとめ
qiita.com
前回のおさらい
「ジャンプ率ジェネレーター」をNext.jsで作り直した
文字サイズを変えると下のサンプルの見た目が変わって、適切なサイズを検討することができるツール
※参考:前回記事
【React】ジャンプ率ジェネレーターをNext.jsで作り直した(Next.js + TypeScript + CSS in JS) - クモのようにコツコツと
主なアスペクト比
次は「アスペクト比ジェネレーター」を作りたい。画像のアスペクト比(縦横比率)を検討することができる。
こちらの記事にまとめた主要なアスペクト比で切り替える設定にする。
- スクエア(1:1)
- デジカメ4:3(1.333:1)
- 白銀比(1.414:1)
- デジカメ3:2(1.5:1)
- 黄金比(1.618:1)
- デジカメ16:9(1.777:1)
※参考:【黄金比、白銀比】代表的なアスペクト比(縦横比率)を一覧にしてみた - クモのようにコツコツと
また、画像を外接リサイズしたいので私の愛してやまないobject-fit: cover
も使う!これで画像のサイズが不揃いでもページ上の画像の見え方が統一されるのだ。
※参考:私が愛してやまない待望の外接リサイズobject-fitを使うのにIEのせいであと1年半も待ってらんないっ!!(→祝・IEサポート終了!) - クモのようにコツコツと
作ったもの
作ったものこちら「アスペクト比ジェネレーター」
aspect-ratio-generator.vercel.app
※参考:アスペクト比ジェネレーター
ソースコードはこちら
※参考:GitHub - ryo-i/aspect-ratio-generator
上の設定項目が下の画像に反映される。初期設定はスクエア(正方形)
「アスペクト比」を変更すると画像の縦横比率が変わる
「画像の向き」を変更すると画像の向きが変わる
「画像サイズ」を変更すると画像のサイズが変わる
「サイズ(幅)の刻み」を変えると画像サイズ変更の刻みが変わる
ページ設定
title、meta、OGP設定
アプリのベースは先日作ったNextスターターキットを使う。Next.js + CSS in JS + TypeScript環境。
※参考:【React】ReactでWebアプリを作るシリーズまとめ(随時更新) - Qiita
「/data/data.json」にurl、title、descriptionを設定
{ "head": { "url": "https://aspect-ratio-generator.vercel.app/" }, "header": { "title":"アスペクト比ジェネレーター", "text": "黄金比、白銀比、デジカメサイズなどの見え方とCSS設定値を調べることができます。" }, // 後略
「/pages/index.tsx」でデータを読み込む
import Data from '../data/data.json';
ヘッダーとページタイトルはデータから読み込むがページテキストはベタ打ち
const headerTitle = Data.header.title; const headerText = Data.header.text; const pageTitle = Data.header.title; const pageText = 'アスペクト比の設定を変更すると下の画像とCSS設定値が変化します';
画像をアップ
「/public」フォルダに画像をアップ。
※参考:aspect-ratio-generator/public at main · ryo-i/aspect-ratio-generator · GitHub
アップしたのは以前、アスペクト比の記事で用いた階段の画像(kaidan.jpg)
※参考:【黄金比、白銀比】代表的なアスペクト比(縦横比率)を一覧にしてみた - クモのようにコツコツと
この正方形(スクエア)な画像をアスペクト比によってトリミングする。
Innerコンポーネント構成
画像の初期値を設定する
先ほどの「data.json」でInnerコンポーネントで読み込む初期値を設定する
"inner": { "aspect": { "square": { "name": "スクエア", "ratio": 1 }, "silverRatio": { "name": "白銀比", "ratio": 1.414 }, "goldenRatio": { "name": "黄金比", "ratio": 1.618 }, "camera4_3": { "name": "デジカメ4:3", "ratio": 1.333 }, "camera3_2": { "name": "デジカメ3:2", "ratio": 1.5 }, "camera16_9": { "name": "デジカメ16:9", "ratio": 1.777 } }, "direction": { "horizontal": "横", "vertical": "縦" }, "step": 10, "size": 600, "max": 1000 },
Inner
キーの中にInnerコンポーネントの初期値を設定aspect
キーの中に各アスペクト比の項目名と比率を設定direction
キーの中に向きを設定step
にサイズ(幅)の刻みの初期値10
を設定size
に画像サイズの初期値600
を設定max
に画像サイズの最大値1000
を設定
ここのキー名を手がかりにページのタグや処理と紐づける。
max
はページ上の設定項目にはないがスマホなど画面サイズが狭い時に可変させる。
各種インポート
「/components/Inner.tsx」を修正する
冒頭で各種インポート
import React, { useState, useEffect } from 'react'; import styled from 'styled-components'; import Data from '../data/data.json';
フック(useState
とuseEffect
)を使うのでインポート
※参考:【React】フック(React Hooks)事始め:useState、useEffect、useContext - クモのようにコツコツと
styled-components
とdata.json
はこれまで通り
Innerコンポーネント定義
コンポーネントは関数コンポーネント
// Component function Inner() { // 中略(コンポーネント設定) } export default Inner;
Inner
コンポーネントを定義して、最後にエクスポートする。
フック設定
フックの初期値を設定
useState()
を使ってフックの初期値を設定していく
const data = Data.inner; const [aspectName, setAspectName] = useState(data.aspect.square.name); const [aspectRatio, setAspectRatio] = useState(data.aspect.square.ratio); const [direction, setDirection] = useState(data.direction.horizontal); const [step, setStep] = useState(data.step); const [width, setWidth] = useState(data.size); const [height, setHeight] = useState(data.size); const [maxSize, setMaxSize] = useState(data.max);
aspectName
にはスクエアの名前(aspect.square.name
)を設定aspectRatio
にはスクエアの比率(aspect.square.ratio
)を設定direction
には横向き(direction.horizontal
)を設定step
、width
、height
、maxSize
は初期値そのものなのでそのまま設定
画面幅によるサイズ変更
useEffect()
を使って画面幅に基づいたサイズ変更
useEffect(() => { const windowWidth = document.body.clientWidth; if (900 > windowWidth) { const resultSize = windowWidth - 120; setMaxSize(resultSize); setWidth(resultSize); setHeight(resultSize); } }, []);
- 変数
windowWidth
で画面サイズclientWidth
を取得 - もし
windowWidth
が900より少なかったら - 変数
resultSize
でwindowWidth
から120引いた数値を取得 setMaxSize()
、setWidth()
、setHeight()
にresultSize
をセット
画面サイズが900pxより狭い時だけ処理を実行する。
clientWidth
はスクロールバーを抜いた画面幅を取得できるプロパティ
※参考:Element.clientWidth - Web API | MDN
※参考:ウインドウサイズを取得する 【JavaScript 動的サンプル】
inputタグから数値を取得
独自フックuseGetNumber()
でinputから取得した値を数値(number
)として返す
const useGetNumber = (e: React.ChangeEvent<HTMLInputElement>) => { const getValue: number = Number(e.target.value); return getValue; };
独自フック参考。
※参考:【React】独自フックで関数を抽出、全てのツマミが動いた!(ジャンプ率ジェネレーター) - クモのようにコツコツと
画像の向きによる画像幅の計算
独自フックuseGetHeight()
で画像の向き変更によって画像幅を変更する。
const useGetHeight = (width: number, ratio: number, direction: string) => { if (direction === '横') { return Math.floor(width / ratio); } else if (direction === '縦') { return Math.floor(width * ratio); } };
- 独自フック
useGetHeight()
の引数はwidth
、ratio
、direction
- もし
direction
が「横」ならwidth
割るratio
の結果を返す - またはもし
direction
が「縦」ならwidth
掛けるratio
の結果を返す
計算結果は少数の桁数が多いことがあるので、Math.floor()
で少数を切り捨てする
※参考:【JS】Math.random()でつくるサイコロとおみくじ - クモのようにコツコツと
アスペクト比の変更
関数changeAspect()
でアスペクト比を変更
const changeAspect = (e: React.ChangeEvent<HTMLInputElement>) => { const getValue = e.target.value; const changeName = data.aspect[getValue].name; const changeRatio = data.aspect[getValue].ratio; const changeHeight = useGetHeight(width, changeRatio, direction); setAspectRatio(changeRatio); setAspectName(changeName); setHeight(changeHeight); };
changeDirection()
の引数はイベントターゲット(inputタグ)- 変数
getValue
でinputタグのの値を取得 - 変数
changeName
でaspect
のgetValue
のname
を取得 - 変数
changeRatio
でaspect
のgetValue
のratio
を取得 - 変数
changeHeight
でuseGetHeight()
実行、引数はwidth
,changeRatio
,direction
setAspectRatio()
でchangeRatio
をセットsetHeight()
でchangeHeight
をセット
オブジェクト(連想配列)の指定はドットで繋ぐのが楽だったが、今回のように変数名をキー名に代入したい場合は角カッコ[]
を使う書き方が有効だった!
※参考:JavaScriptのオブジェクトのキーに変数の値を使うTips - Qiita
useGetHeight()
の引数に今回取得したchangeRatio
を設定している
画像の向きを変更
関数changeDirection()
で画像の向きを変更
const changeDirection = (e: React.ChangeEvent<HTMLInputElement>) => { const getValue = e.target.value; const changeDirection = data.direction[getValue]; const changeHeight = useGetHeight(width, aspectRatio, changeDirection); setDirection(changeDirection); setHeight(changeHeight); };
- 変数
changeDirection
でdirection
のgetValue
を取得 - 変数
changeHeight
でuseGetHeight()
実行、引数はwidth
、aspectRatio
、changeDirection
setDirection()
でchangeDirection
をセットsetHeight()
でchangeHeight
をセット
changeAspect()
と似ているがuseGetHeight()
の引数に今回取得したchangeDirection
を設定している
画像サイズの変更
関数ChangeImageSize()
で画像サイズを変更
const ChangeImageSize = (e: React.ChangeEvent<HTMLInputElement>) => { const getValue = useGetNumber(e); const changeHeight = useGetHeight(getValue, aspectRatio, direction); setWidth(getValue); setHeight(changeHeight); };
- 変数
getValue
でuseGetNumber()
によってターゲットを数値として取得 - 変数
changeHeight
でuseGetHeight()
実行、引数はgetValue
、aspectRatio
、direction
setWidth()
でgetValue
をセットsetHeight()
でchangeHeight
をセット
useGetHeight()
の引数に今回取得したgetValue
を設定している
画像サイズ(幅)の刻み値を変更
関数changeStep()
で画像サイズを変更する際の幅の刻み値を変更する
const changeStep = (e: React.ChangeEvent<HTMLInputElement>) => { const getValue = useGetNumber(e); setStep(getValue); };
- 変数
getValue
でuseGetNumber()
によってターゲットを数値として取得 setStep()
でgetValue
をセット
画像サイズのCSS変更
変数getValue
の連想配列にwidth
とheight
を設定して画像サイズのインラインスタイルとして動的に変更
// Change CSS const imgStyle = { width: width + 'px', height: height + 'px' }
インラインスタイルについてはこちらを参照
※参考:【React】ジャンプ率ジェネレーターをNext.jsで作り直した(Next.js + TypeScript + CSS in JS) - クモのようにコツコツと
JSX設定
JSXの構造
JSXは下記の2パートにわかれる
// JSX return ( <div className="inner"> <Setting> // 設定変更箇所 </Setting> <Example> // 画像表示箇所 </Example> </div> );
Setting
コンポーネントに設定変更のinputタグ類があるExample
コンポーネントに変更結果を反映する画像などがある
Setting
とExample
にはStyled-componentsでCSS in JSを設定している。スタイル詳細は下記を参照。
※参考:aspect-ratio-generator/Inner.tsx at main · ryo-i/aspect-ratio-generator · GitHub
設定変更のJSX
Setting
コンポーネントに設定変更のinputタグ類を設定
<Setting> <dl> <dt> 主なアスペクト比 </dt> <dd> <label><input type="radio" name="aspect" value="square" onChange={changeAspect} defaultChecked />スクエア(1:1)</label> <label><input type="radio" name="aspect" value="silverRatio" onChange={changeAspect} />白銀比(1.414:1)</label> <label><input type="radio" name="aspect" value="goldenRatio" onChange={changeAspect} />黄金比(1.618:1)</label> <label><input type="radio" name="aspect" value="camera4_3" onChange={changeAspect} />デジカメ4:3(1.333:1)</label> <label><input type="radio" name="aspect" value="camera3_2" onChange={changeAspect} />デジカメ3:2(1.5:1)</label> <label><input type="radio" name="aspect" value="camera16_9" onChange={changeAspect} />デジカメ16:9(1.777:1)</label> </dd> <dt> 画像の向き </dt> <dd> <label><input type="radio" name="direction" value="horizontal" onChange={changeDirection} defaultChecked />横向き</label> <label><input type="radio" name="direction" value="vertical" onChange={changeDirection} />縦向き</label> </dd> <dt> 画像サイズ </dt> <dd> <input type="range" className="widthValue" min="10" max={maxSize} step={step} defaultValue={width} onChange={ChangeImageSize} /> <p>幅:{width}px、高さ:{height}px</p> </dd> <dt> サイズ(幅)の刻み </dt> <dd> <label><input type="radio" name="step" value="1" onChange={changeStep} />1px</label> <label><input type="radio" name="step" value="5" onChange={changeStep} />5px</label> <label><input type="radio" name="step" value="10" onChange={changeStep} defaultChecked />10px</label> </dd> </dl> </Setting>
- 「主なアスペクト比」のradioボタンで
changeAspect
を実行 - 「画像の向き」のradioボタンで
changeDirection
を実行 - 「画像サイズ」のrangeバーで
ChangeImageSize
を実行
max
属性でmaxSize
、step
属性でstep
を読み込み
テキスト「幅」でwidth
、「高さ」でheight
を読み込み - 「サイズ(幅)の刻み」のradioボタンで
changeStep
を実行
inputタグ、radioボタンの初期状態はdefaultChecked
、rangeバーの初期値はdefaultValue
で設定。
変更結果のJSX
Example
コンポーネントに変更結果を反映する画像などを設定
<Example> <section> <h2>{aspectName + "(1:" + aspectRatio + " " + direction + "向き)"}</h2> <figure><img src="/kaidan.jpg" style={imgStyle}/></figure> <p>CSS設定 {'{'} width: {width}px; height: {height}px; object-fit: cover; {'}'}</p> </section> </Example>
- h2タグで
aspectName
、aspectRatio
、direction
を読み込む - imgタグの
style
属性でimgStyle
を読み込む - pタグにCSSスタイルの
width
、height
を表示
pタグ波カッコ{}
をテキストとして表示したかったのだがJSXだとJSの式として認識されてしまった。preタグで囲ってもうまくいかない。。
調べたらシングルコーテーションで囲ってさらに波カッコで囲う{'{'}
とエスケープができた!こんな方法があったとは!多謝♪
※参考:React(JSX)で波括弧をエスケープしたい - Javaエンジニア、React+Redux+Firebaseでアプリを作る
最後に
といういうことで、ようやくReactアプリの2つ目が完成しました〜♪
一つ目がジャンプ率ジェネレーターで2つ目がアスペクト比ジェネレーター。だんだんとinputタグとフックを連携させる方法に慣れてきている感じがしてきています。
次もこちらのデザインまとめの中からジェネレーターを作ります。ちょっとこれまでよりも大きいテーマになるんですが、、HSBとRBGを変換する配色ツールにトライしてみたいと思います。
※参考:【デザインの基本】メリハリ、縦横比率、画面分割、タイポ、色相環、配色 まとめ - Qiita
HSBの補色は単純な180度の反対側ではないところに難しさを感じていますが、ぜひ補色や近似色を含んだ3色パレットの形で完成させたいと目論んでいます。
それではまた!
※参考:ReactでWebアプリを作るシリーズまとめ
qiita.com