Add some sugar on the location feature
This commit is contained in:
parent
b6af245327
commit
85cdd69570
2 changed files with 149 additions and 28 deletions
|
|
@ -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,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -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 = { },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue