Skip to content

Commit c069110

Browse files
add BeforeAfterLayout
1 parent c55334c commit c069110

1 file changed

Lines changed: 147 additions & 0 deletions

File tree

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package com.smarttoolfactory.image.beforeafter
2+
3+
import androidx.compose.foundation.Canvas
4+
import androidx.compose.foundation.layout.Box
5+
import androidx.compose.foundation.layout.BoxWithConstraints
6+
import androidx.compose.foundation.layout.Column
7+
import androidx.compose.foundation.layout.fillMaxSize
8+
import androidx.compose.foundation.shape.GenericShape
9+
import androidx.compose.material.Text
10+
import androidx.compose.runtime.*
11+
import androidx.compose.ui.Modifier
12+
import androidx.compose.ui.draw.clipToBounds
13+
import androidx.compose.ui.geometry.Offset
14+
import androidx.compose.ui.geometry.Size
15+
import androidx.compose.ui.graphics.Color
16+
import androidx.compose.ui.graphics.graphicsLayer
17+
import androidx.compose.ui.input.pointer.pointerInput
18+
import androidx.compose.ui.layout.onSizeChanged
19+
import androidx.compose.ui.unit.LayoutDirection
20+
import androidx.compose.ui.unit.dp
21+
import com.smarttoolfactory.gesture.detectTransformGestures
22+
import com.smarttoolfactory.gesture.pointerMotionEvents
23+
24+
@Composable
25+
fun BeforeAfterLayout(
26+
modifier: Modifier = Modifier,
27+
before: @Composable () -> Unit,
28+
after: @Composable () -> Unit
29+
) {
30+
31+
var parentSize by remember { mutableStateOf(Size.Zero) }
32+
var handlePosition by remember { mutableStateOf(100f) }
33+
34+
var isHandleTouched by remember { mutableStateOf(false) }
35+
36+
var zoom by remember { mutableStateOf(1f) }
37+
var pan by remember { mutableStateOf(Offset.Zero) }
38+
39+
val shape by remember(handlePosition, zoom, pan) {
40+
mutableStateOf(
41+
GenericShape { size: Size, layoutDirection: LayoutDirection ->
42+
43+
val maxX = (parentSize.width * (zoom - 1) / 2f)
44+
val panPos = (maxX - pan.x) / zoom
45+
46+
val shapeOffset = handlePosition / zoom + panPos
47+
48+
moveTo(0f, 0f)
49+
lineTo(shapeOffset, 0f)
50+
lineTo(shapeOffset, size.height)
51+
lineTo(0f, size.height)
52+
close()
53+
}
54+
)
55+
}
56+
57+
val gestureModifier = Modifier
58+
.clipToBounds()
59+
.pointerInput(Unit) {
60+
detectTransformGestures(
61+
onGesture = { _: Offset, panChange: Offset, zoomChange: Float, _, _, _ ->
62+
63+
zoom = (zoom * zoomChange).coerceIn(1f, 5f)
64+
65+
val maxX = (size.width * (zoom - 1) / 2f)
66+
val maxY = (size.height * (zoom - 1) / 2f)
67+
68+
val newPan = pan + panChange.times(zoom)
69+
pan = Offset(
70+
newPan.x.coerceIn(-maxX, maxX),
71+
newPan.y.coerceIn(-maxY, maxY)
72+
)
73+
}
74+
)
75+
}
76+
.pointerMotionEvents(
77+
onDown = {
78+
val position = it.position
79+
val xPos = position.x
80+
isHandleTouched = ((handlePosition - xPos) * (handlePosition - xPos) < 10000)
81+
},
82+
onMove = {
83+
if (isHandleTouched) {
84+
handlePosition = it.position.x
85+
it.consume()
86+
}
87+
},
88+
onUp = {
89+
isHandleTouched = false
90+
}
91+
)
92+
.graphicsLayer {
93+
this.scaleX = zoom
94+
this.scaleY = zoom
95+
this.translationX = pan.x
96+
this.translationY = pan.y
97+
}
98+
99+
val parentModifier = modifier
100+
.then(gestureModifier)
101+
.onSizeChanged {
102+
parentSize = Size(it.width.toFloat(), it.height.toFloat())
103+
}
104+
105+
Column {
106+
BoxWithConstraints {
107+
Box(modifier = parentModifier) {
108+
109+
val afterModifier = Modifier
110+
.fillMaxSize()
111+
.graphicsLayer {
112+
this.clip = true
113+
this.shape = shape
114+
}
115+
116+
Box(modifier = Modifier.fillMaxSize()) {
117+
before()
118+
}
119+
120+
Box(modifier = afterModifier) {
121+
after()
122+
}
123+
}
124+
125+
Canvas(modifier = Modifier.matchParentSize()) {
126+
127+
val canvasWidth = size.width
128+
129+
val imagePosition = handlePosition.coerceIn(0f, canvasWidth)
130+
131+
drawLine(
132+
Color.White,
133+
strokeWidth = 2.dp.toPx(),
134+
start = Offset(imagePosition, 0f),
135+
end = Offset(imagePosition, size.height)
136+
)
137+
drawCircle(
138+
color = Color.Red,
139+
center = Offset(imagePosition, size.height / 2),
140+
radius = 30f
141+
)
142+
}
143+
}
144+
145+
Text("Size: $parentSize, posX: $handlePosition")
146+
}
147+
}

0 commit comments

Comments
 (0)