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

fix: Fixed auto align to bounds bugs #486

Merged
merged 6 commits into from
Jul 3, 2024

Conversation

nuintun
Copy link
Contributor

@nuintun nuintun commented Jul 3, 2024

Fixed auto align to bounds bugs: #485 (comment)

auto-bug-fixed

@nuintun nuintun requested a review from prc5 as a code owner July 3, 2024 10:41
Copy link
Collaborator

@prc5 prc5 left a comment

Choose a reason for hiding this comment

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

Looks great, few adjustments and we can ship it!
Also please fix the typescript issues from the previous PR so it will be bundling again - and this way it will be possible to release it.

Comment on lines 210 to 217
frameId = requestAnimationFrame(() => {
const { scale } = this.transformState;

handleCancelAnimation(this);
handleCalculateBounds(this, scale);
handleAlignToBounds(this);
});
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this is too heavy for the animation frame and too hacky.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is some impact from the animation frame, but because of cancelAnimationFrame, the impact is limited.


handleCancelAnimation(this);
handleCalculateBounds(this, scale);
handleAlignToBounds(this);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would add the customTime parameter to handleAlignToBounds so it would be:

handleAlignToBounds(this, 0)

So the alignment will be instant - or controlled via new prop resizeAlignmentTime: number

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a very good implementation👍.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you add it here too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK,I will add this, but I think that normal mouse pinning should not delay alignment bounds; only resizing needs to.

handleAlignToBounds(this, 0) is not necessary.

Just needs:

clearTimeout(timerId);

timerId = setTimeout(() => {
  handleCancelAnimation(this);
  handleCalculateBounds(this, scale);
  handleAlignToBounds(this);
}, resizeAlignmentTime);

Copy link
Collaborator

Choose a reason for hiding this comment

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

There is no need for it to limit the mouse panning. Check this out:

export function handleAlignToBounds(
  contextInstance: ReactZoomPanPinchContext,
  // one addition here! it is still optional
  customAlignmentTime?: number
): void {
  const { scale } = contextInstance.state;
  const { minScale, autoAlignment } = contextInstance.setup;
  const { disabled, sizeX, sizeY, animationTime, animationType } =
    autoAlignment;

  const isDisabled = disabled || scale < minScale || (!sizeX && !sizeY);

  if (isDisabled) return;

  const targetState = handlePanToBounds(contextInstance);

  if (targetState) {
    animate(
       contextInstance, 
       targetState, 
       // and here! we only apply it for cases where customAlignment is needed
       customAlignmentTime ?? animationTime, 
       animationType
    );
  }
}

This way there is no need for the setTimeout 😄

Copy link
Collaborator

Choose a reason for hiding this comment

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

Btw. Thank you for your work, that's a great contribution! 🙌🏻

nuintun added 2 commits July 3, 2024 21:04

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
@nuintun
Copy link
Contributor Author

nuintun commented Jul 3, 2024

@prc5 Typescript issues fixed, and also fixed build tools warnings.

屏幕截图 2024-07-03 210841
屏幕截图 2024-07-03 211820

@@ -60,7 +60,7 @@ export class ZoomPanPinch {

public transformState: ReactZoomPanPinchState;
public setup: LibrarySetup;
public observer: ResizeObserver;
public observer!: ResizeObserver;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this is not a good solution for this case since it's not representing true state. We initialize observer with undefined value so in this case it is giving us the falsy assumption that it is always there by ! casting. You should adjust logic to handle observer being undefined value instead.

Like it was done here: https://github.com/BetterTyped/react-zoom-pan-pinch/pull/485/files#diff-e358387c9a984c08ecacfd998a21a99ffbe7156e0eca96588cedce311d5a65acL160

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because the observer already exists after initialization.
The code here does not have conditional branches like before: https://github.com/nuintun/react-zoom-pan-pinch/blob/0a2e348e1847e6bb31283104fd0b88fe1fdea796/src/core/instance.core.ts#L199

Copy link
Collaborator

Choose a reason for hiding this comment

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

The problem is that initialization is a method, not a constructor so it could be triggered at different times - for example, just after rendering.
So the lifecycle is like:

  1. We make instance new ReactZoomPanPinch()
  2. Reacts lifecycle kicks in and renders the content for x time
  3. Then we trigger initialize on useLayoutEffect - this is the time when wrapper and content is mounted to DOM

Between 1 and 3 there is some processing on the React's side which is taking unspecified amount of time based on the component you're building. During this time observer is undefined so this implementation is not truthy.

So my approach would be to keep it as close as possible to the real flow. Typescript must reflect the real world otherwise it is becoming a problem to handle later on.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK, I will fixed this.

Copy link
Collaborator

@prc5 prc5 left a comment

Choose a reason for hiding this comment

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

I'm still not convinced why we need the requestAnimationFrame instead of just calling the alignToBounds once. I cannot accept this as it may cause some uncontrolled memory leaks. Let's try to do it without it.

@nuintun
Copy link
Contributor Author

nuintun commented Jul 3, 2024

I'm still not convinced why we need the requestAnimationFrame instead of just calling the alignToBounds once. I cannot accept this as it may cause some uncontrolled memory leaks. Let's try to do it without it.

After having resizeAlignmentTime, requestAnimationFrame can be removed😁.

Using requestAnimationFrame here is just because I don’t want handleAlignToBounds to be triggered frequently, but I also want to make handleAlignToBounds trigger as soon as possible.

Copy link
Collaborator

@prc5 prc5 left a comment

Choose a reason for hiding this comment

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

Amazing work, thank you! 🚀

@prc5 prc5 merged commit 3643a47 into BetterTyped:master Jul 3, 2024
Copy link

🎉 This PR is included in version 3.7.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Successfully merging this pull request may close these issues.

None yet

2 participants