LoginSignup
2
1

More than 1 year has passed since last update.

【React】値を操作するコントローラーパッケージ(LEVA・dat.GUI)

Last updated at Posted at 2021-10-25

概要

ReactでWebアプリケーションを作成していると、値を操作するコントローラーが欲しくなるときがあります。
ちょっと動作を確認したいときに、LEVA・dat.GUIを使用すると簡単にコントローラーが実装できます。

追記(2021.10.28) LEVAに関して、もう少し調査をして扱えるインターフェイスがdat.GUIと遜色ないとわかったので、改めてまとめました。

どちらがいいのか

インターフェイスの種類は、dat.GUIの方が充実してる印象です。
LEVAは、インターフェイスの種類では劣りますが、使い方がdat.GUIより簡単です。

使いたいインターフェイスの種類によって、パッケージを使い分けするのがいいと思います。

LEVA

ドキュメント

インストール

npm i leva

インターフェースの種類

スクリーンショット 2021-10-25 122839.png
※ インターフェイスの名前は自由に付けられます。

コード

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で初期値を指定します。
RightColor: { r: 61, g: 199, b: 190, a: 0.5 }

スクリーンショット 2021-10-25 123827.png

コントローラー上ではHexColorとして表示されますが、受け取れる値(props.RightColor)は、rgbaのオブジェクトとなります。

外部からコントローラーの値を変更したい場合

dat.GUI

ドキュメント

インストール

npm i dat.gui
npm i -D @types/dat.gui

インターフェースの種類

スクリーンショット 2021-10-25 124549.png
※ インターフェイスの名前は自由に付けられます。

コード

dat.GUIはReact用のパッケージではないので、すこしだけ実装量が増えます。
コードが長くなるのでカスタムフックにしています。

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()をすることで、自動的にコントローラーが表示されるようになります。
  • インスタンスを作成して、それぞれインターフェイスを追加していきます。
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インターフェイスでは、透過も扱えますが、コントローラー上で値を変更することができませんでした。
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1