Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions apps/cientos-docs/app/components/controls/DragControls.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<!-- eslint-disable no-console -->
<script setup lang="ts">
import { shallowRef, watch } from 'vue'
import { DragControls } from '@tresjs/cientos'
import { TresCanvas } from '@tresjs/core'
import { NoToneMapping, SRGBColorSpace } from 'three'
import type { DragControls as ThreeDragControls } from 'three/examples/jsm/Addons.js'
import { useControls } from '@tresjs/leches'

const gl = {
clearColor: '#82DBC5',
alpha: false,
outputColorSpace: SRGBColorSpace,
toneMapping: NoToneMapping,
}

const uuid = inject(`uuid`)
const boxRef = shallowRef()
const sphereRef = shallowRef()

const { lock, enabled, dragLimitsMin, dragLimitsMax } = useControls({
lock: {
options: ['x', 'y', 'z', 'none'],
value: 'none',
},
enabled: true,
dragLimitsMax: {
value: 5,
min: 1,
max: 7,
step: 1,
},
dragLimitsMin: {
value: -5,
min: -7,
max: -1,
step: 1,
},
}, { uuid })

function onDrag(_e: ThreeDragControls) {
if (!boxRef.value) { return }
const snap = 0.5
const pos = boxRef.value.position
pos.x = Math.round(pos.x / snap) * snap
pos.z = Math.round(pos.z / snap) * snap
}
</script>

<template>
<TresCanvas v-bind="gl">
<TresPerspectiveCamera :position="[0, 7.5, 7.5]" :look-at="[0, 0, 0]" />
<TresMesh ref="boxRef" :position="[0.5, 0.5, 0.5]">
<TresBoxGeometry />
<TresMeshStandardMaterial />
</TresMesh>
<TresMesh ref="sphereRef" :position="[4, 0, 0]">
<TresSphereGeometry />
<TresMeshStandardMaterial />
</TresMesh>
<TresDirectionalLight :position="[0, 10, 0]" :intensity="1.5" />
<TresAmbientLight :intensity="0.25" />
<DragControls
:objects="[sphereRef]"
:lock="lock"
:enabled="enabled"
:dragLimits="[[dragLimitsMin, dragLimitsMax], [dragLimitsMin, dragLimitsMax], [dragLimitsMin, dragLimitsMax]]"
/>
<DragControls
:objects="[boxRef]"
:drag-limits="[[-4.5, 4.5], undefined, [-4.5, 4.5]]"
lock="y"
@drag="onDrag"
/>
<TresGridHelper :args="[10, 20]" />
</TresCanvas>
</template>
92 changes: 92 additions & 0 deletions apps/cientos-docs/content/2.api/2.controls/drag-controls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
title: Drag Controls
description: Drag and drop 3D objects in your scene with pointer events
---

The [Drag Controls](https://threejs.org/docs/#examples/en/controls/DragControls) allow you to drag and move 3D objects in your scene using pointer events. You can optionally lock movement to a single axis, set drag limits per axis, and listen to drag lifecycle events.

::SceneControlsWrapper
::ControlsDragControls
::
::

## Usage

The `objects` prop accepts an array of `Object3D` instances. You can pass template refs directly — Vue will unwrap them automatically.

```vue{3}
<script setup>
import { shallowRef } from 'vue'
import { DragControls } from '@tresjs/cientos'

const boxRef = shallowRef()
</script>

<template>
<TresCanvas>
<TresPerspectiveCamera :position="[0, 7.5, 7.5]" :look-at="[0, 0, 0]" />
<DragControls :objects="[boxRef]" />
<TresMesh ref="boxRef">
<TresBoxGeometry />
<TresMeshStandardMaterial />
</TresMesh>
</TresCanvas>
</template>
```

::prose-warning
If you are using <span style="background-color:#222;padding:0.25rem;border-radius:4px;"><a href="/api/controls/orbit-controls" style="color:#f7f7f7;text-decoration:none">OrbitControls</a></span> alongside DragControls, they will interfere with each other. Set `make-default` on **OrbitControls** to prevent conflicts while dragging.
::

## Props

| Prop | Description | Default |
| :------------- | :------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
| **objects** | Array of [Object3D](https://threejs.org/docs/index.html#api/en/core/Object3D) instances to make draggable. | `[]` |
| **camera** | The camera used for raycasting. Defaults to the scene's active camera. | `undefined` |
| **enabled** | If `false`, dragging is disabled and the object snaps back to its position on each drag event. | `true` |
| **lock** | Locks movement along one axis. Can be `'x'`, `'y'`, `'z'`, or `'none'`. | `'none'` |
| **dragLimits** | Per-axis position limits as `[[xMin, xMax], [yMin, yMax] \| undefined, [zMin, zMax] \| undefined]`. Pass `undefined` for axes with no limit. | `undefined` |
| **domElement** | The DOM element that listens for pointer events. | `undefined` |

## Events

| Event | Description | Payload |
| :------------ | :---------------------------------------------------- | :------------------ |
| **dragstart** | Fired when the user starts dragging an object. | `ThreeDragControls` |
| **drag** | Fired every frame while an object is being dragged. | `ThreeDragControls` |
| **dragend** | Fired when the user releases a dragged object. | `ThreeDragControls` |
| **hoveron** | Fired when the pointer moves over a draggable object. | `ThreeDragControls` |
| **hoveroff** | Fired when the pointer leaves a draggable object. | `ThreeDragControls` |

## Tip
### Grid snapping

There is no built-in snap prop, but you can implement snapping in the `@drag` event handler by rounding the object's position to the desired interval:

```vue
<script setup>
import { shallowRef } from 'vue'
import { DragControls } from '@tresjs/cientos'

const boxRef = shallowRef()

function onDrag() {
if (!boxRef.value) { return }
const snap = 0.5
boxRef.value.position.x = Math.round(boxRef.value.position.x / snap) * snap
boxRef.value.position.z = Math.round(boxRef.value.position.z / snap) * snap
}
</script>

<template>
<TresCanvas>
<TresPerspectiveCamera :position="[0, 7.5, 7.5]" :look-at="[0, 0, 0]" />
<DragControls :objects="[boxRef]" lock="y" @drag="onDrag" />
<TresMesh ref="boxRef">
<TresBoxGeometry />
<TresMeshStandardMaterial />
</TresMesh>
</TresCanvas>
</template>
```
99 changes: 99 additions & 0 deletions apps/playground/src/pages/cientos/controls/DragControlsDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<!-- eslint-disable no-console -->
<script setup lang="ts">
import { shallowRef, watch } from 'vue'
import { DragControls } from '@tresjs/cientos'
import { TresCanvas } from '@tresjs/core'
import { NoToneMapping, SRGBColorSpace } from 'three'
import type { DragControls as ThreeDragControls } from 'three/examples/jsm/Addons.js'
import { TresLeches, useControls } from '@tresjs/leches'

const gl = {
clearColor: '#82DBC5',
alpha: false,
outputColorSpace: SRGBColorSpace,
toneMapping: NoToneMapping,
}

const boxRef = shallowRef()
const sphereRef = shallowRef()
const controlsRef = shallowRef<InstanceType<typeof DragControls> | null>(null)

const { lock, enabled, dragLimitsMin, dragLimitsMax } = useControls({
lock: {
options: ['x', 'y', 'z', 'none'],
value: 'none',
},
enabled: true,
dragLimitsMax: {
value: 3,
min: 1,
max: 5,
step: 1,
},
dragLimitsMin: {
value: -3,
min: -5,
max: -1,
step: 1,
},
})

function onDragStart(e: ThreeDragControls) {
console.log('dragstart', e)
}

function onDrag(_e: ThreeDragControls) {
if (!boxRef.value) { return }
const snap = 0.5
const pos = boxRef.value.position
pos.x = Math.round(pos.x / snap) * snap
pos.z = Math.round(pos.z / snap) * snap
}

function onDragEnd(e: ThreeDragControls) {
console.log('dragend', e)
}

function onHoverOn(e: ThreeDragControls) {
console.log('hoveron', e)
}

function onHoverOff(e: ThreeDragControls) {
console.log('hoveroff', e)
}
</script>

<template>
<TresLeches />
<TresCanvas v-bind="gl">
<TresPerspectiveCamera :position="[0, 7.5, 7.5]" :look-at="[0, 0, 0]" />
<TresMesh ref="boxRef" :position="[0.5, 0.5, 0.5]">
<TresBoxGeometry />
<TresMeshStandardMaterial />
</TresMesh>
<TresMesh ref="sphereRef" :position="[4, 0, 0]">
<TresSphereGeometry />
<TresMeshStandardMaterial />
</TresMesh>
<TresDirectionalLight :position="[0, 10, 0]" :intensity="1.5" />
<TresAmbientLight :intensity="0.25" />
<DragControls
ref="controlsRef"
:objects="[sphereRef]"
:lock="lock"
:enabled="enabled"
:dragLimits="[[dragLimitsMin, dragLimitsMax], [dragLimitsMin, dragLimitsMax], [dragLimitsMin, dragLimitsMax]]"
@dragstart="onDragStart"
@dragend="onDragEnd"
@hoveron="onHoverOn"
@hoveroff="onHoverOff"
/>
<DragControls
:objects="[boxRef]"
:drag-limits="[[-4.5, 4.5], undefined, [-4.5, 4.5]]"
lock="y"
@drag="onDrag"
/>
<TresGridHelper :args="[10, 20]" />
</TresCanvas>
</template>
5 changes: 5 additions & 0 deletions apps/playground/src/router/routes/cientos/controls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,9 @@ export const controlsRoutes = [
name: 'Helper',
component: () => import('@/pages/cientos/controls/HelperDemo.vue'),
},
{
path: '/cientos/controls/drag-controls',
name: 'DragControls',
component: () => import('@/pages/cientos/controls/DragControlsDemo.vue'),
},
]
Loading
Loading