20211126のReactに関する記事は11件です。

WebGLでミラーボールを作るハンズオン(react-three-fiber + drei + TypeScript)

WebGLでミラーボールを作ってみます。ハンズオン的に全ての手順を説明します。 ツールとしてはreact-three-fiber + drei + TypeScript + create-react-appを使います。 本記事の想定読者 Web開発に詳しいが、3Dは詳しくない人向けです。 今回作成するもの 動くデモはこちら 解説 1. 3Dモデル作り まずBlender(ここではバージョン2.92.0)を起動します。 立方体をクリックしてキーボードの「X(エックス)」を押下し、画面に表示された「削除」を押す。 Command + A (Windowsでは Ctrl + A) を押して「メッシュ」→「UV球」 球ができたら球をクリックし、以下の1→2をクリック 「メタリック」と「粗さ」を図のとおり設定する。 メタリック: 0.926 粗さ: 0.074 モデルを.glb形式に変換する。「ファイル」→「エクスポート」→「glTF 2.0(.glb/.gltf)」と選択 名前を「ball.glb」と設定して、あとは何も変えずに「glTF2.0をエクスポート」を押す。 モデルはこれで完成となります。 2. 実装 ここから実装をしていきます。まずライブラリのインストールをします。 yarn create react-app my-app --template typescript yarn add three @react-three/fiber @types/three yarn add @react-three/drei # そして開発サーバーの起動 cd my-app yarn yarn start まず、3Dモデルを配置しておきます。srcディレクトリ直下に、先ほど作ったball.glbを入れます。 それではコードを修正していきます。 src/global.d.tsファイルを作成し、以下を書き込みます。 これにより、先ほど作った.glbファイルがTypeScriptから読み込めるようになります。 declare module "*.glb"; src/index.cssの先頭に以下のコードを追加します。 これはレイアウト調整のためです。 html, body, #root, #root > div { height: 100%; } src/App.tsxを以下のコードに入れ替えます。 これにより、3Dモデルが読み込まれ、画面に表示されます。 import "./App.css"; import React, { Suspense } from "react"; import { Canvas, useFrame } from "@react-three/fiber"; import { useGLTF, Environment, OrbitControls } from "@react-three/drei"; import modelPath from "./ball.glb"; import { GLTF } from "three/examples/jsm/loaders/GLTFLoader"; function Model() { const gltf = useGLTF(modelPath) as unknown as GLTF; useFrame(() => (gltf.scene.rotation.y += 0.002)); return <primitive object={gltf.scene} />; } function App() { return ( <div className="App"> <Canvas> <OrbitControls /> <color attach="background" args={[0x333353]} /> <ambientLight /> <pointLight position={[10, 10, 10]} /> <Suspense fallback={null}> <Environment preset="studio" /> <Model /> </Suspense> </Canvas> </div> ); } export default App; 以上です。 ソースコード 今回作ったものは以下にあります。うまく動かない場合参考にしてください。 確認環境 Node.js v14.17.1 Blender 2.92.0 MacBook Pro (16-inch 2019) macOS Monterey 12.0.1
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者向け】React QueryでQiita API叩いてみる

これはなに? 個人的に最近話題のReact Queryについて入門してみようと思うので導入がてら記事を書いてみました。 やってみようと思った背景 個人的に話題と書いたのにも理由があり、npm trendsで見てみると今年の1月頃から急激に伸びているのです。 これを見た筆者は驚いて今書いている、という感じです。 出典: npm trends 作ったもの Qiita APIを使って記事を取得してそのまま表示する画面を作成しました! 導入手順 導入するにあたって、今回は以下の要件で進めていきます React FW: Next.js UIライブラリ: Chakra UI 1. プロジェクトの作成 作成手順は別の記事でも書いているので、ここでは省略させていただきます。 2. パッケージの追加 バージョンは執筆時点で最新の3.33.5です。 3. 導入 まずApp.tsxにProviderを記載します App.tsx import "@/styles/globals.css" import type { AppProps } from "next/app" import React from "react" import { ChakraProvider } from "@chakra-ui/react" import { QueryClient, QueryClientProvider } from "react-query" const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false, staleTime: 300000, }, }, }) function MyApp({ Component, pageProps }: AppProps) { return ( <QueryClientProvider client={queryClient}> <ChakraProvider> <Component {...pageProps} /> </ChakraProvider> </QueryClientProvider> ) } export default MyApp 次にreact queryを使ってQiita APIを呼び出している部分 pages/reactQuery.tsx import { Box, ListItem, Text, UnorderedList } from "@chakra-ui/react" import { NextPage } from "next" import React from "react" import { useQuery } from "react-query" import axios from "axios" const ReactQuery: NextPage = () => { const query = "nextjs" const { data, isLoading } = useQuery("qiita", async () => { const { data } = await axios.get("https://qiita.com/api/v2/items", { params: { page: "1", per_page: "20", query: query, }, }) return data }) if (isLoading) { return <Text>Loading...</Text> } if (!data) { return <Text>Error Occured</Text> } return ( <Box> <UnorderedList> {data?.map((item: any, index: number) => { return <ListItem key={index}>{item.title}</ListItem> })} </UnorderedList> </Box> ) } export default ReactQuery オプションに関してはApp.tsxのqueryClientで、実際に使用するreactQuery.tsxではわずか10行ほどで実装できてとてもシンプルです。 defaultOptionsをトップレベルに書くだけで全画面で適用されるので無限に再フェッチするというアホみたいなことを防げたりするので良いなと思いました。 今回適用したデフォルトオプション const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false, staleTime: 300000, }, }, }) retry: リクエストに失敗した場合に何回まで再リクエストを送るか refetchOnWindowFocus: window.focus時に再取得を行うかどうか staleTime: 再取得する間隔の指定 デフォルトでは0なので注意 ミリ秒で指定 まとめ 今回はReact Queryを使って実際に動かすところまでやってみましたが、mutationsやquery invalidation、その他のオプションをまだ使いきれていないので、また次回調べながら記事にしていこうと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript セミコロンについて

function 文末のセミコロンについて セミコロン const mul = function(a, b){ return a * b } JavaScriptでは文末にセミコロンがない場合、セミコロンがある前提で コードを実行してくれます。 ですが、場合によっては意図しない挙動になってしまうことがあります。 場合によっては... const mul = function(a, b){ return a * b } 以上の場合、文末にセミコロンが付くため、 returnの結果がundefinedになってしまいます。 場合によっては... const mul = function(a, b){ return; //undefined a * b; //実行されない } まとめ ・挙動がおかしい際、セミコロンの自動付与について確認してみる ・個人的にはセミコロンは記載した方がわかりやすいと思うが、コード規約等に追記した方がよいかも。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactでモーダル背景固定用CustomHooks

モーダルで背景を固定したい時のCustomHooks CustomHook import { useEffect, useState } from "react"; export const useScrollLock = (isLock: boolean): void => { const [scrollY, setScrollY] = useState(0); useEffect(() => { if (isLock) { const y = window.scrollY; setScrollY(y); document.body.style.position = "fixed"; document.body.style.top = `-${y}px`; document.body.style.height = "auto"; } else { document.body.style.position = ""; document.body.style.top = ""; document.body.style.height = ""; window.scrollTo(0, scrollY); } }, [isLock]); }; 使用例 export const Modal = (props: Props) => { useScrollLock(props.isOpen); // 省略 }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ポケモンBDSP】ポケッチの電卓をWebアプリで再現しようとしたら、バグまで再現してしまった話

ポケッチの電卓でバグ!? この記事を書いた1週間前の2021年11月19日に、『ポケットモンスター ブリリアントダイヤモンド・シャイニングパール』(ポケモンBDSP)が発売されました!このゲームは特にバグが多い作品として盛り上がっていて、連日様々なバグ発見のツイートを見てきました。 そんな中、以下のツイートを見つけました。 ゲーム内に登場するスマートウォッチのようなアイテム「ポケッチ」の電卓アプリで計算をしている様子の動画ですが、 なんと9+10=21となっています。 ※これが本当にバグなのか、動画を編集したフェイクなのかは分かりません。 電卓を作るのはそこまで難しいイメージはなく、バグが出るのはかなり意外だったので、電卓の作り方に興味が湧いてきました。 私は今までカウンターやタイマー等のWebアプリを作ってきましたが、電卓を作ったことがなかったので一度作ってみようと思いました。 自分で電卓を作ってみた 今回はポケッチの電卓と同じような動作をする簡単なWebアプリを作ってみました。 小数点の入力と四則演算ができます。(「*」と「/」はそれぞれ「×」と「÷」として扱います) 使用技術はNext.js(React) + TypeScriptです。 ↓こちらからアクセスできます ↓ソースコードはこちらです ゲーム内のポケッチの電卓では、数値は10桁1までしか表示できないため、計算結果が99億9999万9999を超える場合は、「??????????」と表示されます。 私の電卓でもこれを再現するために、10桁を超える数値を表示しようとすると、代わりに「??????????」と表示されるようにしています。 index.tsx(数値の表示部分) <input type="text" defaultValue={ String(outputValue).length > MAX_DIGIT ? "??????????" : outputValue } size={10} style={{ textAlign: "right" }} ></input> 他人の電卓のコードを参考にせずに自分で考えながら作ったので、制作に2時間くらいかかりました。 新たなバグの判明 自分で電卓を作った翌日に、以下の記事を見つけました。 記事によると、ポケモンBDSPをドイツ語またはフランス語でプレイしている際に、ポケッチの電卓の挙動がおかしくなるそうです。 その例として、510÷252の計算結果が「??????????」になるというものがあります。 まさかとは思い、自分の電卓でも510÷252を試してみると... 「??????????」が表示されてしまいました? 510÷252の正確な答えは「2.0238095238095...」で、割り切れない循環小数になります。 したがって、1÷3=0.3333...も同様に「??????????」と表示されてしまいます。 本来「??????????」が表示されるパターンとしては、計算結果が大きすぎる数(100億以上)、あるいは小さすぎる(負が大きい)数(-10億以下)になった場合が想定されていたと思います。 しかし、ポケモンBDSPのドイツ語・フランス語のポケッチの電卓と私の電卓では、計算結果が大きすぎたり小さすぎたりしなくても、小数点以下を含めた桁数が多いと「??????????」と表示されてしまっています。 ちなみにポケモンBDSPにはリメイク前の作品『ポケットモンスター ダイヤモンド・パール』がありますが、そちらの日本語版のポケッチの電卓では510÷252の計算結果が「2.02380952」と表示されていて、10桁になるように小数第九位の「3」以下が切り捨てられていることが分かります。 よって、この不具合の対策としては、計算結果が小数の場合に、10桁で表示できるように小数を切り捨てる処理を入れるべきでした。 また、上記のAUTOMATONの記事によると、ドイツ語とフランス語では小数点表記にピリオド(.)ではなくカンマ(,)が使われることが原因ではないかと考察した人がいるそうです。 これらを踏まえてポケモンBDSPの電卓のバグの原因を詳細に考察すると、 「計算結果が小数の場合は10桁以下になるように切り捨てられるはずだが、計算結果が小数かどうかはピリオド(.)の有無で判定しているため、小数点がカンマ(,)であるドイツ語やフランス語では小数切り捨ての処理が行われない」 と考えられます。 正確には10桁ではなく10文字。マイナス記号や小数点も1文字としてカウントされる。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

イテレーター(ES6)

イテレーターとは イテレーターとは配列に対して関数を適用し、スマートな書き方で コーディングできるメソッドです。 使用例 イテレーター const array = ["鈴木くん", "佐々木くん", "田中さん"] let students = '' array.forEach((v) => {students = students + '<td>${v}</td>'}) console.log("<table><tr>" + students) + "</tr></table>") 特徴 JSX等で配列を加工して使う際に利用できる。 人によって可読性が変わるので、コード規約等で統一できると良い
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

プロトタイプ変数

PropTypes varやletより厳密な変数宣言、プロトタイプ変数が存在します。 数値や文字列しか格納できなくすることで仕様理解や潜在的なエラー発見が可能になります。 型 宣言方法 数値     PropTypes.number 文字列    PropTypes.string 論理値    PropTypes.bool 配列     PropTypes.array 関数     PropTypes.func オブジェクト PropTypes.object 使用例 PropTypes import PropTypes from 'prop-types'; class MyComponent extends React.Component { render() { // This must be exactly one element or it will warn. const i = this.props.number; return ( <div>{i}</div> //数値以外が格納されるとエラー ); } } introduction.propTypes = { number: PropTypes.number }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Harmoware-VIS はじめに

はじめに Harmoware-VISについては別記事「Harmoware-VISの紹介」をご覧ください。 インストール 準備 node.js v18 インストール Mapboxのaccesstoken取得 ⇒ Mapbox.com npm install command. npm install harmoware-vis Github install & Run the software command. git clone https://github.com/Harmoware/Harmoware-VIS cd Harmoware-VIS npm install # example visualize-sample npm run visualize-sample Harmoware-VISの使用方法 基本的に以下のようなコードがベースになります。 rootコンテナは「harmoware-vis.Container」を継承します。 rootコンテナを「harmoware-vis.connectToHarmowareVis」でconnectしexport defaultします。 標示するレイヤーは「harmoware-vis.HarmoVisLayers」の「layers」で指定します。 app.js import React from 'react'; import { Container, connectToHarmowareVis, HarmoVisLayers, MovesLayer, MovesInput } from 'harmoware-vis'; const MAPBOX_TOKEN = 'XXXXXXXXXX'; //Mapboxのaccesstoken class App extends Container { render() { const { actions, viewport, movedData } = this.props; const optionVisible = false; return ( <div> <div className="harmovis_controller"> <ul > <li><MovesInput actions={actions} /></li> </ul> </div> <div className="harmovis_area"> <HarmoVisLayers viewport={viewport} actions={actions} mapboxApiAccessToken={MAPBOX_TOKEN} layers={[ new MovesLayer({ movedData, actions, optionVisible })]} /> </div> </div> ); }} export default connectToHarmowareVis(App); index.js import { render } from 'react-dom'; import { getCombinedReducer } from 'harmoware-vis'; import { createStore } from 'redux'; import { Provider } from 'react-redux'; import React from 'react'; import App from './app'; import 'harmoware-vis/scss/harmoware.scss'; const store = createStore(getCombinedReducer()); render( <Provider store={store}><App /></Provider>, document.getElementById('app') ); APIリファレンス Harmoware-VISのAPIリファレンスについては別記事「Harmoware-VIS APIリファレンス」をご覧ください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React + Reduxの構成でアプリを作成しました【9】【Firebaseを使ったチャットアプリ】

Reactアプリの作成 $ create-react-app <アプリ名> 必要なパッケージのインストール redux react-redux @material-ui/core @material-ui/icons firebase $ yarn add redux react-redux @material-ui/core @material-ui/icons firebase Firebaseの設定 公式サイト:Firebase ① Firebaseにアクセスし、アカウントでログインし、コンソール画面に移動する。 ②プロジェクトの追加を押して、プロジェクト名を入力する。 ③ウェブアプリを追加するために、ニックネームを入力する。 Firebase Realtime Databaseとの紐付け firebase/config.jsを作成し、Firebaseとの接続情報を記載する。 src/firebase/config.js export const firebaseConfig = { apiKey: "****************", authDomain: "****************", databaseURL: "****************", projectId: "****************", appId: "****************", storageBucket: "****************", messagingSenderId: "****************" }; firebase/index.jsでデータベース参照用のインスタンスを作成する。 このファイルをインポートすることで、どこからでもデータベースにアクセスすることができる。 src/firebase/index.js import firebase from 'firebase'; import {firebaseConfig} from './config.js'; export const firebaseApp = firebase.initializeApp(firebaseConfig); export const firebaseDb = firebaseApp.database(); ComponentをMaterial-UIから探す 公式:Material-UI ヘッダー メッセージ入力欄 メッセージ送信ボタン メッセージ表示欄 Material-UIのComponentを編集する src/App.js import React, {Component} from 'react'; import {connect} from 'react-redux'; import AlignItemsList from './components/AlignItemsList'; import ButtonAppBar from './components/ButtonAppBar.js'; import TextInput from './components/TextInput.js'; import {firebaseDb} from './firebase'; import './styles/App.css'; const messagesRef = firebaseDb.ref('messages'); class App extends Component { render() { return ( <React.Fragment> <ButtonAppBar /> <div className="App"> <AlignItemsList /> <TextInput value="メッセージを入力" /> </div> </React.Fragment> ); } } export default App; 参考サイト 『React』 +『Redux』 + 『Firebase』でLINE風のChat機能を作ろう! 【準備編】 『React』 +『Redux』 + 『Firebase』でLINE風のChat機能を作ろう! 【Component編】
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React Three Fiber】DraggableなMeshの実装

概要 React Three Fiberで、ドラッグ可能なメッシュの実装方法をまとめました。 https://rgn91.csb.app/ この記事は、以下のコースの受講に伴って、ドラッグ操作について内容を整理して改めてまとめたものです。 このコースは、少し情報(使用しているライブラリのバージョン)が古いです。 受講してみたい方で、最新のライブラリのバージョンを使用したい方、TypeScriptで実装したい方は、私が受講した時のリポジトリが役に立つかもしれないです。ご参考の際は、Read Meを読んでください。 実装 App.tsx import React, { Suspense, useEffect, useRef, VFC } from 'react'; import * as THREE from 'three'; import { DragControls } from 'three-stdlib'; import { OrbitControls, Sphere, Stats, TorusKnot, useHelper, useTexture } from '@react-three/drei'; import { Canvas, useFrame, useThree } from '@react-three/fiber'; export const App: VFC = () => { return ( <Canvas camera={{ position: [0, 10, 8], fov: 50, aspect: window.innerWidth / window.innerHeight, near: 0.1, far: 2000 }} dpr={window.devicePixelRatio} shadows> {/* canvas color */} <color attach="background" args={['#1e1e1e']} /> {/* fps */} <Stats /> {/* camera controller */} <OrbitControls attach="orbitControls" /> {/* lights */} <Draggable> <PointLight /> </Draggable> {/* objects */} <Suspense fallback={null}> <Draggable> <ToonTorusKnot textureName="threeTone" color="#22cefa" position={[1, 2, 0]} /> </Draggable> <Draggable> <ToonTorusKnot textureName="fiveTone" color="#ea7600" position={[-1, 2, 0]} /> </Draggable> </Suspense> {/* helper */} <axesHelper /> <gridHelper position={[0, 0.01, 0]} args={[10, 10, 'red', 'black']} /> </Canvas> ) } // ============================================== type ToonTorusKnotProps = { textureName: string color: string position: [number, number, number] } const ToonTorusKnot: VFC<ToonTorusKnotProps> = props => { const { textureName, color, position } = props const ref = useRef<THREE.Mesh>(null) const toneMap = useTexture(`/assets/textures/${textureName}.jpg`) toneMap.minFilter = THREE.NearestFilter toneMap.magFilter = THREE.NearestFilter useFrame(() => { ref.current!.rotation.x += 0.005 ref.current!.rotation.y += 0.005 }) return ( <TorusKnot ref={ref} args={[0.5, 0.2, 128, 128]} position={position}> <meshToonMaterial color={color} gradientMap={toneMap} /> </TorusKnot> ) } // ============================================== const PointLight: VFC = () => { const lightRef = useRef<THREE.Light>() useHelper(lightRef, THREE.PointLightHelper, [0.5]) return ( <group position={[0, 5, 0]}> <Sphere scale={0.2}> <meshBasicMaterial color="#f3f3f3" /> </Sphere> <pointLight ref={lightRef} intensity={1} shadow-mapSize-width={2048} shadow-mapSize-height={2048} castShadow /> </group> ) } // ============================================== type DraggableProps = { children: React.ReactNode } const Draggable: VFC<DraggableProps> = ({ children }) => { const ref = useRef<THREE.Group>(null) const { camera, gl, scene } = useThree() useEffect(() => { const controls = new DragControls(ref.current!.children, camera, gl.domElement) controls.transformGroup = true const orbitControls = (scene as any).orbitControls controls.addEventListener('dragstart', () => { orbitControls.enabled = false }) controls.addEventListener('dragend', () => { orbitControls.enabled = true }) }, [camera, gl.domElement, scene]) return <group ref={ref}>{children}</group> } Draggable Componentの実装 実装にはDragControlsを使用します。 type DraggableProps = { children: React.ReactNode } const Draggable: VFC<DraggableProps> = ({ children }) => { const ref = useRef<THREE.Group>(null) const { camera, gl, scene } = useThree() useEffect(() => { const controls = new DragControls(ref.current!.children, camera, gl.domElement) controls.transformGroup = true // console.log(scene) const orbitControls = (scene as any).orbitControls controls.addEventListener('dragstart', () => { orbitControls.enabled = false }) controls.addEventListener('dragend', () => { orbitControls.enabled = true }) }, [camera, gl.domElement, scene]) return <group ref={ref}>{children}</group> } インスタンスを作成するには、引数に以下の3つが必要です。 objects: An array of draggable 3D objects. camera: The camera of the rendered scene. domElement: The HTML element used for event listeners. この内、cameraとdomElementは、useThreeフック経由で取得することができます。 objectsには、ドラッグさせたいオブジェクトの配列を渡します。配列なので、引数で渡されたコンポーネント(children)をgroup化して、それを引数に渡します。ドラッグは、配列で渡されたオブジェクトそれぞれについて適用されます。 ただし、ドラッグできるオブジェクトは、そもそもドラッグが可能なオブジェクトに限ります。今回の例で言えば、Meshオブジェクトはドラッグ可能で、Lightオブジェクトはドラッグ不可です。 groupに対してドラッグを適用したい場合は、controls.transformGroup = trueを設定します。groupにドラッグ可能なオブジェクトが1つでも含まれていれば、group自体がドラッグされるようになります。 このため、PointLightオブジェクトはドラッグ不可ですが、PointLightオブジェクトと一緒に渡されているSphereジオメトリがドラッグ可能で、それらをまとめてgroup化しているので、結果的にPointLightオブジェクトもドラッグ操作で移動します。 orbitControlsの無効化 orbitControlsを追加している場合、ドラッグ操作でカメラも移動してしまします。そのため、ドラッグをしているときはカメラの操作を無効にする必要があります。 orbitControlsオブジェクトは、sceneオブジェクトから取得することができます。ただし、orbitControls追加時に明示的にattachする必要があります。 <OrbitControls attach="orbitControls" /> console.log Scene //・・・ > orbitControls: OrbitControls {object: PerspectiveCamera, domElement: canvas, enabled: true, target: Vector3, minDistance: 0, …} あとは、dragstart・dragendイベントで、orbitControlsのtrue/falseを切り替えます。 const orbitControls = (scene as any).orbitControls controls.addEventListener('dragstart', () => { orbitControls.enabled = false }) controls.addEventListener('dragend', () => { orbitControls.enabled = true }) MeshToonMaterial meshのmaterialには、MeshToonMaterialを使用しています。 const ToonTorusKnot: VFC<ToonTorusKnotProps> = props => { const { textureName, color, position } = props const ref = useRef<THREE.Mesh>(null) const toneMap = useTexture(`/assets/textures/${textureName}.jpg`) toneMap.minFilter = THREE.NearestFilter toneMap.magFilter = THREE.NearestFilter useFrame(() => { ref.current!.rotation.x += 0.005 ref.current!.rotation.y += 0.005 }) return ( <TorusKnot ref={ref} args={[0.5, 0.2, 128, 128]} position={position}> <meshToonMaterial color={color} gradientMap={toneMap} /> </TorusKnot> ) } gradientMapを指定して、影を3段階、5段階にしています。 texture画像は、three.jsの公式サイトから拝借しました。ダウンロード方法はおまけを参照してください。 PointLight ライトは、位置がわかりやすいようにSphereジオメトリも追加しています。また、Sphereジオメトリを追加したことでPointLightもドラッグすることができます。 const PointLight: VFC = () => { const lightRef = useRef<THREE.Light>() useHelper(lightRef, THREE.PointLightHelper, [0.5]) return ( <group position={[0, 5, 0]}> <Sphere scale={0.2}> <meshBasicMaterial color="#f3f3f3" /> </Sphere> <pointLight ref={lightRef} intensity={1} shadow-mapSize-width={2048} shadow-mapSize-height={2048} castShadow /> </group> ) } ライト自体(pointLight)が本当にドラッグできているのか確認するために、helperを追加しています。 useHelperを使用することで簡単に実装することができます。特に、第3引数([0.5])にはPointLightHelperのインスタンス生成時に渡すsphereSizeを渡します。 useHelper PointLightHelper 成果物 おまけ Texture 画像のダウンロード方法 MeshToonMaterialのページから、新規Windowでサンプルを開きます。 [F12]を押して、ソースのテクスチャファイルを選択して、新しいタブで開きます。 画像が新規画面で表示されるので、保存します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactのVFCとFCについて

ReactのVFC型とFC型について Reactで開発しているとき、VFCとFCって何が違うん?ってなったので調べたことを ここにまとめてみます。 何が違うのか? VFCの定義 VFC=VoidFunctionComponentの略だそうで、型定義は以下のようにされています。 interface VoidFunctionComponent<P = {}> { (props: P, context?: any): ReactElement<any, any> | null; propTypes?: WeakValidationMap<P> | undefined; contextTypes?: ValidationMap<any> | undefined; defaultProps?: Partial<P> | undefined; displayName?: string | undefined; } FCの定義 FC=FunctionComponentの略だそうで、型定義は以下のようにされています。 interface FunctionComponent<P = {}> { (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null; propTypes?: WeakValidationMap<P> | undefined; contextTypes?: ValidationMap<any> | undefined; defaultProps?: Partial<P> | undefined; displayName?: string | undefined; } 基本的に、4つのフィールドを所有していることに変わりはないのですが、 FCでは、propsにchildrenが暗黙的に定義されていて、VFCではされていないという違いがあります。 結論 つまり、どっちがいいのかという感じですが、 私はVFCがいいと思います。 VFCを使用してchildrenを明示的にするほうがTypeScriptコードとして遥かにクリーンなコードだと言えると思います。 ちなみに、FCに暗黙的にchildrenが定義されているのは今後修正されるそうなので、 修正されたらFCが推奨になるそうです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む