I have 3 .GLB files which I want to change based on the state (Sort of a gallery and one main image), but it loads weirdly when changing and it doesn't render some 3d models. Using three.js, ChakraUI, and nextJS. As I've only begun to test out 3d model I don't know if this is the correct way to re-render or conditionally show the model.
Series.js
const itemData = [
{
image: "/images/ssr-img1.jpg",
model: "zombie.glb",
},
{
image: "/images/ssr-img2.jpg",
model: "star-icon.glb",
},
{
image: "/images/ssr-img3.jpg",
model: "dog.glb",
},
{
image: "/images/ssr-img4.jpg",
model: "zombie.glb",
},
{
image: "/images/ssr-img5.jpg",
model: "star-icon.glb",
},
{
image: "/images/ssr-img6.jpg",
model: "dog.glb",
},
];
export default function Series() {
const [items, setItems] = useState(itemData);
const [model, setModel] = useState("star-icon.glb");
const [currentImage, setCurrentImage] = useState(itemData[0].image);
return (
<Box id="nfc" bg="#edf3f8" py="2rem">
<Suspense fallback={null}>
<Test model={model} />
</Suspense>
<Title color="black">Rarity List</Title>
<Stack>
<HStack align="center" justify="center" my="1rem" maxW="full">
{items.map((item, index) => (
<Image
maxW="5%"
key={index}
onClick={() => setModel(item.model)}
cursor="pointer"
src={item.image}
alt="test"
/>
))}
</HStack>
</Stack>
</Box>
);
}
Test.js (The 3d model)
import { Canvas, useFrame } from "#react-three/fiber";
import { useGLTF, Environment, OrbitControls } from "#react-three/drei";
import { Box } from "#chakra-ui/react";
function Dog(props) {
const { scene } = useGLTF(props.model);
return <primitive object={scene} {...props} />;
}
function Zoom() {
useFrame((state, delta) => {
state.camera.zoom += delta / 100;
state.camera.updateProjectionMatrix();
});
}
export default function Test({ model }) {
return (
<Box h="xl">
<Canvas camera={{ position: [2.5, 2.5, -2.5], fov: 35 }}>
<ambientLight intensity={0.5} />
<Dog
model={model}
position={[-0.1, -0.2, 0]}
rotation={[0, Math.PI / 2, 0]}
scale={0.2}
/>
<Environment preset="city" />
<OrbitControls
minPolarAngle={Math.PI / 2.5}
maxPolarAngle={Math.PI / 2.5}
/>
<Zoom />
</Canvas>
</Box>
);
}
Related
Currently getting my head into React and DND for the first time. I have created a small example application where I try to implement a basic functionality of DND.
I´m running into two issues:
After I drop an item and reorder the state object (and set the state) the items get reseted to the inital state.
After the first drop I cannot drag all items further as they seem to not be associated with the draggable id anymore.
This screenshot shows that the source and destination id is different and also that the object order of the state has changed (onDragEnd function).
Also the second issue described is shown, that the draggable id is not available anymore.
Here is my code
import { PlusCircleOutlined } from "#ant-design/icons/lib/icons";
import { Row, Col, Button, Divider } from "antd";
import { useState } from "react";
import {
DragDropContext,
Draggable,
Droppable,
DroppableProvided,
DraggableLocation,
DropResult,
DroppableStateSnapshot,
DraggableProvided,
DraggableStateSnapshot,
} from "react-beautiful-dnd";
import Note from "../note/Note";
function Home() {
const [notes, setNotes] = useState([
{ title: "Hallo", text: "BlaBla" },
{ title: "Hallo1", text: "BlaBla1" },
{ title: "Hallo2", text: "BlaBla1" },
]);
function onDelete(index: number) {
const newNotes = [...notes];
newNotes.splice(index, 1);
setNotes(newNotes);
}
function onEdit(index: number, title: string, text: string) {
const newNotes = [...notes];
newNotes[index].title = title;
newNotes[index].text = text;
setNotes(newNotes);
}
function onCreateNote() {
let notesCount = notes.length;
setNotes((notes) => [
...notes,
{ title: "New" + notesCount, text: "New text" },
]);
}
function onDragEnd(result: DropResult): void {
console.log("DRAG END");
console.log(result);
const { source, destination } = result;
if (!destination) {
return;
}
console.log(notes);
const newNotes = [...notes];
const [removed] = newNotes.splice(source.index, 1);
newNotes.splice(destination.index, 0, removed);
console.log(newNotes);
setNotes(newNotes);
//Wenn mehr als eine liste
/* if (source.droppableId === destination.droppableId) {
return;
} */
}
return (
<div>
<Row>
<Col xs={2} sm={4} md={6} lg={6} xl={6}>
<Button
type="primary"
shape="round"
size="large"
icon={<PlusCircleOutlined />}
onClick={() => onCreateNote()}
>
Create new note
</Button>
</Col>
</Row>
<Divider></Divider>
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="droppable">
{(provided: DroppableProvided, snapshot: DroppableStateSnapshot) => (
<div
ref={provided.innerRef}
{...provided.droppableProps}
/* style={getListStyle(snapshot.isDraggingOver)} */
>
<Row gutter={[16, { xs: 8, sm: 16, md: 24, lg: 32 }]}>
{notes.map((value, index) => (
<Col
className="gutter-row"
xs={2}
sm={4}
md={6}
lg={6}
xl={6}
key={index}
>
<Draggable
key={index}
draggableId={value.title}
index={index}
>
{(providedDraggable: DraggableProvided) => (
<div
ref={providedDraggable.innerRef}
{...providedDraggable.draggableProps}
{...providedDraggable.dragHandleProps}
>
<Note
onDelete={() => onDelete(index)}
onEdit={(title: string, text: string) =>
onEdit(index, title, text)
}
noteTitle={value.title}
noteText={value.text}
/>
</div>
)}
</Draggable>
</Col>
))}
</Row>
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
</div>
);
}
export default Home;
Any idea what I´m missing here?
Here is a cube of planes that I can click
The main issue with this so far is that clicking on a plane clicks through when I would only want to click the plane that my mouse is over. What am I missing on my Three.js Plane?
I have tried searching for something relating to collision in three.js but to no avail so far.
After further research, I think it has something to do with RayCasting?
import React, { useState, useRef } from "react";
import { OrbitControls, Plane } from "#react-three/drei";
import { Canvas, useFrame, useThree, extend } from "#react-three/fiber";
import styles from "../styles/game.module.css";
import { DoubleSide } from "three";
const Cell = (props) => {
const [hovered, hover] = useState(false);
const [checked, setChecked] = useState(false);
const colorStyle = () => {
if (hovered) return "hotpink";
if (checked) return "lightblue";
return "orange";
};
return (
<Plane
scale={1}
onClick={() => setChecked(!checked)}
onPointerEnter={() => hover(true)}
onPointerLeave={() => hover(false)}
position={props.position}
rotation={props.rotation}
>
<meshPhongMaterial side={DoubleSide} color={colorStyle()} />
</Plane>
);
};
const Cube = () => {
useFrame((state, delta) => {
});
return (
<>
{/* Back Face */}
<Cell position={[-1, 1, -1.5]} rotation={[0, 0, 0]} />
// other cells here
{/* Front Face */}
<Cell position={[-1, 1, 1.5]} rotation={[0, 0, 0]} />
// other cells here
{/* Left Face */}
<Cell position={[-1.5, 1, 1]} rotation={[0, Math.PI / 2, 0]} />
// other cells here
{/* Right Face */}
<Cell position={[1.5, 1, 1]} rotation={[0, Math.PI / 2, 0]} />
// other cells here
{/* Bottom Face */}
<Cell position={[1, -1.5, 1]} rotation={[Math.PI / 2, 0, 0]} />
// other cells here
{/* Top */}
<Cell position={[1, 1.5, 1]} rotation={[Math.PI / 2, 0, 0]} />
// other cells here
</>
);
};
const SceneItems = () => {
return (
<>
<OrbitControls minDistance={7.5} maxDistance={15} />
<ambientLight intensity={0.5} />
<spotLight position={[10, 15, 10]} angle={0.3} />
<Cube position={[1, 1, 1]} />
</>
);
};
const CompleteScene = () => {
return (
<div id={styles.scene}>
<Canvas>
<SceneItems />
</Canvas>
</div>
);
};
It appears that all I needed to prevent this click-through from happening was to add event.stopPropagation() to my event listeners on my Plane. Now I no longer click through the Plane
const Cell = (props) => {
const [hovered, hover] = useState(false);
const [checked, setChecked] = useState(false);
useCursor(hovered);
const colorStyle = () => {
if (hovered) return "hotpink";
if (checked) return "lightblue";
return "orange";
};
return (
<Plane
scale={1}
onClick={(e) => {
e.stopPropagation();
setChecked(!checked);
}}
onPointerEnter={(e) => {
e.stopPropagation();
hover(true);
}}
onPointerLeave={(e) => {
e.stopPropagation();
hover(false);
}}
position={props.position}
rotation={props.rotation}
>
<meshPhongMaterial side={DoubleSide} color={colorStyle()} />
</Plane>
);
};
More details here: https://docs.pmnd.rs/react-three-fiber/api/events#pointer-capture
I have a camera view which shows the camera view as the background. On that page I have a joystick and some sliders to control exposure and move around the view. How can I make a toggle to disable the joystick and sliders so that the view is unobstructed?
Below is my code. What I need is a toggle which can hide the sliders and joystick which are in this view. So when the toggle is selected it just hides everything and only shows the mjpeg stream and when unselected it shows the sliders and joystick again.
import { Layout } from "components/common";
import { Box, Text, Icon, IconButton, VStack, Button } from "native-base";
import React, {
cloneElement,
useCallback,
useLayoutEffect,
useState,
} from "react";
import * as ScreenOrientation from "expo-screen-orientation";
import { useFocusEffect, useNavigation } from "#react-navigation/core";
import { Feather } from "#expo/vector-icons";
import { KorolJoystick, KorolSlider, Stepper } from "components/ui";
import { SettingsModal } from "components/CameraView";
import { WebView } from "react-native-webview";
import { useAppSelector } from "redux-store/store";
// import socket from "tools/poseCam";
var ip = "10.42.0.1";
let ws = new WebSocket(`ws://${ip}:2100/ws`);
ws.onopen = () => {
console.log("connection established");
};
const CameraView = () => {
const nav = useNavigation();
const [showSettings, setShowSettings] = useState(false);
const { controlType, step } = useAppSelector((state) => state.cameraSettings);
useLayoutEffect(() => {
ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.LANDSCAPE);
}, []);
const [x, setX] = useState(0);
useFocusEffect(
useCallback(() => {
return () => {
ScreenOrientation.unlockAsync();
};
}, [])
);
return (
<Layout>
<Box flex={1} position="relative">
<WebView
originWhitelist={["*"]}
scrollEnabled={false}
scalesPageToFit
style={{
backgroundColor: "transparent",
}}
containerStyle={{
flex: 1,
}}
source={{
uri: "http://10.42.0.1:2101/mjpeg",
// uri: "http://mjpeg.sanford.io/count.mjpeg",
}}
/>
<VStack
position="absolute"
display="flex"
width="full"
flexDirection="row"
padding="5"
alignItems="center"
>
<IconButton
marginRight="auto"
icon={<Icon as={Feather} color="primary.500" name="arrow-left" />}
colorScheme="primary"
onPress={() => nav.goBack()}
/>
<Button
size="md"
startIcon={<Icon size="xs" as={Feather} name="plus" />}
backgroundColor="primary.500:alpha.40"
>
Add Waypoint
</Button>
<IconButton
icon={<Icon as={Feather} color="primary.500" name="settings" />}
colorScheme="primary"
onPress={() => setShowSettings(true)}
/>
</VStack>
<VStack position="absolute" bottom="30" left="30" width="56" space={2}>
<VStack>
{/* <Text>Focus</Text>
<KorolSlider /> */}
</VStack>
<VStack>
{/* <Text>Zoom</Text>
<KorolSlider /> */}
</VStack>
<VStack>
<Text>Slide</Text>
<KorolSlider
value={x}
onChange={(slider) => {
setX(slider);
console.log(slider);
}}
onDragEnd={() => {
let message = {
from: "FromClient",
data: {
case: "joystick",
joystick: {
slider: x,
},
},
};
ws.send(JSON.stringify(message));
}}
/>
</VStack>
</VStack>
<Box position="absolute" right="30" bottom="30">
{controlType === "joystick" ? (
<KorolJoystick
onMove={(values) => {
console.log(values);
let message = {
from: "FromClient",
data: {
case: "joystick",
joystick: {
angle: values.angle.radian,
force: values.force,
},
},
};
ws.send(JSON.stringify(message));
}}
onStop={(values) => {
let message = {
from: "FromClient",
data: {
case: "joystick",
joystick: {
angle: 0,
force: 0,
},
},
};
ws.send(JSON.stringify(message));
}}
/>
) : (
<Stepper
onPress={(dir) => {
const vel = 5;
const tiltVel = 5;
// if (Math.abs(step)<=1){
// vel=1
// }
if (dir === "up") {
const message = {
from: "FromClient",
data: {
case: "controlCMDRel",
moveData: { tilt: [step, vel] },
},
};
console.log(message);
ws.send(JSON.stringify(message));
}
if (dir === "down") {
const message = {
from: "FromClient",
data: {
case: "controlCMDRel",
moveData: { tilt: [-step, vel] },
},
};
ws.send(JSON.stringify(message));
}
if (dir === "left") {
const message = {
from: "FromClient",
data: {
case: "controlCMDRel",
moveData: { pan: [-step, vel] },
},
};
ws.send(JSON.stringify(message));
}
if (dir === "right") {
const message = {
from: "FromClient",
data: {
case: "controlCMDRel",
moveData: { pan: [step, vel] },
},
};
ws.send(JSON.stringify(message));
}
}}
/>
)}
</Box>
</Box>
<SettingsModal
show={showSettings}
onClose={() => setShowSettings(false)}
/>
</Layout>
);
};
export default CameraView;
I cant see where (on which element) your background image is set,but you can render any component conditionally like this:
return(
<div>
I am wrapper
{isVisible && <div>I am visible if isVisible is true</div>}
</div>
)
and you can set background image on the parent div, so when the child div is not visible you can see the background image.
I experienced React before and trying to learn React Native so here is my problem about react-navigation
I have some separated class or js files for react-navigation
main.js
type Props = {};
class Main extends Component<Props> {
componentDidMount() {
SplashScreen.hide();
}
render() {
return (
<Container>
<MainPostsList />
</Container>
)
}
};
const Stack = createStackNavigator();
//its not a class for this navigation thing
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={Main} />
<Stack.Screen name="Post" component={PostsPage} />
</Stack.Navigator>
</NavigationContainer>
)
}
export default App;
postlist.js
export default class MainPostLists extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
data: [],
}
}
componentDidMount() {
getMainPosts().then(data => {
this.setState({
isLoading: false,
data: data
})
}, error => {
Alert.alert('An error has occurred', 'Please try again later.')
})
}
render() {
console.log(this.state.data.data);
let view = this.state.isLoading ? (
<View>
<ActivityIndicator animating={this.state.isLoading} size={"large"} style={{paddingTop: 40}} />
</View>
) : (
<List
dataArray={this.state.data.data}
renderRow={(item) => {
return <MenuPostItem data={item} />
}} />
)
return (
<Container>
<Content>
{view}
</Content>
</Container>
)
}
}
postlistitem.js
export default class MenuPostItem extends Component {
constructor(props) {
super(props);
this.data = props.data;
}
render() {
console.log(this.data)
const goToPostPage = (postlink) => {
console.log("Going to Post Page " + postlink);
this.props.navigation.navigate('Post')
};
return (
<ListItem button onPress={() => {goToPostPage(this.data.postlink)}} onLongPress>
<Left>
{this.data.type === 'video' ? (
<Thumbnail style={{ width: 150, height: 84, borderRadius: 0 }} source={{
uri: `http://` + hostname + `:5000/media/streamimage?src=${this.data.media.videothumbsrc}`
//uri: ImageThumbTest
}} />
) : (null)}
{this.data.type === 'text' ? (
<Thumbnail style={{ width: 150, height: 84, borderRadius: 0 }} />,
<Text style={{paddingLeft: 40, paddingRight: 40}}>Text Post</Text>
) : (null)}
</Left>
<Body>
<Text>{this.data.title}</Text>
<Text note numberOfLines={3}>{this.data.description}</Text>
</Body>
</ListItem>
)
}
}
this error appears on Android as I click on an item from the list:
it was supposed to go to a post dynamic page
MenuPostItem doesn't have any access to navigation props.
Here how you get access to navigation props
Passing the navigation prop to MenuPostItem from MainPostLists component
<List
dataArray={this.state.data.data}
renderRow={(item) => {
return <MenuPostItem data={item} navigation={this.props.navigation} />;
}}
/>;
Using useNavigation hooks (you need to change your MenuPostItem to the functional component to use useNavigation hook)
a minimal example
import * as React from "react";
import { Button } from "react-native";
import { useNavigation } from "#react-navigation/native";
function MenuPostItem() {
const navigation = useNavigation();
return (
<Button
title="go to post"
onPress={() => {
navigation.navigate("Post");
}}
/>
);
}
learn more about useNavigation
create a function for list items like below
const MenuPostItem = ({data, onPress}) = > {
return (
<ListItem button onPress={() => onPress('Post')}>
<Left>
{data.type === 'video' ? (
<Thumbnail
style={{ width: 150, height: 84, borderRadius: 0 }}
source={{uri: `http://` + hostname + `:5000/media/streamimage?src=${data.media.videothumbsrc}`}}
/>
) : (null)}
{data.type === 'text' ? (
<Thumbnail style={{ width: 150, height: 84, borderRadius: 0 }} />,
<Text style={{paddingLeft: 40, paddingRight: 40}}>Text Post</Text>
) : (null)}
</Left>
<Body>
<Text>{data.title}</Text>
<Text note numberOfLines={3}>{data.description}</Text>
</Body>
</ListItem>
)}
export default MenuPostItem;
and then in your MainPostLists.js file -
<List
dataArray={this.state.data.data}
renderRow={(item) => {
return <MenuPostItem data={item} navigation={this.props.navigation.navigate} />;
}}
/>;
The PhotosPage component below is rendered using
<Route path="/settings/photos" component={PhotosPage} />
The signature of the component is:
const PhotosPage = ({
uploadProfileImage,
photos,
profile,
deletePhoto,
setMainPhoto
})=>{}
However, there are two types of parameters that are used:
photos and profile are part of redux state
uploadProfileImage, deletePhoto, and setMainPhoto are imports
That are never passed in. Are these parameters passed in by react, or is this a javascript feature that I don't understand. Thanks.
import React, { useState, useEffect, Fragment } from "react";
import { connect } from "react-redux";
import { compose } from "redux";
import { firestoreConnect } from "react-redux-firebase";
import {
Segment,
Header,
Divider,
Grid,
Button
} from "semantic-ui-react";
import { toastr } from "react-redux-toastr";
import DropzoneInput from "./DropzoneInput";
import CropperInput from "./CropperInput";
import {
uploadProfileImage,
deletePhoto,
setMainPhoto
} from "../../userActions";
import UserPhotos from "./UserPhotos";
const query = ({ auth }) => {
return [
{
collection: "users",
doc: auth.uid,
subcollections: [{ collection: "photos" }],
storeAs: "photos"
}
];
};
const actions = {
uploadProfileImage,
deletePhoto
};
const mapState = state => ({
auth: state.firebase.auth,
profile: state.firebase.profile,
photos: state.firestore.ordered.photos
});
const PhotosPage = ({
uploadProfileImage,
photos,
profile,
deletePhoto,
setMainPhoto
}) => {
const [files, setFiles] = useState([]);
const [image, setImage] = useState(null);
useEffect(() => {
return () => {
files.forEach(file => URL.revokeObjectURL(file.preview));
};
}, [files]);
const handleUploadImage = async () => {
try {
await uploadProfileImage(image, files[0].name);
handleCancelCrop();
toastr.success("Success", "photo has been uploaded.");
} catch (error) {
toastr.error("Ooops", "something whent wrong");
console.log(error);
}
};
const handleCancelCrop = () => {
setFiles([]);
setImage(null);
};
const handleDeletePhoto = async photo => {
//try {
await deletePhoto(photo);
// } catch (error) {
// toastr.error("OOps", error.message);
// }
};
return (
<Segment>
<Header dividing size="large" content="Your Photos" />
<Grid>
<Grid.Row />
<Grid.Column width={4}>
<Header color="teal" sub content="Step 1 - Add Photo" />
<DropzoneInput setFiles={setFiles} />
</Grid.Column>
<Grid.Column width={1} />
<Grid.Column width={4}>
<Header sub color="teal" content="Step 2 - Resize image" />
{files.length > 0 && (
<CropperInput setImage={setImage} imagePreview={files[0].preview} />
)}
</Grid.Column>
<Grid.Column width={1} />
<Grid.Column width={4}>
<Header sub color="teal" content="Step 3 - Preview & Upload" />
{files.length > 0 && (
<>
<div
className="img-preview"
style={{
minHeight: "200px",
minWidth: "200px",
overflow: "hidden"
}}
/>
<Button.Group>
<Button
onClick={handleUploadImage}
style={{ width: "100px" }}
positive
icon="check"
/>
<Button
onClick={handleCancelCrop}
style={{ width: "100px" }}
icon="close"
/>
</Button.Group>
</>
)}
</Grid.Column>
</Grid>
<Divider />
<UserPhotos
photos={photos}
profile={profile}
deletePhoto={handleDeletePhoto}
/>
</Segment>
);
};
export default compose(
connect(
mapState,
actions
),
firestoreConnect(auth => query(auth))
)(PhotosPage);
When we wrap a component with 'Connect' this is then connected to our Redux store. We can then give it 2 functions - mapStateToProps (which maps our store state to the component props) and mapDispatchToProps - which we call actions - (that maps our store actions to our component). The actions become part of the props that we can call from the component.