transformControls causes frame drops

I’m working on something like a threejs editor, and when I add objects (no matter how simple the objects are), when I drag the transformControls, the 60fps drops to 44fps, but if I don’t add any operations, there won’t be much fluctuation when operating the objects in the original scene. I don’t quite understand why

Here is the code:

 transformControls.addEventListener('objectChange', () => {
      this.signals.objectChanged.emit('', transformControls.object);
    });
    transformControls.addEventListener('mouseDown', () => {
      const object = transformControls.object;
      objectPositionOnDown = object.position.clone();
      objectRotationOnDown = object.rotation.clone();
      objectScaleOnDown = object.scale.clone();
      this.controls.enabled = false;

    });
    // 当鼠标抬起去发送请求
    transformControls.addEventListener('mouseUp', () => {
      const {conf} = storeToRefs(useBusinessData());
      const object = transformControls.object;
      const keys = object.allPathKey.split('.');
      const lastKey = keys.pop();
      const lastObj = keys.reduce((acc: any, key: any) => acc?.[key], conf.value);
      if (object !== undefined) {


        switch (transformControls.getMode()) {

          case 'translate':
            if (!objectPositionOnDown.equals(object.position)) {
              this.editor.execute(new SetPositionCommand(this.editor, object, object.position, objectPositionOnDown));
              if (lastObj && lastKey) {
                // 需要修改的响应值
                const reflectObj = lastObj[lastKey];
                this.editor.wsSend(
                  'change',
                  [{
                    value: {
                      ...reflectObj,
                    },
                    key: object.allPathKey,
                  }]
                );
              }

            }
            break;
          case 'rotate':
            if (!objectRotationOnDown.equals(object.rotation)) {
              this.editor.execute(new SetRotationCommand(this.editor, object, object.rotation, objectPositionOnDown));
              if (lastObj && lastKey) {
                const reflectObj = lastObj[lastKey];
                this.editor.wsSend(
                  'change',
                  [{
                    value: {
                      ...reflectObj,
                    },
                    key: object.allPathKey,
                  }]
                );
              }

            }
            break;
          case 'scale':
            if (!objectScaleOnDown.equals(object.scale)) {
              this.editor.execute(new SetScaleCommand(this.editor, object, object.scale, objectPositionOnDown));
              if (lastObj && lastKey) {
                const reflectObj = lastObj[lastKey];
                this.editor.wsSend(
                  'change',
                  [{
                    value: {
                      ...reflectObj,
                    },
                    key: object.allPathKey,
                  }]
                );
              }
            }
            break;
        }
        // 恢复相机控制移动
        this.controls.enabled = true;

      }
    });

And I did a performance analysis ,here are the pictures

It confuses me.

The mouseDown and mouseUp events should be fine since they’re only triggered once. Do you also have a mouseMove event?

As a rule of thumb, avoid creating new objects in performance-critical methods (e.g., object.position.clone(); creates a new object).

Also, consider throttling your mouse events.

1 Like

There is no mousemove event.
only transformControls triggers objectchange and I just distribute it.

transformControls.addEventListener('objectChange', () => {
      this.signals.objectChanged.emit('', transformControls.object);
});

The strange thing is that objects added after the operation will cause lag, but objects created during the init phase will not.

Creating new Vector3 and Euler instances shouldn’t cause this much lag, but initializing them once and reuse them, like the following, is more efficient:

const objectPositionOnDown = new Vector3();
const objectRotationOnDown = new Euler();
const objectScaleOnDown = new Vector3();

transformControls.addEventListener('mouseDown', () => {
    const object = transformControls.object;
    objectPositionOnDown.copy(object.position);
    objectRotationOnDown.copy(object.rotation);
    objectScaleOnDown.copy(object.scale);
    this.controls.enabled = false;
});

Since the specifics of this.editor.execute, new SetRotationCommand, SetScaleCommand…, aren’t fully clear, pinpointing the exact source of the performance issue is tricky.

First, you combined pointerEvents with mouseEvents. These treat ux timing differently and on different devices. Second, transform can sort in space with irregular shapes. You should flag an active transform and return early from duplicate listeners.

Apologies for Belated Correspondence,
ABC “Batch Hash” 8Skii

Thanks for your suggestion! These codes are actually the source code of threejs editor. You can view the viewport.js file at the following url

Thanks for your suggestion! These are the source codes of threejs editor. The only difference between mine and his is that I keep calling render in requestAnimation, while threejs editor will call render manually if there is a view update.

here is the video


Although it is not clear, you can see that it is particularly stuck after adding the object. But after I refresh the page, dragging the object becomes normal

I haven’t fully gone through your code, but from a quick look, it seems signals.objectChanged and render() are called multiple times per frame. Even just two calls per frame can introduce noticeable lag.

To mitigate this, consider the follwing:

  • Adding debouncing or throttling to mouse and objectChange events to reduce the frequency of updates.
  • Ensure render() is only called once per frame (per requestAnimationFrame) by implementing a queue system, debouncing, or a simple but effective boolean flag (like rendered) that resets once per frame/RAF cycle.
1 Like

Thanks for your advice, appreciate the help

1 Like

i find some useful things


why pointerdown trigger so many times??

It seems like there might be multiple instances of TransformControls in your code.

Try adding a console.log where you’re creating new TransformControls to check how often it’s called. This can help identify if unnecessary instances are being created.

Typically, you only need one TransformControls per scene or app. You can manage it using attach, detach, enabled, and visible options, and be sure to call controls.dispose() when you’re done with any instance.

1 Like

just init onece