Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Pasted multiple objects disappearing #10463

Open
7 tasks done
alexxGmZ opened this issue Mar 14, 2025 · 4 comments
Open
7 tasks done

[Bug]: Pasted multiple objects disappearing #10463

alexxGmZ opened this issue Mar 14, 2025 · 4 comments

Comments

@alexxGmZ
Copy link

CheckList

  • I agree to follow this project's Code of Conduct
  • I have read and followed the Contributing Guide
  • I have read and followed the Issue Tracker Guide
  • I have searched and referenced existing issues and discussions
  • I am filing a BUG report.
  • I have managed to reproduce the bug after upgrading to the latest version
  • I have created an accurate and minimal reproduction

Version

6.0.2

In What environments are you experiencing the problem?

Node.js, Chrome, Firefox

Node Version (if applicable)

22.4.0

Link To Reproduction

https://codesandbox.io/p/devbox/fabric-vanillajs-sandbox-forked-znnpd4?workspaceId=ws_NS6tC9GyuG2c2xiMvDGxkf

Steps To Reproduce

  1. Select multiple objects.
  2. Copy and paste.

Expected Behavior

The pasted objects are visible and interactive.

The bug doesn't exist in v5.3.0. I have copied the copy-paste demo from the website assuming that it is still viable.

Actual Behavior

2025-03-14.13-39-08.mp4

Error Message & Stack Trace

@asturur
Copy link
Member

asturur commented Mar 14, 2025

can you make it happen here:
https://fabricjs.com/demos/copy-and-paste/

And in case not can you guess the code differences that are making it happen?

@alexxGmZ
Copy link
Author

can you make it happen here:
https://fabricjs.com/demos/copy-and-paste/

That's the snippet I did use for v6.6.1.

let _clipboard;

/**
 * Copies the currently selected objects on the Fabric.js canvas to the
 * clipboard.
 *
 * @param {fabric.Canvas} canvas - The Fabric.js canvas instance from which
 * objects are copied.
 */
export function copyObjects(canvas) {
   if (!canvas) return;
   console.log("copyObjects()");
   canvas.getActiveObject().clone().then((cloned) => {
      _clipboard = cloned;
   });
}

/**
 * Pastes the objects from the clipboard onto the Fabric.js canvas.
 *
 * @param {fabric.Canvas} canvas - The Fabric.js canvas instance where the
 * objects will be pasted.
 */
export async function pasteObjects(canvas) {
   if (!canvas) return;
   console.log("pasteObjects()");

   const clonedObj = await _clipboard.clone();
   canvas.discardActiveObject();
   clonedObj.set({
      left: clonedObj.left + 10,
      top: clonedObj.top + 10,
      evented: true
   });

   if (clonedObj.type === "activeSelection") {
      clonedObj.canvas = canvas;
      clonedObj.forEachObject((object) => {
         canvas.add(object);
      });
      clonedObj.setCoords();
   }
   else {
      canvas.add(clonedObj);
   }

   _clipboard.top += 10;
   _clipboard.left += 10;
   canvas.setActiveObject(clonedObj);
   canvas.requestRenderAll();
}

The only difference is instead of if (clonedObj instanceof fabric.ActiveSelection), I did if (clonedObj.type === "activeSelection")

This is the snippet I used for v5.3.0.

let clipboard = null;

/**
 * Copies the currently selected objects on the Fabric.js canvas to the clipboard.
 *
 * @param {fabric.Canvas} canvas - The Fabric.js canvas instance from which objects
 * are copied.
 */
function copyObjects(canvas) {
   if (!canvas) return;

   console.log(`copyObjects(${canvas})`);
   canvas.getActiveObject().clone((cloned) => {
      clipboard = cloned;
      if (cloned.type === 'activeSelection') {
         cloned.forEachObject((obj) => {
            console.log(`Copied object - Type: ${obj.type}`);
         });
      }
      else console.log(`Copied object - Type: ${cloned.type}`);
   });
}

/**
 * Pastes the objects from the clipboard onto the Fabric.js canvas.
 *
 * @param {fabric.Canvas} canvas - The Fabric.js canvas instance where the objects will be pasted.
 * @param {number} pointerX - The x-coordinate for object placement if using the mouse.
 * @param {number} pointerY - The y-coordinate for object placement if using the mouse.
 */
function pasteObjects(canvas, pointerX, pointerY) {
   if (!canvas || !clipboard) return;
   console.log(`called pasteObjects(${canvas}, ${pointerX}, ${pointerY})`);

   clipboard.clone(function(clonedObj) {
      canvas.discardActiveObject();
      // if pointerX and pointerY has value
      if (pointerX && pointerY) {
         clonedObj.set({
            left: parseFloat(pointerX),
            top: parseFloat(pointerY),
            evented: true,
         });
      }

      // else below the original object
      else {
         clonedObj.set({
            left: clonedObj.left + 10,
            top: clonedObj.top + 10,
            evented: true,
         });
      }

      if (clonedObj.type === 'activeSelection') {
         // active selection needs a reference to the canvas.
         clonedObj.canvas = canvas;
         clonedObj.forEachObject((obj) => {
            canvas.add(obj);
            console.log(`Pasted object - Type: ${obj.type}`);
         });
         // this should solve the unselectability
         clonedObj.setCoords();
      }
      else {
         canvas.add(clonedObj);
         console.log(`Pasted object - Type: ${clonedObj.type}`);
      }

      clipboard.top += 10;
      clipboard.left += 10;
      canvas.setActiveObject(clonedObj);
      canvas.requestRenderAll();
   });
}

I've read the breaking change of clone() from using callback to promises.

@asturur
Copy link
Member

asturur commented Mar 14, 2025

I think the issue is that you are adding objects from the active selection right away in the canvas without removing them from the active selection first.
The codesandbox is returning 502 at the moment and i can't really use it.
Try something like:

  if (clonedObj.type === "activeSelection") {
    clonedObj.canvas = canvas;

    clonedObj.removeAll().forEach((object) => {
      canvas.add(object);
    });
  } else {
    canvas.add(clonedObj);
  }

and see if it makes a difference.

But i m not able to replicate your video on the official demo

@alexxGmZ
Copy link
Author

Still the same.

output.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants