概要
ReactでWebアプリケーションを作成していると、値を操作するコントローラーが欲しくなるときがあります。
ちょっと動作を確認したいときに、LEVA・dat.GUIを使用すると簡単にコントローラーが実装できます。
追記(2021.10.28)
LEVAに関して、もう少し調査をして扱えるインターフェイスがdat.GUIと遜色ないとわかったので、改めてまとめました。
どちらがいいのか
インターフェイスの種類は、dat.GUIの方が充実してる印象です。
LEVAは、インターフェイスの種類では劣りますが、使い方がdat.GUIより簡単です。
使いたいインターフェイスの種類によって、パッケージを使い分けするのがいいと思います。
LEVA
ドキュメント
インストール
npm i leva
インターフェースの種類
コード
.tsx
import { rgba } from 'csx';
import { useControls } from 'leva';
import React, { useEffect, useRef, VFC } from 'react';
import { css } from '@emotion/css';
export const LevaController: VFC = () => {
const leftContainerRef = useRef<HTMLDivElement>(null)
const rightContainerRef = useRef<HTMLDivElement>(null)
const props = useControls({
LeftColor: '#C23841',
RightColor: { r: 61, g: 199, b: 190, a: 0.5 },
Text: 'hogehoge',
Toggle: true,
Number: {
value: 4,
min: 0,
max: 10,
step: 1
},
Interval: {
min: 0,
max: 10,
value: [4, 5]
},
Position: { x: 0, y: 0 },
BoxSize: {
value: [10, 20],
joystick: false
}
})
const colorToString = (color: { r: number; g: number; b: number; a: number }) => {
return rgba(color.r, color.g, color.b, color.a).toString()
}
useEffect(() => {
leftContainerRef.current!.style.backgroundColor = props.LeftColor
rightContainerRef.current!.style.backgroundColor = colorToString(props.RightColor)
}, [props])
return (
<div className={styles.container}>
<div ref={leftContainerRef} className={styles.leftContainer}></div>
<div ref={rightContainerRef} className={styles.rightContainer}></div>
</div>
)
}
const styles = {
container: css`
position: relative;
width: 100%;
height: 100%;
display: flex;
`,
leftContainer: css`
position: relative;
width: 50%;
height: 100%;
`,
rightContainer: css`
position: relative;
width: 50%;
height: 100%;
`
}
- useControlsを呼びだすことによって、自動的にコントローラーが表示されるようになります。
- Colorのインターフェイスで
透過
を扱いたい場合は、rgbaで初期値を指定します。
.tsx
RightColor: { r: 61, g: 199, b: 190, a: 0.5 }
コントローラー上ではHexColorとして表示されますが、受け取れる値(props.RightColor)は、rgbaのオブジェクトとなります。
外部からコントローラーの値を変更したい場合
dat.GUI
ドキュメント
インストール
npm i dat.gui
npm i -D @types/dat.gui
インターフェースの種類
コード
dat.GUIはReact用のパッケージではないので、すこしだけ実装量が増えます。
コードが長くなるのでカスタムフックにしています。
.tsx
import React, { useEffect, useRef, VFC } from 'react';
import { css } from '@emotion/css';
import { useDatGuiController } from './useDatGuiController';
export const DatGuiController: VFC = () => {
// コントローラーの作成
const { values } = useDatGuiController()
const containerRef = useRef<HTMLDivElement>(null)
useEffect(() => {
containerRef.current!.style.backgroundColor = values.colorHex
}, [values.colorHex])
return (
<div ref={containerRef} className={styles.container}>
<div
className={
styles.text
}>{`group x:${values.group.x}, y:${values.group.y}, z:${values.group.z}`}</div>
</div>
)
}
const styles = {
container: css`
position: relative;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
`,
text: css`
color: #fff;
font-size: 5rem;
`
}
useDatGuiController.tsx
import * as dat from 'dat.gui';
import { useEffect, useState } from 'react';
const props = {
colorHex: '#1e1e1e',
colorRGBA: [0, 128, 255, 0.3],
text: 'hoge',
number: 5,
check: true,
select: 'foo',
group: {
x: 0,
y: 0,
z: 0
}
}
export const useDatGuiController = () => {
const [values, setValues] = useState(props)
useEffect(() => {
const gui = new dat.GUI()
// object key name = prop name
gui.addColor(props, 'colorHex').onChange(e =>
setValues(prev => {
return { ...prev, colorHex: e }
})
)
gui.addColor(props, 'colorRGBA').onChange(e =>
setValues(prev => {
return { ...prev, colorRGBA: e }
})
)
gui.add(props, 'text').onChange(e =>
setValues(prev => {
return { ...prev, text: e }
})
)
gui.add(props, 'number', 0, 10, 0.1).onChange(e =>
setValues(prev => {
return { ...prev, number: e }
})
)
gui.add(props, 'check').onChange(e =>
setValues(prev => {
return { ...prev, check: e }
})
)
gui.add(props, 'select', ['foo', 'bar', 'hoge']).onChange(e =>
setValues(prev => {
return { ...prev, select: e }
})
)
gui.add(
{
button: () => console.log('clicked!')
},
'button'
)
// グループ化
const group = gui.addFolder('group')
group.add(props.group, 'x', -100, 100, 1).onChange(e =>
setValues(prev => {
return { ...prev, group: { ...prev.group, x: e } }
})
)
group.add(props.group, 'y', -100, 100, 1).onChange(e =>
setValues(prev => {
return { ...prev, group: { ...prev.group, y: e } }
})
)
group.add(props.group, 'z', -100, 100, 1).onChange(e =>
setValues(prev => {
return { ...prev, group: { ...prev.group, z: e } }
})
)
return () => {
group.destroy()
gui.destroy()
}
}, [])
return { values }
}
- **new dat.GUI()**をすることで、自動的にコントローラーが表示されるようになります。
- インスタンスを作成して、それぞれインターフェイスを追加していきます。
.tsx
const gui = new dat.GUI()
// object key name = prop name
gui.addColor(props, 'colorHex').onChange(e =>
setValues(prev => {
return { ...prev, colorHex: e }
})
)
- 色を扱う場合はaddColor、グループ化する場合はaddFolder、その他はadd関数を使用します。
- 第1引数にはObject(props)をとります。初期値になっています。第2引数には対応するkey名を指定します。
- Colorインターフェイスでは、
透過
も扱えますが、コントローラー上で値を変更することができませんでした。