Nextjs,TypeScriptで3Dを表示 react-three-fiber
Nextjs,TypeScript を用いて react-three-fiber の
公式にあるサンプルコードを細かく解説していきます!
デモサイトです
デモサイト
準備
まずは環境構築をします。
// npmの場合
$ npx create-next-app --ts
// yarnの場合
$ yarn create next-app --typescript
続いてパッケージをインストールします。
// npmの場合
$ npm install three @types/three @react-three/fiber @types/three
// yarnの場合
$ yarn add three @types/three @react-three/fiber @types/three
実装
必要なパッケージをインストールしたので早速コードを書いていきます。
import type { NextPage } from "next";
import React, { useRef, useState } from "react";
import * as THREE from "three";
import { Canvas, useFrame } from "@react-three/fiber";
const Home: NextPage = () => {
const Box = (props: JSX.IntrinsicElements["mesh"]) => {
const ref = useRef<THREE.Mesh>(null!);
const [hovered, setHover] = useState(false);
const [active, setActive] = useState(false);
useFrame((state, delta) => (ref.current.rotation.x += 0.01));
return (
<mesh
{...props}
ref={ref}
scale={active ? 1.5 : 1}
onClick={(event) => setActive(!active)}
onPointerOver={(event) => setHover(true)}
onPointerOut={(event) => setHover(false)}
>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color={hovered ? "hotpink" : "orange"} />
</mesh>
);
};
return (
<Canvas
style={{
width: 100 + "vw",
height: 100 + "vh",
position: "fixed",
top: 0,
left: 0,
}}
>
<ambientLight />
<pointLight position={[10, 10, 10]} />
<Box position={[-1.2, 0, 0]} />
<Box position={[1.2, 0, 0]} />
</Canvas>
);
};
export default Home;
解説
細かく解説していきます。
const Box = (props: JSX.IntrinsicElements["mesh"]) => {
Box という関数コンポーネントを定義しています。
引数に props を指定し、
JSX で使用可能な HTML 要素の一覧を定義できる
IntrinsicElements
を指定し、
Three.js のMesh
クラスに対する型<mesh>
を指定しています。
これにより<mesh>
要素のプロパティを安全に扱えるようにしています。
const ref = useRef<THREE.Mesh>(null!);
そもそもuseRef()
とは
DOM 要素に直接アクセスするためのフックで
再レンダリングを発生させずに、値を保持することができます。
型引数として<THREE.Mesh>
を指定し、
Mesh オブジェクトであることを明示しています。
最後のnull!
は非 null アサーション演算子として
null である可能性がある値を!
を使って
null でないことを示すために使用しています。
const [hovered, setHover] = useState(false);
const [active, setActive] = useState(false);
上記 2 つはuseState
フックを用いて状態を管理しています
1 つ目は hovered という状態変数を宣言して初期値は false で
ユーザーがマウスホバーしたときに true に変わりマウスを
外したときに false に変わります。
2 つ目は active という状態変数で初期値は同じく false
こちらはユーザーがマウスをクリックしている間は
true に変更され、クリックをやめたとき false になる状態変数です。
useFrame((state, delta) => (ref.current.rotation.x += 0.01));
react-three/fiber のuseFrame()
フックを使って
Three.js のrequestAnimationFrame
ループにアクセスし、
アニメーションをループ出来るようになります。
第一引数はstate
と呼ばれ、その名の通り現在の Three.js の
状態を表すオブジェクトです。
第二引数はdelta
と呼ばれ、経過時間を表します。
先程指定したref
に対して毎フレーム X 軸に 0.01 ラジアン分回転
させるアニメーションを意味しています。
return (
<mesh
{...props}
ref={ref}
scale={active ? 1.5 : 1}
onClick={(event) => setActive(!active)}
onPointerOver={(event) => setHover(true)}
onPointerOut={(event) => setHover(false)}
>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color={hovered ? "hotpink" : "orange"} />
</mesh>
);
立方体を描写するコンポーネントを書いています。
<mesh>
コンポーネントは 3D オブジェクトをレンダリングするために
使用され、props として、位置、回転、スケール、ジオメトリ、マテリアル、
ライト、カメラなどが渡せます。
{...props}
は先程の渡せる要素をまとめて props として
渡せるように記入しています。
このコードがない場合後ほど出てきますが、
<Box position={[-1.2, 0, 0]} />
この position の部分を mesh に props として渡さなくては
いけないですし、BOX 要素になにか付け足すたびに
props として渡さなくてはいけなくなります。
scale={}
部分は active の時大きさを 1.5 倍にします。
次のonClick={(event) => setActive(!active)}
で active かどうかを指定しています。
要はマウスでクリックしている間は 1.5 倍になり、
クリックをやめると元のサイズに戻るということです。
onPointerOver={(event) => setHover(true)}
onPointerOut={(event) => setHover(false)}
はマウスをホバーしたときに状態変数の setHover を
true もしくは false に変わるようにしています。
<boxGeometry args={[1, 1, 1]} />
このコードは Thre.js の立方体のジオメトリを定義するコードです。
args
で立方体の幅、高さ、深さを指定しています。
<meshStandardMaterial color={hovered ? "hotpink" : "orange"} />
meshStandardMaterial
は Three.js で提供されている標準的なマテリアルの一種です。
ここではホバーするたびに色が変わるように指定されています。
return (
<Canvas
style={{
width: 100 + "vw",
height: 100 + "vh",
position: "fixed",
top: 0,
left: 0,
}}
>
<ambientLight />
<pointLight position={[10, 10, 10]} />
<Box position={[-1.2, 0, 0]} />
<Box position={[1.2, 0, 0]} />
</Canvas>
);
<Canvas
style={{
width: 100 + "vw",
height: 100 + "vh",
position: "fixed",
top: 0,
left: 0,
}}
>
<Canvas>
は 3D シーンを描画するためのコンポーネントで
CSS を用いてキャンバスの高さ、横幅を指定しています。
<ambientLight />
<pointLight position={[10, 10, 10]} />
<ambientLight>
は 3D シーンに影の明るさを決定する
環境光を提供しています。
このコンポートを追加することにより、オブジェクトが
より自然な見た目になります。
<pointLight position={[10, 10, 10]} />
は
位置(10,10,10,)に物体の立体感を表現するポイントライト
を定義しています。
<Box position={[-1.2, 0, 0]} />
<Box position={[1.2, 0, 0]} />
先程定義した Box の関数コンポーネントに
position
プロパティを指定しています。
第一引数は X 軸、第二引数は Y 軸、
第三引数は X 軸を指定できます。
2つの Box がかぶらないように
X 軸にそれぞれ-1.2
,1.2
を指定しています。
さいごに
おつかれさまでした!
WebGL はとても難しいですが、書けるようになったら
ブラウザ向けのウェブコンテンツの可能性が
広がるので、もっと勉強していきます。
Discussion