Skip to content

Commit ea2fc12

Browse files
authored
fix: UX improvement (#634)
* chore: add reset color button * fix: use default color for each car when select a new car * fix: compilation error * fix: each car now has its own state * fix: linting issues * fix: rename files + cleaner code * fix: react key * fix: limit min and max distance
1 parent 63b7654 commit ea2fc12

File tree

5 files changed

+109
-29
lines changed

5 files changed

+109
-29
lines changed

prototype/src/App.tsx

Lines changed: 93 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,110 @@
11
import { Canvas } from '@react-three/fiber'
22
import { Environment, Loader, OrbitControls, Stats, useProgress } from "@react-three/drei"
3-
import Lamborghini from "./components/Models/Lamborghini"
3+
import Lamborghini from "./components/Models/Lamborghini Aventador J"
44
import Scene from "./components/Models/Autobianchi Stellina"
5-
import Maserati from "./components/Models/Maserati_mc20"
6-
import { Leva, useControls } from 'leva'
7-
import { Suspense } from 'react'
5+
import Maserati from "./components/Models/Maserati MC20"
6+
import { Leva, levaStore, useControls, button } from 'leva'
7+
import { Suspense, useEffect, useRef, useState } from 'react'
8+
import { Model, ModelProps, models } from './components/Models/model'
9+
10+
interface Cars {
11+
readonly Model:(props: ModelProps) => JSX.Element;
12+
readonly interior: string;
13+
readonly exterior: string;
14+
}
815

916
export default function App() {
10-
const carNameComponentMap = {
11-
"Lamborghini Aventador J": Lamborghini,
12-
"Maserati MC20": Maserati,
13-
"Autobianchi Stellina": Scene
17+
const cars: Record<Model, Cars> = {
18+
"Lamborghini Aventador J": {
19+
Model: Lamborghini,
20+
interior: "#000000",
21+
exterior: "#9a9898",
22+
},
23+
"Maserati MC20": {
24+
Model: Maserati,
25+
interior: "#000000",
26+
exterior: "#ffffff"
27+
},
28+
"Autobianchi Stellina": {
29+
Model: Scene,
30+
interior: "#000000",
31+
exterior: "#963f3f"
32+
},
33+
};
34+
35+
const [carsState, setCarsState] = useState(() => cars);
36+
const carsStateRef = useRef(carsState);
37+
38+
useEffect(() => {
39+
carsStateRef.current = carsState;
40+
}, [carsState]);
41+
42+
const resetCarColor = () => {
43+
const model = levaStore.get("Select") as Model;
44+
set({
45+
Exterior: cars[model].exterior,
46+
Interior: cars[model].interior,
47+
});
48+
};
49+
50+
const setCarInterior = (interior: string) => {
51+
const model = levaStore.get("Select") as Model;8
52+
setCarsState({
53+
...carsStateRef.current,
54+
[model]: {
55+
...carsStateRef.current[model],
56+
interior
57+
}
58+
})
1459
};
1560

16-
const { Interior, Exterior, Rotation, Select, Stats: stats } = useControls({
17-
Select: { options: Object.keys(carNameComponentMap) },
18-
Interior: '#000000',
19-
Exterior: '#9a9898',
61+
const setCarExterior = (exterior: string) => {
62+
const model = levaStore.get("Select") as Model;
63+
setCarsState({
64+
...carsStateRef.current,
65+
[model]: {
66+
...carsStateRef.current[model],
67+
exterior
68+
}
69+
})
70+
};
71+
72+
const [{ Rotation, Stats: stats }, set] = useControls(() => ({
73+
Select: {
74+
options: models,
75+
onChange: (value: Model) => {
76+
set({
77+
Exterior: carsStateRef.current[value].exterior,
78+
Interior: carsStateRef.current[value].interior,
79+
});
80+
}
81+
},
82+
Interior: {
83+
value: '#000000',
84+
onChange: setCarInterior
85+
},
86+
Exterior: {
87+
value: '#9a9898',
88+
onChange: setCarExterior
89+
},
2090
Rotation: false,
2191
Stats: true,
22-
});
92+
"Reset color": button(resetCarColor),
93+
}));
2394

24-
const { progress } = useProgress()
95+
const { progress } = useProgress();
2596

2697
return (
2798
<>
2899
<Canvas camera={{ position: [0, 0, 10] }} shadows={true} frameloop="demand">
29100
<Suspense fallback={null}>
30-
{Object.entries(carNameComponentMap)
31-
.map(([name, CarModel]) => (
32-
CarModel({
33-
exterior: Exterior,
34-
interior: Interior,
35-
visible: Select === name,
101+
{models
102+
.map(name => (
103+
cars[name].Model({
104+
exterior: carsState[name].exterior,
105+
interior: carsState[name].interior,
106+
visible: levaStore.get("Select") === name,
107+
key: name
36108
})
37109
))}
38110
</Suspense>
@@ -42,7 +114,7 @@ export default function App() {
42114
blur={0.5}
43115
/>
44116
{stats ? <Stats /> : undefined}
45-
<OrbitControls maxPolarAngle={7 * Math.PI / 18} maxDistance={20} autoRotate={Rotation} />
117+
<OrbitControls maxPolarAngle={7 * Math.PI / 18} autoRotate={Rotation} minDistance={2} maxDistance={15} />
46118
</Canvas>
47119
<Loader />
48120
<Leva

prototype/src/components/Models/Autobianchi Stellina.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Auto-generated by: https://github.com/pmndrs/gltfjsx
55
import * as THREE from 'three'
66
import { useGLTF } from '@react-three/drei'
77
import { GLTF } from 'three/examples/jsm/loaders/GLTFLoader'
8-
import { Model } from "./model"
8+
import { ModelProps } from "./model"
99

1010
type GLTFResult = GLTF & {
1111
nodes: {
@@ -186,7 +186,7 @@ type GLTFResult = GLTF & {
186186
}
187187
}
188188

189-
export default function Car(props: Model) {
189+
export default function Car(props: ModelProps) {
190190
const { nodes, materials } = useGLTF('/scene.gltf') as GLTFResult
191191

192192
return (

prototype/src/components/Models/Lamborghini.tsx renamed to prototype/src/components/Models/Lamborghini Aventador J.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Auto-generated by: https://github.com/pmndrs/gltfjsx
44
import * as THREE from 'three'
55
import { useGLTF } from '@react-three/drei'
66
import { GLTF } from 'three/examples/jsm/loaders/GLTFLoader'
7-
import { Model } from './model'
7+
import { ModelProps } from './model'
88

99
type GLTFResult = GLTF & {
1010
nodes: {
@@ -72,7 +72,7 @@ type GLTFResult = GLTF & {
7272
materials: object
7373
}
7474

75-
export default function Lamborghini(props: Model) {
75+
export default function Lamborghini(props: ModelProps) {
7676
const { nodes } = useGLTF('/lamborghini.glb') as GLTFResult;
7777

7878
return (

prototype/src/components/Models/Maserati_mc20.tsx renamed to prototype/src/components/Models/Maserati MC20.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Title: Maserati MC20
99
import * as THREE from 'three'
1010
import { useGLTF } from '@react-three/drei'
1111
import { GLTF } from 'three-stdlib'
12-
import { Model } from './model'
12+
import { ModelProps } from './model'
1313

1414
type GLTFResult = GLTF & {
1515
nodes: {
@@ -561,7 +561,7 @@ type GLTFResult = GLTF & {
561561
}
562562
}
563563

564-
export default function Car(props: Model) {
564+
export default function Car(props: ModelProps) {
565565
const { nodes, materials } = useGLTF('/maserati_mc20.glb') as GLTFResult
566566
return (
567567
<group {...props} dispose={null} position={[-2.2, -1, 4]} scale={[2, 2, 2]}>

prototype/src/components/Models/model.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export interface Model {
1+
export interface ModelProps {
22
/**
33
* Set color of the model
44
*/
@@ -11,4 +11,12 @@ export interface Model {
1111
* To show or hide model
1212
*/
1313
readonly visible: boolean
14-
}
14+
/**
15+
* Key of the component
16+
*/
17+
readonly key: string
18+
}
19+
20+
21+
export const models = ["Lamborghini Aventador J", "Maserati MC20", "Autobianchi Stellina"] as const;
22+
export type Model = typeof models[number];

0 commit comments

Comments
 (0)