Add some sugar on the location feature

This commit is contained in:
Thomas Andres Gomez 2023-08-11 10:46:09 +02:00
parent b6af245327
commit 85cdd69570
2 changed files with 149 additions and 28 deletions

View file

@ -22,6 +22,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
@ -29,8 +30,14 @@ import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.ui.composable.AsyncImage
import com.pixelized.rplexicon.ui.theme.colors.LexiconColors
import com.pixelized.rplexicon.utilitary.extentions.lexicon
import com.skydoves.landscapist.ImageOptions
import kotlin.math.sqrt
private val RADIUS = 12.dp
private val SQUARE = (RADIUS / sqrt(2f))
@Composable
fun FantasyMap(
@ -39,13 +46,30 @@ fun FantasyMap(
imageOptions: ImageOptions = ImageOptions(),
@DrawableRes previewPlaceholder: Int,
item: State<LocationDetailUio>,
highlight: State<Offset>,
selectedItem: State<Int>,
onMarquee: (MarqueeUio) -> Unit,
onTap: (Offset) -> Unit,
) {
val colorScheme = MaterialTheme.lexicon.colorScheme
val animatedScale = animateFloatAsState(targetValue = state.scale, label = "ScaleAnimation")
val animatedOffset = animateOffsetAsState(targetValue = state.offset, label = "OffsetAnimation")
val animatedScale = animateFloatAsState(
targetValue = state.scale,
label = "ScaleAnimation",
)
val animatedOffset = animateOffsetAsState(
targetValue = state.offset,
label = "OffsetAnimation",
)
val animatedMarqueeAlpha = animateFloatAsState(
targetValue = if (state.freeHand) 0f else 1f,
label = "MarqueAlphaAnimation",
)
val animatedCrossAlpha = remember {
derivedStateOf {
1f - animatedMarqueeAlpha.value
}
}
LaunchedEffect(key1 = "CenterOnMarquee:${selectedItem.value}") {
item.value.marquees.getOrNull(selectedItem.value)?.position
@ -95,37 +119,27 @@ fun FantasyMap(
.drawWithContent {
drawContent()
if (state.freeHand.not()) {
if (animatedMarqueeAlpha.value > 0f) {
item.value.marquees.forEachIndexed { index, item ->
if (item.position != Offset.Unspecified) {
drawCircle(
color = colorScheme.shadow,
radius = 12.dp.toPx() / animatedScale.value,
style = Stroke(
width = 2.dp.toPx() / animatedScale.value,
),
center = Offset(
x = size.width * item.position.x,
y = size.height * item.position.y + 2.dp.toPx() / animatedScale.value,
)
)
drawCircle(
color = when (selectedItem.value) {
index -> colorScheme.base.primary
else -> Color.White
},
radius = 12.dp.toPx() / animatedScale.value,
style = Stroke(
width = 2.dp.toPx() / animatedScale.value,
),
center = Offset(
x = size.width * item.position.x,
y = size.height * item.position.y,
)
drawMarque(
colorScheme = colorScheme,
alpha = animatedMarqueeAlpha,
scale = animatedScale,
position = item.position,
selected = selectedItem.value == index
)
}
}
}
if (highlight.value != Offset.Unspecified && animatedCrossAlpha.value > 0f) {
drawCross(
colorScheme = colorScheme,
alpha = animatedCrossAlpha,
scale = animatedScale,
position = highlight.value,
)
}
}
.pointerInput("DetectTapGestures") {
detectTapGestures(
@ -240,4 +254,100 @@ class FantasyMapState(
else -> origin
}
}
}
private fun DrawScope.drawMarque(
colorScheme: LexiconColors,
alpha: State<Float>,
scale: State<Float>,
position: Offset,
selected: Boolean,
) {
drawCircle(
color = colorScheme.shadow,
alpha = alpha.value,
radius = 12.dp.toPx() / scale.value,
style = Stroke(
width = 2.dp.toPx() / scale.value,
),
center = Offset(
x = size.width * position.x,
y = size.height * position.y + 2.dp.toPx() / scale.value,
)
)
drawCircle(
color = when (selected) {
true -> colorScheme.base.primary
else -> Color.White
},
alpha = alpha.value,
radius = 12.dp.toPx() / scale.value,
style = Stroke(
width = 2.dp.toPx() / scale.value,
),
center = Offset(
x = size.width * position.x,
y = size.height * position.y,
)
)
}
private fun DrawScope.drawCross(
colorScheme: LexiconColors,
alpha: State<Float>,
scale: State<Float>,
position: Offset,
) {
drawLine(
color = colorScheme.shadow,
alpha = alpha.value,
strokeWidth = 2.dp.toPx() / scale.value,
start = Offset(
x = size.width * position.x - SQUARE.toPx() / scale.value,
y = size.height * position.y - SQUARE.toPx() / scale.value + 2.dp.toPx() / scale.value,
),
end = Offset(
x = size.width * position.x + SQUARE.toPx() / scale.value,
y = size.height * position.y + SQUARE.toPx() / scale.value + 2.dp.toPx() / scale.value,
)
)
drawLine(
color = colorScheme.shadow,
alpha = alpha.value,
strokeWidth = 2.dp.toPx() / scale.value,
start = Offset(
x = size.width * position.x + SQUARE.toPx() / scale.value,
y = size.height * position.y - SQUARE.toPx() / scale.value + 2.dp.toPx() / scale.value,
),
end = Offset(
x = size.width * position.x - SQUARE.toPx() / scale.value,
y = size.height * position.y + SQUARE.toPx() / scale.value + 2.dp.toPx() / scale.value,
)
)
drawLine(
color = colorScheme.base.primary,
alpha = alpha.value,
strokeWidth = 2.dp.toPx() / scale.value,
start = Offset(
x = size.width * position.x - SQUARE.toPx() / scale.value,
y = size.height * position.y - SQUARE.toPx() / scale.value,
),
end = Offset(
x = size.width * position.x + SQUARE.toPx() / scale.value,
y = size.height * position.y + SQUARE.toPx() / scale.value,
)
)
drawLine(
color = colorScheme.base.primary,
alpha = alpha.value,
strokeWidth = 2.dp.toPx() / scale.value,
start = Offset(
x = size.width * position.x + SQUARE.toPx() / scale.value,
y = size.height * position.y - SQUARE.toPx() / scale.value,
),
end = Offset(
x = size.width * position.x - SQUARE.toPx() / scale.value,
y = size.height * position.y + SQUARE.toPx() / scale.value,
)
)
}

View file

@ -23,7 +23,6 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilledIconButton
import androidx.compose.material3.FilledIconToggleButton
import androidx.compose.material3.FilledTonalIconButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
@ -108,6 +107,7 @@ fun LocationDetail(
}
}
val mapHighlight = remember { mutableStateOf(Offset.Unspecified) }
val selectedIndex = remember { mutableStateOf(0) }
Surface {
@ -119,6 +119,7 @@ fun LocationDetail(
fantasyMapState = fantasy,
item = viewModel.location,
selectedIndex = selectedIndex,
mapHighlight = mapHighlight,
onBack = {
screen.popBackStack()
},
@ -137,9 +138,16 @@ fun LocationDetail(
actionLabel = ok,
duration = SnackbarDuration.Indefinite,
)
mapHighlight.value = Offset.Unspecified
}
mapHighlight.value = it
},
onTouch = {
if (it) {
mapHighlight.value = Offset.Unspecified
} else {
job.value?.cancel()
}
fantasy.toggleFreeHand(it)
},
onCenter = {
@ -199,6 +207,7 @@ private fun LocationContent(
fantasyMapState: FantasyMapState,
item: State<LocationDetailUio>,
selectedIndex: State<Int>,
mapHighlight: State<Offset>,
onBack: () -> Unit,
onMarquee: (MarqueeUio) -> Unit,
onMapTap: (Offset) -> Unit,
@ -266,6 +275,7 @@ private fun LocationContent(
imageOptions = ImageOptions(contentScale = ContentScale.Fit),
item = item,
selectedItem = selectedIndex,
highlight = mapHighlight,
onMarquee = onMarquee,
onTap = onMapTap,
)
@ -412,6 +422,7 @@ private fun LocationPreview() {
)
},
selectedIndex = remember { mutableStateOf(0) },
mapHighlight = remember { mutableStateOf(Offset(0.5f, 0.5f)) },
onBack = { },
onMarquee = { },
onMapTap = { },