Reputation: 61
Can anyone help me get this line to show up? Im using @react-three/fiber and Typescript
My Faulty Component:
import * as THREE from 'three'
const RoadLine = ({start, end}: {start: THREE.Vector3, end: THREE.Vector3}) => {
return (
<line>
<bufferGeometry setFromPoints={() => new THREE.BufferGeometry().setFromPoints([start, end])}/>
<lineBasicMaterial color={'green'}/>
</line>
)
}
export default RoadLine
------------------------------------------------------------------------------------------
The Props Im sending in:
{roads?.map(road => {
return (
<RoadLine
key={road.id}
start={new THREE.Vector3(1,0,3)} //numbers just for test atm
end={new THREE.Vector3(11,0,33)} //numbers just for test atm
/>
)
})}
Im trying to make a basic line from point a to b essentially, not sure if I'm going about it correctly
Upvotes: 4
Views: 9530
Reputation: 782
Both answers contain known anti-patterns that should be avoided.
drei
and three.js
code base.start
and end
. These two values are lists. React does shallow comparison, so this often leads to re-render when these two variables are re-constructed, even though they contain the same value.map
to instantiate the points. This is not a good style unless you are trying to accommodate more than two pointsInstead of implementing your own, I recommend using the Line
component from drei
. they use instancing to improve performance, and accommodate multiple segments. See below:
import {
Line,
} from "@react-three/drei";
export Example = ({cx, cy, distance}) => (
<Line
points={[ // Array of points, Array<Vector3
[-cx, -cy, -distance], // | Vector2 | [number, number, number]
[cx, -cy, -distance], // | [number, number] | number>
[cx, cy, -distance],
[-cx, cy, -distance],
[-cx, -cy, -distance],
]}
color={"#23aaff"} // Default
lineWidth={1} // In pixels (default)
// segments // If true, renders a THREE.LineSegments2. Otherwise, renders a THREE.Line2
/>
)
Upvotes: 5
Reputation: 19
our friend's answer almost works, but for the typescript some tweaking is needed:
import * as THREE from 'three'
import React, { useRef } from 'react'
import { Canvas, useFrame } from '@react-three/fiber'
type props = {
start: number[],
end: number[]
}
function Line (props: props) {
const ref = useRef<THREE.Line>()
useFrame(() => {
if(ref.current){
ref.current.geometry.setFromPoints([props.start, props.end].map((point) => new THREE.Vector3(...point)));
}
})
return (
<line ref={ref}>
<bufferGeometry />
<lineBasicMaterial color="hotpink"/>
</line>
)
}
export default function App() {
return (
<Canvas>
<ambientLight intensity={0.5} />
<spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} />
<pointLight position={[-10, -10, -10]} />
<Line start={[0,0,0]} end={[1,1,0]} />
<Line start={[1,1,0]} end={[2,0,0]} />
</Canvas>
)
}
Upvotes: 1
Reputation: 79
I was struggling with this until I came across this response from Paul Henschel.
It becomes easier if you don't think of [line] as a wrapper or an abstraction. the jsx you write is threejs. if you write a line in threejs you can make it in jsx. but you're mixing up props and functions. THREE.BufferGeometry.setPoints is a function, and you overwrite it. in react/jsx does not call onClick, it sets it. you can't call a function declaratively, you do that as a side effect in either useEffect or useLayoutEffect. you use the latter if you want to call your function before the thing renders on screen.
He linked this codesandbox, I will post the code here as well. https://codesandbox.io/s/basic-demo-forked-e46t9?file=/src/App.js
import * as THREE from 'three'
import React, { useLayoutEffect, useRef } from 'react'
import { Canvas } from '@react-three/fiber'
function Line({ start, end }) {
const ref = useRef()
useLayoutEffect(() => {
ref.current.geometry.setFromPoints([start, end].map((point) => new THREE.Vector3(...point)))
}, [start, end])
return (
<line ref={ref}>
<bufferGeometry />
<lineBasicMaterial color="hotpink" />
</line>
)
}
export default function App() {
return (
<Canvas>
<ambientLight intensity={0.5} />
<spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} />
<pointLight position={[-10, -10, -10]} />
<Line start={[0, 0, 0]} end={[1, 0, 0]} />
<Line start={[1, 0, 0]} end={[1, 1, 0]} />
</Canvas>
)
}
Upvotes: 7