react-three-fiber でアイコンをクルクルする
したいよね?
なので、cubeGeometry に png画像を貼り付けてグルグルさせてみました。
準備
Typescript 版の React プロジェクト作成
react-three/fiber のインストール
% npx create-react-app cube-icon
% cd cube-icon
% npm install three @react-three/fiber
アイコン用ファイル( icon.png )を cube-icon/public
以下に配置します。
今回は以下の画像ファイルを使用しました。
大人気 AppleWatch アプリの 男は読経!女も読経! 用に作ったアイコンです。
アートですねぇ...
コード
App.js, App.css の2ファイル、Index.jsは省略
App.css
この設定がないと表示が小さくなってしまう
.App {
width: 100vw;
height: 100vh;
}
App.js
function IconBoard
内で 1x1x0.05の薄い板を作って
マテリアルのマップにテキスチャを貼り付けてます。
IconBoard
の引数でテキスチャとポジションを指定できるようにしてます。
useFrame()
内に
マウスが mesh
の上に乗るとアイコンが捻り回転、離れると戻る。
クリックされると縦回転、一回転すると止まる。
の処理を記述。
import { useState, useRef } from 'react';
import { Canvas, useFrame } from '@react-three/fiber';
import { TextureLoader } from 'three';
import './App.css';
function IconBoard(props) {
const iconRef = useRef();
const [hover, setHover] = useState(false);
const [click, setClick] = useState(false);
const maxRad = Math.PI*2;
useFrame(()=>{
const rotZ = iconRef.current.rotation.z;
if (hover) {
iconRef.current.rotation.z = rotZ < maxRad? rotZ + 0.03: maxRad;
} else {
iconRef.current.rotation.z = rotZ > 0.0? rotZ - 0.05: 0.0;
}
if (click) {
iconRef.current.rotation.y += 0.04;
if (iconRef.current.rotation.y > maxRad) {
iconRef.current.rotation.y = 0;
setClick(false);
}
}
});
return (
<mesh
position={props.position}
ref={iconRef}
onPointerOver={()=>setHover(true)}
onPointerLeave={()=>setHover(false)}
onClick={()=>setClick(true)}
>
<boxGeometry args={[1,1,0.05]} />
<meshBasicMaterial map={props.map} />
</mesh>
);
}
function App() {
const texture = new TextureLoader().load("./icon.png");
return (
<div className="App">
<Canvas>
<IconBoard map={texture} position={[0,0,0]}/>
</Canvas>
</div>
);
}
export default App;
実行画面
複数表示
function App()
内で '< IconBoard />' を並べると複数のアイコンボードを表示できます。
function App() {
const texture = new TextureLoader().load("./icon.png");
return (
<div className="App">
<Canvas>
{[...Array(3)].map((_,x)=><IconBoard map={texture} position={[(x-1)*1.5,1.5,0]}/>)}
<IconBoard map={texture} position={[0,0,0]}/>
{[...Array(3)].map((_,x)=><IconBoard map={texture} position={[(x-1)*1.5,-1.5,0]}/>)}
</Canvas>
</div>
);
}
こんな感じで、さらに神々しさが増しました。
最後に
今年のGWは、Three.js と React-Three で苦手な3Dの勉強を頑張りましたよ。
まだまだ最低限の機能しか使えないけど、なかなか楽しく学べたのでヨシ!