Skip to content

feat(2d): add SVG component - #763

Merged
aarthificial merged 28 commits into
motion-canvas:mainfrom
levirs565:svg
Aug 22, 2023
Merged

feat(2d): add SVG component#763
aarthificial merged 28 commits into
motion-canvas:mainfrom
levirs565:svg

Conversation

@levirs565

@levirs565 levirs565 commented Jul 4, 2023

Copy link
Copy Markdown
Contributor

This will add SVG component. This component can render SVG and tween between two SVG.

This is part of add Latex component pull request.

Tweening Algorithm

SVG tweening algorithm support insertion, deletion and tranformation. This algorithm is based on ManimCommunity TransformMatchingShapes

This algorithm also support SVG that contain 2 or more element with same id. When number of element with some id in source and target do not match, all element will be transformed wihout insertion or deletion.

When number of element with some id in source is greater than number of element with that id then excess element will be transformed into target last element with that id.

[1, 2, 1, 3, 1] -> [1, 2, 3]

Second and third source element with id 1 will be transformed into first target element with id 1.

When number of element with some id in source is lower than number of element with that id then source last element with that id will be cloned and then transformed target element that index is matched.

[1, 2, 3] --> [1, 2, 1, 3, 1]

First source element with id 1 will cloned two time and the two item will transformed to second and third target element with id 1 respectively

This algorithm does not support moving item index. Why? Because moving item index cannot transformed/animated smoothly.

This algorithm will insert element based on previous element not index. Why? For simplicity. When, there are inserted element and element previous item is moved then the element will inserted after previous item.

[1, 2, 3] --> [1, 3, 5, 2]

result: [1,2,3,5]

5 is after 3, 5 is inserted after 3.

@levirs565
levirs565 requested a review from aarthificial as a code owner July 4, 2023 04:27
@levirs565

Copy link
Copy Markdown
Contributor Author

@aarthificial Are you accept the tweening algorithm? If not, please give me Suggestion about tweening algoritm

@Firnael

Firnael commented Jul 10, 2023

Copy link
Copy Markdown

Hi @levirs565 , with this final part merged, will LaTex tweening be finally possible ?
Thank you for your work here.

Comment thread packages/2d/src/components/SVG.ts
Comment thread packages/2d/src/utils/diff.ts Outdated
Comment thread packages/2d/src/components/SVG.ts Outdated
@levirs565

Copy link
Copy Markdown
Contributor Author

Hi @levirs565 , with this final part merged, will LaTex tweening be finally possible ? Thank you for your work here.

Yes, Latex tweening will be possible. How we insert new node when Tweening SVG is not affecting Latex tweening. Insertion order when tweening Latex is not important.

@levirs565

Copy link
Copy Markdown
Contributor Author

In my opinion, this pull request are ready

@ksassnowski ksassnowski left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When trying to tween between SVGs that use stroke-linecap="round" and/or stroke-linejoin="round", the line cap and line join get discarded during the tween, then suddenly get reapplied after the tween has finished.

Code to reproduce

import {makeScene2D} from '@motion-canvas/2d';
import {createRef} from '@motion-canvas/core';
import {SVG} from '@motion-canvas/2d/lib/components/SVG';

const icon1 = `
<svg width="128" height="128" fill="none" viewBox="0 0 24 24">
  <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M16.75 17.25H17.25C18.3546 17.25 19.25 16.3546 19.25 15.25V6.75C19.25 5.64543 18.3546 4.75 17.25 4.75H6.75C5.64543 4.75 4.75 5.64543 4.75 6.75V15.25C4.75 16.3546 5.64543 17.25 6.75 17.25H7.25"></path>
  <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 15.75L9 19.25H15L12 15.75Z"></path>
</svg>
`;

const icon2 = `
<svg width="128" height="128" fill="none" viewBox="0 0 24 24">
  <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4.75 8.75C4.75 7.64543 5.64543 6.75 6.75 6.75H15.25C16.3546 6.75 17.25 7.64543 17.25 8.75V15.25C17.25 16.3546 16.3546 17.25 15.25 17.25H6.75C5.64543 17.25 4.75 16.3546 4.75 15.25V8.75Z"></path>
  <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M17.75 10.75H18C18.6904 10.75 19.25 11.3096 19.25 12V12C19.25 12.6904 18.6904 13.25 18 13.25H17.75"></path>
</svg>
`;

export default makeScene2D(function* (view) {
  const svg = createRef<SVG>();

  view.add(<SVG ref={svg} svg={icon1} stroke={'white'} />);

  yield* svg().svg(icon2, 1);
});
svgtween.mp4

I'm not sure if this is something we can realistically fix, though.

Comment thread packages/2d/src/components/SVG.ts
Comment thread packages/2d/src/components/SVG.ts Outdated
Comment thread packages/2d/src/components/SVG.ts Outdated
Comment thread packages/2d/src/components/SVG.ts Outdated
@ksassnowski

Copy link
Copy Markdown
Contributor

I also think that the SVG component should use the node's size signal (if provided) to set the width and height of the actual SVG DOM node. From what I can tell, you have to explicitly change the width and height attributes of the actual SVG string if you want to make it smaller or larger at the moment.

@levirs565

Copy link
Copy Markdown
Contributor Author

When trying to tween between SVGs that use stroke-linecap="round" and/or stroke-linejoin="round", the line cap and line join get discarded during the tween, then suddenly get reapplied after the tween has finished.

Code to reproduce

import {makeScene2D} from '@motion-canvas/2d';
import {createRef} from '@motion-canvas/core';
import {SVG} from '@motion-canvas/2d/lib/components/SVG';

const icon1 = `
<svg width="128" height="128" fill="none" viewBox="0 0 24 24">
  <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M16.75 17.25H17.25C18.3546 17.25 19.25 16.3546 19.25 15.25V6.75C19.25 5.64543 18.3546 4.75 17.25 4.75H6.75C5.64543 4.75 4.75 5.64543 4.75 6.75V15.25C4.75 16.3546 5.64543 17.25 6.75 17.25H7.25"></path>
  <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 15.75L9 19.25H15L12 15.75Z"></path>
</svg>
`;

const icon2 = `
<svg width="128" height="128" fill="none" viewBox="0 0 24 24">
  <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4.75 8.75C4.75 7.64543 5.64543 6.75 6.75 6.75H15.25C16.3546 6.75 17.25 7.64543 17.25 8.75V15.25C17.25 16.3546 16.3546 17.25 15.25 17.25H6.75C5.64543 17.25 4.75 16.3546 4.75 15.25V8.75Z"></path>
  <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M17.75 10.75H18C18.6904 10.75 19.25 11.3096 19.25 12V12C19.25 12.6904 18.6904 13.25 18 13.25H17.75"></path>
</svg>
`;

export default makeScene2D(function* (view) {
  const svg = createRef<SVG>();

  view.add(<SVG ref={svg} svg={icon1} stroke={'white'} />);

  yield* svg().svg(icon2, 1);
});

svgtween.mp4
I'm not sure if this is something we can realistically fix, though.

The problem is not from SVG component but from Path component. To fix this issue you must make Path interpolation precision higher (lower number). By default path component interpolation precision is 5. When we set this to 1, this issue will fixed.

Currently, to change the precision we must edit createCurveProfileLerp.ts file. Below are place of the precision:

export function createCurveProfileLerp(a: CurveProfile, b: CurveProfile) {
  const interpolations = getInterpolationPolygon(a, b, 5, 4);
  // more
}

The precision is 5. Maybe, we must allow user to change the interpolation precision.

@levirs565

levirs565 commented Jul 23, 2023

Copy link
Copy Markdown
Contributor Author

I also think that the SVG component should use the node's size signal (if provided) to set the width and height of the actual SVG DOM node. From what I can tell, you have to explicitly change the width and height attributes of the actual SVG string if you want to make it smaller or larger at the moment.

Current available way to change width or height is to change SVG wrapper scale.

svg().wrapper.scale([2, 1]);

@levirs565

Copy link
Copy Markdown
Contributor Author

I also think that the SVG component should use the node's size signal (if provided) to set the width and height of the actual SVG DOM node. From what I can tell, you have to explicitly change the width and height attributes of the actual SVG string if you want to make it smaller or larger at the moment.

Now, we can change SVG size with width or height property. When only width or only height property are specified, unspecified property will follow specified property and SVG document actual aspect ratio.

@levirs565

Copy link
Copy Markdown
Contributor Author

Is there anything that needs to be fixed in this PR?

@levirs565
levirs565 requested a review from hhenrichsen August 19, 2023 14:10

@aarthificial aarthificial left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies for the delay.
I left some comments, mostly about the documentation.
I'd genuinely recommend using some free grammar checker because there's a lot of mistakes that could be easily picked up by such software.

Are you accept the tweening algorithm? If not, please give me Suggestion about tweening algoritm

The algorithm looks great 👍
The overall implementation also looks solid.

There are still some problems, for example:

  • tweening between two polygons doesn't seem to work.
  • percentage size is not supported (width={'50%'}, etc).

But I think we can fix them in future PRs to get this one done.

Comment thread packages/2d/src/components/SVG.ts Outdated
Comment thread packages/2d/src/components/SVG.ts Outdated
Comment thread packages/2d/src/components/SVG.ts Outdated
Comment thread packages/2d/src/components/SVG.ts Outdated
Comment thread packages/2d/src/components/SVG.ts Outdated
Comment thread packages/2d/src/components/SVG.ts Outdated
Comment thread packages/2d/src/components/SVG.ts Outdated
Comment thread packages/2d/src/components/SVG.ts Outdated
Comment thread packages/2d/src/components/SVG.ts Outdated
Comment thread packages/2d/src/components/SVG.ts
levirs565 and others added 7 commits August 22, 2023 14:29
@levirs565

levirs565 commented Aug 22, 2023

Copy link
Copy Markdown
Contributor Author

tweening between two polygons doesn't seem to work.

In order to support this feature, the Line component must support points tweening. This must implemented in other PR.

percentage size is not supported

When we can get calculated size, we can support percentage size. This can implemented in other PR. Latex tweening does not need this feature. We may use ratio property to support this feature.

After this PR is completed, I will open new PR for Latex tweening.

@aarthificial

Copy link
Copy Markdown
Contributor

In order to support this feature, the Line component must support points tweening. This must implemented in other PR.

Yes, but Line already has limited support for tweening (when arrays have the same size, for example), but the points property is not accounted for by the generateTransformer() method so even that doesn't work.

When we can get calculated size, we can support percentage size.

We'll never be able to get "calculated size" because the point of the desiredSize function is to provide the basis for layout calculations. If you try to get the calculated size at that point you'll create a circular dependency which I'm sure you've noticed already. Classes like Img and Video are good examples of using ratio to support percentage sizes.

@aarthificial
aarthificial merged commit 8eadc11 into motion-canvas:main Aug 22, 2023
@aarthificial

Copy link
Copy Markdown
Contributor

Anyway, thanks for the PR!

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

Successfully merging this pull request may close these issues.

5 participants