fabricjs / fabric.js

Javascript Canvas Library, SVG-to-Canvas (& canvas-to-SVG) Parser
http://fabricjs.com
Other
29.07k stars 3.51k forks source link

`V6`: 🎉 Group Rewrite #7670

Closed ShaMan123 closed 6 months ago

ShaMan123 commented 2 years ago

V6

Hi everyone, fabric is moving forward! We have 2 main goals for v6:

  1. js/ts migration which is covered here #7596
  2. Group rewrite 🎉

You can see #8316

Group Rewrite

This is a tracking issue for the group rewrite, read below for more detail including the current state of things and gist. We aim to fix and support long desired features including:

  1. Nested selection - DONE
  2. Resizing and customized layout - DONE
  3. Transform and coordinate system (which is relative to the parent group) - DONE

Motivating Issues/Discussions

7473 https://github.com/fabricjs/fabric.js/pull/7316 https://github.com/fabricjs/fabric.js/issues/6776 https://github.com/fabricjs/fabric.js/discussions/7136 https://github.com/fabricjs/fabric.js/discussions/7299 https://github.com/fabricjs/fabric.js/issues/7130 https://github.com/fabricjs/fabric.js/issues/7142 #7449 and anything with a group tag

How do you fit in?

  1. You can become an alpha tester (we aim to publish an alpha tag to npm regularly). IMPORTANT NOTICE: all changes made to the v6 branch are experimental and can be removed or changed without notice and without proper support (including supported features from v5). fabric might change drastically between commits. Meaning if you want in - it's on you.

You can join the effort by contributing

This description is updated continuously with relevant information You don't need to scroll and read endless comments (unless you are a maintainer :))


Current State

Fixed logic across fabric for nested objects to be selectable. Group has been completely rewritten and Layer has been introduced. As part of the rewrite group is now a layout engine, you can use it to customize layout of objects. In general the rewrite allows any object to be selectable and interactive if it is referenced in the _objects array of it's parent and it references it's parent via the group property. If you build custom objects with nesting this is what you are looking for.

Usage

It is advised to use set on group/objects from now on (until #7596 is done, then it might change due to setters) Changing subTargetCheck or interactive enables/disables crucial functionality so for these props it is not an advice but a MUST.

new fabric.Group(objects, {
  subTargetCheck: true,
  interactive: true   //  enables selecting subtargets
});

Progress

Experimental

Triggering Layout

9148 extracts layout management to a standalone class

triggerLayout forces a group to recalculate its bounding box and reposition its objects. You can define a layout strategy and assign it to the layout manager. Read the code, it has comments explaining stuff and types that make it clear what you need to return from the method. Look at the modified trigger, it is possible adding more triggers. However I will promote an overall solution. #7882 is a POC, follow the progress trigger

BREAKING

Pitfalls

All object origin methods (e.g. translateToOriginPoint) are relative methods. You should use them ONLY in the correct plane and with points that belong to the relative plane. IMO it makes them worthless.

8767 changes that.

BUGS

If you encounter bugs related to group or if you have a feature request please submit a relevant ticket. Notice that bug reports of versions below v6 will be completely disregarded and closed. This was hard work, put effort into your repro so it is in v6.

Working Examples

Using v6! stale branch - CodeSandbox or JSFiddle - Layer or in JSFiddle

To Do

Look

Video is running in original speed tiger

Cutting Edge

The blue circles are rendered by a textbox and because of the rewrite with a few lines of code I got this working Pay no attention to caching of course

This is EXTREMELY exciting because it means that anyone could create custom objects subclassing whatever object they need and have nested selection work out of the box!

ezgif com-gif-maker (13)

abdussami-tayyab commented 1 year ago

@asturur can you help with my comment above? If I can make some objects in a group interactive and some not, that would be golden for the project I'm working on. If this is not in the plan for now, that would work as well if I can get to know. Thanks for this great package!

cc: @ShaMan123

ShaMan123 commented 1 year ago

Open a dedicated discussion/issue Provide a reproduction

asturur commented 1 year ago

@abdussami-tayyab link the open issue when you open it. The interactive group feature wasn't planned with all those customization in mind, so some of them needs to be evaluated, thought, and built from scratch.

abdussami-tayyab commented 1 year ago

@asturur do we have a boilerplate JSFiddle or CodeSandbox that I can use to replicate my issue? Sorry about commenting here, I understand it breaks the purpose of this thread. I'll delete once you tell me how I can add code to JSFiddle or CodeSandbox (or any other tool).

Thanks!

StringKe commented 11 months ago

Any update on the implementation of this DEMO in V6?

ShaMan123 commented 11 months ago

Are you referring to toGroup/toActiveSelection?

toActiveSelection

canvas.getActiveSelection().add(...group.removeAll())
//or
canvas.setActiveObject(new ActiveSelection(group.removeAll())
// remove group

toGroup

new Group(canvas.getActiveObjects())
StringKe commented 11 months ago

Are you referring to toGroup/toActiveSelection?

toActiveSelection

canvas.getActiveSelection().add(...group.removeAll())
//or
canvas.setActiveObject(new ActiveSelection(group.removeAll())
// remove group

toGroup

new Group(canvas.getActiveObjects())

Thanks, your code is very clean, I used a lot of code to achieve a similar result.

ShaMan123 commented 11 months ago

Thanks, your code is very clean, I used a lot of code to achieve a similar result.

Yes, v6 is a major step forward for the repo and the group rewrite especially

StringKe commented 11 months ago

"fabric": "^6.0.0-beta15" A lot of typescript types are incorrect in this version, in my local project there are up to 200 type checking errors in fabric!

Codesanbox

``` ✔ Browser application bundle generation complete. Error: node_modules/fabric/dist/src/filters/HueRotation.d.ts:14:22 - error TS2417: Class static side 'typeof HueRotation' incorrectly extends base class static side 'typeof ColorMatrix'. The types of 'defaults.mainParameter' are incompatible between these types. Type 'keyof HueRotation | undefined' is not assignable to type 'keyof ColorMatrix | undefined'. 14 export declare class HueRotation extends ColorMatrix { ~~~~~~~~~~~ Error: node_modules/fabric/dist/src/shapes/Path.d.ts:39:17 - error TS2300: Duplicate identifier 'path'. 39 constructor(path: TComplexPathData | string, { path, left, top, ...options }?: Partial); ~~~~ Error: node_modules/fabric/dist/src/shapes/Path.d.ts:39:52 - error TS2300: Duplicate identifier 'path'. 39 constructor(path: TComplexPathData | string, { path, left, top, ...options }?: Partial); ~~~~ ```

Codesanbox https://codesandbox.io/p/devbox/charming-williams-4qrkfz

Local angular 16.2.10

``` Error: node_modules/.pnpm/fabric@6.0.0-beta15/node_modules/fabric/src/Pattern/Pattern.ts:177:34 - error TS2339: Property 'width' does not exist on type 'CanvasImageSource'. Property 'width' does not exist on type 'VideoFrame'. 177 : ifNaN((patternSource.width as number) / width, 0), ~~~~~ Error: node_modules/.pnpm/fabric@6.0.0-beta15/node_modules/fabric/src/Pattern/Pattern.ts:181:34 - error TS2339: Property 'height' does not exist on type 'CanvasImageSource'. Property 'height' does not exist on type 'VideoFrame'. 181 : ifNaN((patternSource.height as number) / height, 0); ~~~~~~ Error: node_modules/.pnpm/fabric@6.0.0-beta15/node_modules/fabric/src/Pattern/Pattern.ts:185:50 - error TS2339: Property 'width' does not exist on type 'CanvasImageSource'. Property 'width' does not exist on type 'VideoFrame'. 185 `

abdussami-tayyab commented 11 months ago

Can someone please add how to setup using ReactJS? It would be very important and won’t take much time.

On Mon, 27 Nov 2023 at 8:44 PM, StringKE @.***> wrote:

"fabric": "^6.0.0-beta15" A lot of typescript types are incorrect in this version, in my local project there are up to 200 type checking errors in fabric! Codesanbox

✔ Browser application bundle generation complete.

Error: node_modules/fabric/dist/src/filters/HueRotation.d.ts:14:22 - error TS2417: Class static side 'typeof HueRotation' incorrectly extends base class static side 'typeof ColorMatrix'. The types of 'defaults.mainParameter' are incompatible between these types. Type 'keyof HueRotation | undefined' is not assignable to type 'keyof ColorMatrix | undefined'.

14 export declare class HueRotation extends ColorMatrix {


Error: node_modules/fabric/dist/src/shapes/Path.d.ts:39:17 - error TS2300: Duplicate identifier 'path'.

39     constructor(path: TComplexPathData | string, { path, left, top, ...options }?: Partial<Props>);

Error: node_modules/fabric/dist/src/shapes/Path.d.ts:39:52 - error TS2300: Duplicate identifier 'path'.

39 constructor(path: TComplexPathData | string, { path, left, top, ...options }?: Partial);


Codesanbox https://codesandbox.io/p/devbox/charming-williams-4qrkfz
Local angular 16.2.10

Error: ***@***.***/node_modules/fabric/src/Pattern/Pattern.ts:177:34 - error TS2339: Property 'width' does not exist on type 'CanvasImageSource'.
  Property 'width' does not exist on type 'VideoFrame'.

177           : ifNaN((patternSource.width as number) / width, 0),

Error: @.***/node_modules/fabric/src/Pattern/Pattern.ts:181:34 - error TS2339: Property 'height' does not exist on type 'CanvasImageSource'. Property 'height' does not exist on type 'VideoFrame'.

181 : ifNaN((patternSource.height as number) / height, 0);


Error: ***@***.***/node_modules/fabric/src/Pattern/Pattern.ts:185:50 - error TS2339: Property 'width' does not exist on type 'CanvasImageSource'.
  Property 'width' does not exist on type 'VideoFrame'.

185       `<image x="0" y="0" width="${patternSource.width}" height="${

Error: @.***/node_modules/fabric/src/Pattern/Pattern.ts:186:23 - error TS2339: Property 'height' does not exist on type 'CanvasImageSource'. Property 'height' does not exist on type 'VideoFrame'.

186 patternSource.height


Error: ***@***.***/node_modules/fabric/src/canvas/Canvas.ts:112:11 - error TS2564: Property '_isClick' has no initializer and is not definitely assigned in the constructor.

112   private _isClick: boolean;

Error: @.***/node_modules/fabric/src/canvas/Canvas.ts:1599:3 - error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'SelectableCanvas'.

1599 clear() {


Error: ***@***.***/node_modules/fabric/src/canvas/Canvas.ts:1607:3 - error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'SelectableCanvas<CanvasEvents>'.

1607   destroy() {

Error: @.***/node_modules/fabric/src/canvas/DOMManagers/CanvasDOMManager.ts:92:3 - error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'StaticCanvasDOMManager'.

92 setDimensions(size: TSize, retinaScaling: number) {


Error: ***@***.***/node_modules/fabric/src/canvas/DOMManagers/CanvasDOMManager.ts:98:3 - error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'StaticCanvasDOMManager'.

98   setCSSDimensions(size: Partial<CSSDimensions>): void {

Error: @.***/node_modules/fabric/src/canvas/DOMManagers/CanvasDOMManager.ts:104:3 - error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'StaticCanvasDOMManager'.

104 cleanupDOM(size: TSize) {


Error: ***@***.***/node_modules/fabric/src/canvas/DOMManagers/CanvasDOMManager.ts:116:3 - error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'StaticCanvasDOMManager'.

116   dispose() {

Error: @.***/node_modules/fabric/src/canvas/SelectableCanvas.ts:274:10 - error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'StaticCanvas'.

274 static ownDefaults: Record<string, any> = canvasDefaults;



Error: ***@***.***/node_modules/fabric/src/canvas/SelectableCanvas.ts:276:10 - error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'StaticCanvas<EventSpec>'.

—
Reply to this email directly, view it on GitHub
<https://github.com/fabricjs/fabric.js/issues/7670#issuecomment-1828085209>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABW3JIXTDGKGJC4E5YTKSNTYGSYORAVCNFSM5OAWEAH2U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCOBSHAYDQNJSGA4Q>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
StringKe commented 11 months ago

Can someone please add how to setup using ReactJS? It would be very important and won’t take much time.

How much does this have to do with react?

angular tries to detect types in node_module.

If the project uses vite's react + typescript templates these issues do not occur.

If a project uses nx.dev for react and is packaged with vite, it will have type issues. nx seems to continue to do some sort of type checking after the vite compilation. nx has a configuration skipTypeCheck

https://nx.dev/nx-api/vite/executors/build#skiptypecheck

ShaMan123 commented 11 months ago

Guys I am hiding your last off topic comments Be strict about the discussions and open a dedicated ticket or else everything becomes spam. We have starter apps, read the README/CONTRIBUTING guides.

ShaMan123 commented 6 months ago

Group rewrite has completed a while back and the LayoutManager has been introduced.

8034 hasn't been merged so look out when adding objects to a group that belonged to a canvas. Call Canvas#remove to be sure until that is fixed. The rest of the add operations should manage removing objects from previous group for you.

The parent ref has been introduced to complement the existing group ref. Basically the group ref is what defines the plane the object is in while the parent ref describe where the object is in the object tree. When selected as part of an ActiveSelection, a nested object (one under some group n levels) will have its group ref set to the active selection and its parent ref set to the group it came from. When not selected both refs point to the group. This is needed for a couple of things including the ability to move the object back to its parent after deselected and rendering it in the case of preserveObjectStacking.

There are pending issues with the transform system and the caching system that will cause bugs when using groups such as:

8743 This bug is caused because fabric uses decomposed values instead of pure matrix multiplication.

Look out for bugs coming from a combination of shadows and caching/clipPaths.

I suggest anyone needing nested objects and groups to familiarize with sendPointToPlane, sendVectorToPlane and sendObjectToPlane. They do the heavy up lifting of the linear algebra for you. Once you understand how they work it becomes easy to do complex linear algebra in your code. Be mindful of the plane you are working in, whether it is the viewport, the scene, the parent plane, the object plane, some other internal object plane (fabric defines many of these but none are easy to grasp, such as pathOffset) or weirder ones. Doing so will save dev hours and make your logic robust for nesting.