-
Notifications
You must be signed in to change notification settings - Fork 8
Description
Specifically, this bit right here when computing the new offset after accounting for zooming and panning:
// For natural zooming and rotating, the centroid of the gesture should
// be the fixed point where zooming and rotating occurs.
// We compute where the centroid was (in the pre-transformed coordinate
// space), and then compute where it will be after this delta.
// We then compute what the new offset should be to keep the centroid
// visually stationary for rotating and zooming, and also apply the pan.
offset = (offset + gestureCentroid / oldScale).rotateBy(gestureRotate) -
(gestureCentroid / newScale + gesturePan / oldScale)gestureCentroid uses a coordinate system where (0,0) is the top-left corner of the screen, but zooming uses a coordinate system where (0,0) is the center of the screen. As a result, all zooming gestures are biased towards the lower-right corner of the screen.
For example, to actually zoom towards the center of the screen, it would be necessary for offset to not change, which would require gestureCentroid to be (0,0) which would require pinching in the top left corner. Having the centroid actually be in the middle of the screen causes the screen to zoom more towards the lower-right corner. The fix is simple, to just change the coordinates for the centroid.
val width = MyApplication.getScreenWidth()
val height = MyApplication.getScreenHeight()
val centroidRelativeToScreenCenter = gestureCentroid.minus(Offset(width / 2f, height / 2f))
offset = (offset + centroidRelativeToScreenCenter / oldScale).rotateBy(gestureRotate) -
(centroidRelativeToScreenCenter / newScale + gesturePan / oldScale)I would make a pull request, but I'm not familiar enough with Kotlin to know the proper way to include methods to get the screen width/height into this repo. I'll put them here, just for reference:
fun getScreenWidth(): Int {
val windowManager = (getContext().getSystemService(Context.WINDOW_SERVICE) as WindowManager)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val windowMetrics = windowManager.currentWindowMetrics
val bounds = windowMetrics.bounds
val insets = windowMetrics.getWindowInsets().getInsetsIgnoringVisibility(
WindowInsets.Type.systemBars()
)
if (getContext().resources.configuration.orientation
== Configuration.ORIENTATION_LANDSCAPE
&& getContext().resources.configuration.smallestScreenWidthDp < 600
) { // landscape and phone
val navigationBarSize = insets.right + insets.left
bounds.width() - navigationBarSize
} else { // portrait or tablet
bounds.width()
}
} else {
val outMetrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(outMetrics)
outMetrics.widthPixels
}
}
fun getScreenHeight(): Int {
val windowManager = (getContext().getSystemService(Context.WINDOW_SERVICE) as WindowManager)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val insets: Insets = windowManager.currentWindowMetrics.windowInsets.getInsetsIgnoringVisibility(
WindowInsets.Type.systemBars()
)
// https://stackoverflow.com/questions/63407883/getting-screen-width-on-api-level-30-android-11-getdefaultdisplay-and-getme
val windowMetrics = windowManager.currentWindowMetrics
val bounds: Rect = windowMetrics.bounds
bounds.height() - insets.top - insets.bottom
} else {
val outMetrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(outMetrics)
outMetrics.heightPixels
}
}