I want to make a sticker maker application with Kotlin and Compose, but I'm stuck in making the sticker editor because I want it to be like the picture I posted below, but I couldn't find a solution. I want this editor to move, zoom, rotate, With just one finger.
Exactly like this gif I posted
like this lib https://github.com/wuapnjie/StickerView but i wnat kotlin and compose
I tried to make it using the articles and previous Stack Overflow questions but it didn't work as I expected
@ExperimentalComposeUiApi
@Composable
fun OneFinger() {
var viewRotation = remember { 0.0 }
var fingerRotation = remember { 0.0 }
var rotation by remember {
mutableStateOf(0.0)
}
var centerX by remember {
mutableStateOf(0f)
}
var centerY by remember {
mutableStateOf(0f)
}
var touchX by remember {
mutableStateOf(0f)
}
var touchY by remember {
mutableStateOf(0f)
}
var translationX by remember {
mutableStateOf(0f)
}
var translationY by remember {
mutableStateOf(0f)
}
var sizeTouchX by remember {
mutableStateOf(0f)
}
var sizeTouchY by remember {
mutableStateOf(0f)
}
var size by remember {
mutableStateOf(85)
}
BoxWithConstraints(
contentAlignment = Alignment.Center,
modifier = Modifier
.size(400.dp)
.background(Color.White)
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.graphicsLayer(
translationX = translationX,
translationY = translationY,
)
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.rotate(rotation.toFloat())
) {
Box(
modifier = Modifier
.size((115 + (size - 85)).dp)
.border(
border = BorderStroke(0.5.dp, Color.Gray),
shape = RectangleShape
)
)
Box(
modifier = Modifier
.size((135 + (size - 85)).dp)
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.align(Alignment.BottomStart)
.onGloballyPositioned {
val windowBounds = it.boundsInWindow()
centerX = (windowBounds.size.width / 2f)
centerY = (windowBounds.size.height / 2f)
Log.e("error1" , "centerX = $centerX and center y = $centerY")
}
.pointerInteropFilter { event ->
touchX = event.x
touchY = event.y
when (event.action) {
MotionEvent.ACTION_DOWN -> {
viewRotation = rotation
fingerRotation = Math.toDegrees(
atan2(
(touchX - centerX).toDouble(),
(centerY - touchY).toDouble()
)
)
}
MotionEvent.ACTION_MOVE -> {
val newFingerRotation = Math.toDegrees(
atan2(
(touchX - centerX).toDouble(),
(centerY - touchY).toDouble()
)
)
rotation =
(viewRotation + newFingerRotation - fingerRotation)
}
MotionEvent.ACTION_UP -> {
fingerRotation = 0.0
}
}
true
}
) {
Image(
painter = painterResource(id = R.drawable.baseline_replay_24),
contentDescription = null,
modifier = Modifier
.align(Alignment.BottomStart)
.clip(CircleShape)
.background(BlackGray)
)
}
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.align(Alignment.BottomEnd)
.pointerInput(Unit) {
awaitEachGesture {
awaitFirstDown()
do {
val event = awaitPointerEvent()
val offset = event.calculatePan()
sizeTouchX += offset.x
sizeTouchY += offset.y
val currentDistance = sqrt(
sizeTouchX.pow(2) + sizeTouchY.pow(2)
)
size = currentDistance.roundToInt()
} while (event.changes.any { it.pressed })
}
}
) {
Image(
painter = painterResource(id = R.drawable.baseline_open_in_full_24),
contentDescription = null,
modifier = Modifier
.align(Alignment.BottomEnd)
.clip(CircleShape)
.background(BlackGray)
)
}
}
Box {
Image(
modifier = Modifier.size(size.dp),
contentDescription = null,
painter = painterResource(R.drawable.ic_launcher_background)
)
}
}
Box(
modifier = Modifier
.size((105 + (size - 85)).dp)
.pointerInput(Unit) {
detectDragGestures { change, dragAmount ->
translationY += dragAmount.y
translationX += dragAmount.x
change.consume()
}
}
)
}
}
}
and in main screen
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun MainScreen() {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.background(Color.LightGray)
.fillMaxSize()
) {
OneFinger()
}
}