fix(photo-editor): 修复裁剪框拖动的闭包快照陷阱
pointerInput(Unit) 的手势 lambda 只在首次组合捕获一次 state, 后续 onDragStart 读的是过期快照,导致再次拖动从中心开始。 改用本地累积值绕开 StateFlow 往返:onDragStart 把 state 快照到 mutableFloatStateOf,onDrag 每帧在累积值上叠加增量,onDragEnd 一次性提交。onDragStart 通过 rememberUpdatedState 读最新 state。 enterCrop 改为幂等(已启用则保留当前框),避免 LaunchedEffect 因 editorState 变化重触时冲掉用户拖动结果。
This commit is contained in:
parent
b8509381d9
commit
d3ebd6829b
@ -80,10 +80,11 @@ class PhotoEditorViewModel(
|
||||
update { it.copy(rotationDegrees = it.rotationDegrees + delta) }
|
||||
}
|
||||
|
||||
/** 进入裁剪模式:启用裁剪框并重置为默认居中区域。 */
|
||||
/** 进入裁剪模式:仅在尚未启用时初始化为默认居中区域,已启用则保留当前框。 */
|
||||
fun enterCrop() {
|
||||
update {
|
||||
it.copy(cropLeft = 0.1f, cropTop = 0.1f, cropRight = 0.9f, cropBottom = 0.9f)
|
||||
if (it.cropEnabled) it
|
||||
else it.copy(cropLeft = 0.1f, cropTop = 0.1f, cropRight = 0.9f, cropBottom = 0.9f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -40,8 +40,10 @@ import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import kotlin.math.absoluteValue
|
||||
@ -436,12 +438,22 @@ private fun CropOverlay(
|
||||
onCropChange: (Float, Float, Float, Float) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
// 拖动手柄内聚在此组件,避免跨重组延迟
|
||||
var dragHandle by remember { mutableStateOf(CropHandle.NONE) }
|
||||
val left = state.cropLeft ?: 0.1f
|
||||
val top = state.cropTop
|
||||
val right = state.cropRight ?: 0.9f
|
||||
val bottom = state.cropBottom
|
||||
// 拖动期间的本地累积值;非拖动时为 null,绘制回退到 state
|
||||
var dragLeft by remember { mutableFloatStateOf(0f) }
|
||||
var dragTop by remember { mutableFloatStateOf(0f) }
|
||||
var dragRight by remember { mutableFloatStateOf(0f) }
|
||||
var dragBottom by remember { mutableFloatStateOf(0f) }
|
||||
// pointerInput(Unit) 只捕获一次 state,用 rememberUpdatedState 让
|
||||
// 手势 lambda 内始终读到最新的 state(含上次拖动提交后的值)
|
||||
val currentState by rememberUpdatedState(state)
|
||||
|
||||
// 绘制用值:拖动中取本地累积,否则取 state
|
||||
val left = if (dragHandle != CropHandle.NONE) dragLeft else (state.cropLeft ?: 0.1f)
|
||||
val top = if (dragHandle != CropHandle.NONE) dragTop else state.cropTop
|
||||
val right = if (dragHandle != CropHandle.NONE) dragRight else (state.cropRight ?: 0.9f)
|
||||
val bottom = if (dragHandle != CropHandle.NONE) dragBottom else state.cropBottom
|
||||
|
||||
Canvas(
|
||||
modifier = modifier.pointerInput(Unit) {
|
||||
detectDragGestures(
|
||||
@ -449,13 +461,16 @@ private fun CropOverlay(
|
||||
val size = this.size
|
||||
val ox = offset.x / size.width
|
||||
val oy = offset.y / size.height
|
||||
dragHandle = hitHandle(
|
||||
ox, oy,
|
||||
state.cropLeft ?: 0.1f,
|
||||
state.cropTop,
|
||||
state.cropRight ?: 0.9f,
|
||||
state.cropBottom
|
||||
)
|
||||
val baseL = currentState.cropLeft ?: 0.1f
|
||||
val baseT = currentState.cropTop
|
||||
val baseR = currentState.cropRight ?: 0.9f
|
||||
val baseB = currentState.cropBottom
|
||||
// 进入拖动:把 state 当前值快照到本地累积
|
||||
dragLeft = baseL
|
||||
dragTop = baseT
|
||||
dragRight = baseR
|
||||
dragBottom = baseB
|
||||
dragHandle = hitHandle(ox, oy, baseL, baseT, baseR, baseB)
|
||||
},
|
||||
onDrag = { change, drag ->
|
||||
if (dragHandle == CropHandle.NONE) return@detectDragGestures
|
||||
@ -463,18 +478,20 @@ private fun CropOverlay(
|
||||
val size = this.size
|
||||
val dx = drag.x / size.width
|
||||
val dy = drag.y / size.height
|
||||
// 每帧从 state 读最新值,叠加增量
|
||||
val cur = moveCrop(
|
||||
dx, dy,
|
||||
state.cropLeft ?: 0.1f,
|
||||
state.cropTop,
|
||||
state.cropRight ?: 0.9f,
|
||||
state.cropBottom,
|
||||
dragHandle
|
||||
)
|
||||
onCropChange(cur.left, cur.top, cur.right, cur.bottom)
|
||||
// 在本地累积值上叠加增量,不触发 StateFlow 往返
|
||||
val cur = moveCrop(dx, dy, dragLeft, dragTop, dragRight, dragBottom, dragHandle)
|
||||
dragLeft = cur.left
|
||||
dragTop = cur.top
|
||||
dragRight = cur.right
|
||||
dragBottom = cur.bottom
|
||||
},
|
||||
onDragEnd = {
|
||||
// 拖动结束:一次性提交回 ViewModel
|
||||
if (dragHandle != CropHandle.NONE) {
|
||||
onCropChange(dragLeft, dragTop, dragRight, dragBottom)
|
||||
}
|
||||
dragHandle = CropHandle.NONE
|
||||
},
|
||||
onDragEnd = { dragHandle = CropHandle.NONE },
|
||||
onDragCancel = { dragHandle = CropHandle.NONE }
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user