Dragcontrol for a group object

Hi all,

There is a case:
I know it’s hard to drag a group of objects. So I make one object as the child of another, which means that objectParent.add(objectChildren).

However, the dragStart function always takes objectChilren as the target, not the objectParent.

Is there any suggestion?


Do you just have to drag a single group or possibly multiple groups in your scene? Besides, how do you organize the objects array which you pass into DragControls? I mean does the array hold only a single group object?

Thanks for replying!

The situation is that I created a mesh and add the edge to this mesh.

dragControls = new THREE.DragControls( drag_objects, camera, Renderer.domElement );

I use raycaster to update the drag_objects which can be dragged.

So the drag_objects is an array but just has one element. The element is a mesh that has an edge child.


I’ve noticed that multiple users in the past had this feature request. It’s actually easy to implement if only one root object is present in drag_objects. The approach does not work if more than one object is present in this array.

DragControls.js (6.2 KB)
DragControls.js JSM (6.2 KB)

Can you please test if these versions of DragControls work for you? The first one is the replacement for examples/js/controls/DragControls.js, the second one for examples/jsm/controls/DragControls.js.

When creating the controls, you have to set the new property transformRoot to true. Otherwise the controls transform individual objects.


I tried the new dragControls.js. It looks like it still not work.

Below is a little test for it:

Did I do something in the wrong way?


       var camera, scene, renderer, controls;
    var cubes = [];
    var drag_obj =[];
    var links = [];
    var dragControls;

    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000);
    camera.position.set(1, 1, 1).setLength(15);

    renderer = new THREE.WebGLRenderer({
      antialias: true
    renderer.setSize(window.innerWidth, window.innerHeight);
    controls = new THREE.OrbitControls(camera, renderer.domElement);
    var plane = new THREE.GridHelper(10,20);

    var cube1 = createCube("red");
    var cube2 = createCube("green");
    var cube3 = createCube("blue");
    //scene.matrixAutoupdate = false;

    function init() {
      dragControls = new THREE.DragControls(drag_obj, camera, renderer.domElement);
      dragControls.transformRoot = true;
      dragControls.addEventListener('dragstart', function(event) {
        controls.enabled = false;
      dragControls.addEventListener('dragend', function(event) {
        controls.enabled = true;

    function createCube(color) {
      var cubegeom = new THREE.BoxGeometry(1, 1, 1);
       var edgeGeo = new THREE.EdgesGeometry(cubegeom);
       let material = new THREE.LineBasicMaterial({
        color: color,
        linewidth: 1
    let lines_mesh = new THREE.LineSegments(edgeGeo, material);
      var cube = new THREE.Mesh(cubegeom, new THREE.MeshBasicMaterial({
        color: color
      return cube;
    var raycaster, mouse = { x : 0, y : 0 };
    raycaster = new THREE.Raycaster();
    window.addEventListener( 'mousedown', raycast, false );

    function setRandomPosition(obj) {
      obj.position.set(Math.random() * 10 - 5, Math.random() * 10 - 5, Math.random() * 10 - 5);

    function animate() {

    function render() {
      renderer.render(scene, camera);

    function raycast(e) {
        drag_obj = [];
        var canvasBounds = renderer.domElement.getBoundingClientRect();
        mouse.x = ( ( event.clientX - canvasBounds.left ) / ( canvasBounds.right - canvasBounds.left ) ) * 2 - 1;
        mouse.y = - ( ( event.clientY - canvasBounds.top ) / ( canvasBounds.bottom - canvasBounds.top) ) * 2 + 1;
        raycaster.setFromCamera( mouse, camera );    

         intersects = raycaster.intersectObjects( cubes );
        if(intersects.length > 0) {
                INTERSECTED = intersects[0].object;
                // }
            dragControls.removeEventListener('dragstart', function(event) {
                controls.enabled = false;
              dragControls.removeEventListener('dragend', function(event) {
                controls.enabled = true;

If you assign a new instance of Array in your raycast() function to drag_obj, then DragControls won’t notice this change. The controls only holds a reference to the initial array passed to the ctor.

So instead of drag_obj = [];, try drag_obj.length = 0;.

I check the dragControls from the console. The draggable object is one and will change according to the raycaster. However, the problem is that when I drag a mesh, the Edge and mesh will be dragged separately. Do you have any suggestion?

1. THREE.DragControls {enabled: true, transformRoot: true, activate: ƒ, deactivate: ƒ, dispose: ƒ, …}

  1. enabled: true
  2. activate: ƒ activate()

    1. length: 0
    2. name: "activate"
    3. arguments: null
    4. caller: null
    5. prototype: {constructor: ƒ}
    6. __proto__: ƒ ()
    7. [[FunctionLocation]]: DragControls.js:24
    8. [[Scopes]]: Scopes[2]

  3. deactivate: ƒ deactivate()

    1. length: 0
    2. name: "deactivate"
    3. arguments: null
    4. caller: null
    5. prototype: {constructor: ƒ}
    6. __proto__: ƒ ()
    7. [[FunctionLocation]]: DragControls.js:36
    8. [[Scopes]]: Scopes[2]

      1. 0: Closure (THREE.DragControls)

        1. _domElement: canvas
        2. _camera: pa {uuid: "DFABD332-600F-4089-AEE3-462CB8163EF9", name: "", type: "PerspectiveCamera", parent: null, children: Array(0), …}
        3. _objects: Array(1)

          1. 0: ca {uuid: "B3022352-5EDD-4F79-B4BC-E511AEE6563F", name: "", type: "Mesh", parent: pb, children: Array(1), …}
          2. length: 1
          3. __proto__: Array(0)

        4. _plane: Ta {normal: n, constant: -3.2861172706435395}
        5. _raycaster: mi {ray: Wb, near: 0, far: Infinity, camera: pa, params: {…}}
        6. _mouse: x {x: 0.971875, y: 0.02149437052200609}
        7. _offset: n {x: 0, y: 0, z: 0}

Can you provide a live example with your code? https://jsfiddle.net/f2Lommf5/

Debugging would make it easier to understand what’s going wrong.

The jsfiddle link is https://jsfiddle.net/allen_007/Ltqdy05z/4/

I noticed that the in dragStart function, the event.object is LineSegments, not the Mesh.

It seems to work with the updated DragControls I have provided earlier: https://jsfiddle.net/9exbnf2t/2/

Keep in mind that raycasting against lines always works with a tolerance value. Since this approach is an approximation, it’s not so exact like raycasting against meshes.

Besides, try to avoid to invoke init() multiple times. Only setup DragControls once.


It works!

Thank!! Your work is really impressive.

With R114, THREE.DragControls now supports dragging a single group object. The example demonstrates this feature:


If you select more than one object, you can now drag’n’drop the entire group.

The solution is not perfect since only a single group is supported right now. However, it’s a start and better than manually modifying THREE.DragControls :wink:.