Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create matrix transformation system in React Native?

I have just built a relatively complex image editing UI in React Native.

The experience is intended to be quite similar to Instagram, and has pinch-to-zoom, pan, and rotate functionality.

Transformations are stored as data, eg:

transformation: {
  bottomBoundary: 1989,
  leftBoundary: 410,
  rightBoundary: 1634,
  topBoundary: 765,
  rotation: 0,
},

Wherein topBoundary and bottomBoundary are both offsets from the top of the image, and leftBoundary and rightBoundary are both offsets from the left of the image.

When rotating, because React Native uses the centre of objects as a transform origin, images need to be offset when in the 90° or 270° orientations, so that they are still "sticky" to the top/left corner and can be offset:

calculateRotationOffset.js

export default function (width, height) {
  const transform = [
    { rotate: `${this.rotation}deg` },
  ]

  /*
    RN rotates around centre point, so we need to
    manually offset the rotation to stick the image
    to the top left corner so that our offsets will
    work.
  */
  if (this.rotation === 90) {
    transform.push(
      { translateX: -((width - height) / 2) },
      { translateY: -((width - height) / 2) },
    )
  } else if (this.rotation === 270) {
    transform.push(
      { translateX: ((width - height) / 2) },
      { translateY: ((width - height) / 2) },
    )
  }
  return transform
}

Something about my rotation and zoom implementation are not working together.

Here is the complete snack example..

To reproduce the bug:

  1. Rotate the image once
  2. Pinch to zoom in or out
  3. Rotate the image again

The image will now be flipped and offset incorrectly.

There are a minimum of three transformations happening sequentially here to reproduce the bug, so I have not been able to track down the issue.

This question will definitely be bountied if necessary, and I might also offer a monetary reward. Thanks!

Update: After a few suggestions, it seems the best way to solve this problem is via matrix transformations, and then somehow convert this matrix back to styling that RN can represent onscreen. The matrix transformations need to support pan, rotate, and zoom.

like image 323
j_d Avatar asked Apr 24 '18 09:04

j_d


Video Answer


1 Answers

I'd replace the bottomBoundary etc. with a decomposed transformation matrix (rotation, scale, translation) and use multitouch in the UI. When sliding a single finger over the image, you can update the translation component of the matrix, and with 2 fingers transform it so that the image appears to stick to the fingers. If you want to constrain rotation to 90 degree increments, you can snap it when the user lets go. This allows you to move freely and intuitively within the image without requiring the current rotation buttons.

Storing the matrix always in decomposed form avoids having to decompose or normalize it to avoid accumulating numerical inaccuracy (skew and changing aspect ratio).

Creating a new DecomposedMatrix class would help structuring the code. The key is to handle transforming it roughly like so:

scaleBy(scale) {
    this.scale *= scale;
    this.x *= scale;
    this.y *= scale;
}
like image 171
jjrv Avatar answered Oct 19 '22 17:10

jjrv