Skip to content

Commit

Permalink
feat: add support for click element selector to PointerLockControls (#…
Browse files Browse the repository at this point in the history
…285)

* feat: add support for click element selector to PointerLockControls

* refactor: simplify and extract internal properties like TransformControls

* docs: add a story for PointerLockControls with selector

* fix: minor TS issue & use normal setup directly in story

Co-authored-by: Josh Ellis <joshua.ellis18@gmail.com>
  • Loading branch information
jure and joshuaellis authored Feb 3, 2021
1 parent 683adaa commit ad42f1f
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 26 deletions.
64 changes: 47 additions & 17 deletions .storybook/stories/PointerLockControls.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
import * as React from 'react'
import { Vector3 } from 'three'
import { Canvas } from 'react-three-fiber'

import { Setup } from '../Setup'

import { PointerLockControls, Icosahedron } from '../../src'

export default {
title: 'Controls/PointerLockControls',
component: PointerLockControlsScene,
decorators: [
(storyFn) => (
<Setup cameraPosition={new Vector3(0, 0, 10)} controls={false}>
{storyFn()}
</Setup>
),
],
}

const NUM = 2
Expand All @@ -23,7 +17,7 @@ interface Positions {
position: [number, number, number]
}

function PointerLockControlsScene() {
function Icosahedrons() {
const positions = React.useMemo(() => {
const pos: Positions[] = []
const half = (NUM - 1) / 2
Expand All @@ -42,16 +36,24 @@ function PointerLockControlsScene() {
return pos
}, [])

return (
<group>
{positions.map(({ id, position }) => (
<Icosahedron key={id} args={[1, 1]} position={position}>
<meshBasicMaterial attach="material" color="white" wireframe />
</Icosahedron>
))}
</group>
)
}

function PointerLockControlsScene() {
return (
<>
<group>
{positions.map(({ id, position }) => (
<Icosahedron key={id} args={[1, 1]} position={position}>
<meshBasicMaterial attach="material" color="white" wireframe />
</Icosahedron>
))}
</group>
<PointerLockControls />
<Setup controls={false} camera={{ position: [0, 0, 10] }}>
<Icosahedrons />
<PointerLockControls />
</Setup>
</>
)
}
Expand All @@ -60,3 +62,31 @@ export const PointerLockControlsSceneSt = () => <PointerLockControlsScene />
PointerLockControlsSceneSt.story = {
name: 'Default',
}

function PointerLockControlsSceneWithSelector() {
return (
<>
<div
id="instructions"
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '2em',
background: 'white',
}}
>
Click here to play
</div>
<Setup controls={false} camera={{ position: [0, 0, 10] }}>
<Icosahedrons />
<PointerLockControls selector="#instructions" />
</Setup>
</>
)
}

export const PointerLockControlsSceneStWithSelector = () => <PointerLockControlsSceneWithSelector />
PointerLockControlsSceneStWithSelector.story = {
name: 'With selector',
}
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ If available controls have damping enabled by default, they manage their own upd

Drei currently exports OrbitControls [![](https://img.shields.io/badge/-codesandbox-blue)](https://codesandbox.io/s/r3f-contact-shadow-h5xcw), MapControls [![](https://img.shields.io/badge/-codesandbox-blue)](https://codesandbox.io/s/react-three-fiber-map-mkq8e), TrackballControls, FlyControls, DeviceOrientationControls, TransformControls [![](https://img.shields.io/badge/-codesandbox-blue)](https://codesandbox.io/s/r3f-drei-transformcontrols-hc8gm), PointerLockControls [![](https://img.shields.io/badge/-codesandbox-blue)](https://codesandbox.io/s/blissful-leaf-vkgi6)

PointerLockControls additionally supports a `selector` prop, which enables the binding of `click` event handlers for control activation to other elements than `document` (e.g. a 'Click here to play' button).

# Shapes

[Buffer-geometry](https://threejs.org/docs/index.html#api/en/core/BufferGeometry) short-cuts for Plane, Box, Sphere, Circle, Cone, Cylinder, Tube, Torus, TorusKnot, Ring, Tetrahedron, Polyhedron, Icosahedron, Octahedron, Dodecahedron, Extrude, Lathe, Parametric.
Expand Down
18 changes: 9 additions & 9 deletions src/core/PointerLockControls.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import * as React from 'react'
import { ReactThreeFiber, useThree, Overwrite } from 'react-three-fiber'
import { ReactThreeFiber, useThree } from 'react-three-fiber'
import { PointerLockControls as PointerLockControlsImpl } from 'three/examples/jsm/controls/PointerLockControls'
import useEffectfulState from '../helpers/useEffectfulState'

export type PointerLockControls = Overwrite<
ReactThreeFiber.Object3DNode<PointerLockControlsImpl, typeof PointerLockControlsImpl>,
{ target?: ReactThreeFiber.Vector3 }
>
export type PointerLockControls = ReactThreeFiber.Object3DNode<PointerLockControlsImpl, typeof PointerLockControlsImpl>

export const PointerLockControls = React.forwardRef((props: PointerLockControls, ref) => {
export type PointerLockControlsProps = PointerLockControls & { selector?: string }

export const PointerLockControls = React.forwardRef(({ selector, ...props }: PointerLockControlsProps, ref) => {
const { camera, gl, invalidate } = useThree()
const controls = useEffectfulState(
() => new PointerLockControlsImpl(camera, gl.domElement),
Expand All @@ -23,9 +22,10 @@ export const PointerLockControls = React.forwardRef((props: PointerLockControls,

React.useEffect(() => {
const handler = () => controls?.lock()
document.addEventListener('click', handler)
return () => document.removeEventListener('click', handler)
}, [controls])
const element = selector ? document.querySelector(selector) : document
element && element.addEventListener('click', handler)
return () => (element ? element.removeEventListener('click', handler) : undefined)
}, [controls, selector])

return controls ? <primitive dispose={undefined} object={controls} {...props} /> : null
})

0 comments on commit ad42f1f

Please sign in to comment.