Skip to content

Commit

Permalink
Fix background parallax (#1230)
Browse files Browse the repository at this point in the history
This used to be a feature, but then when replacing with LazyRow, it was
dropped.
Let's fix it + use the deferred modifier to properly read scroll state.
  • Loading branch information
mlykotom authored Nov 28, 2023
2 parents e52dfd0 + e9448df commit d8d341b
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.TileMode
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

Expand All @@ -47,13 +49,30 @@ fun Modifier.offsetGradientBackground(
offset: Float = 0f
) = background(
Brush.horizontalGradient(
colors,
colors = colors,
startX = -offset,
endX = width - offset,
tileMode = TileMode.Mirror
)
)

fun Modifier.offsetGradientBackground(
colors: List<Color>,
width: Density.() -> Float,
offset: Density.() -> Float = { 0f }
) = drawBehind {
val actualOffset = offset()

drawRect(
Brush.horizontalGradient(
colors = colors,
startX = -actualOffset,
endX = width() - actualOffset,
tileMode = TileMode.Mirror
)
)
}

fun Modifier.diagonalGradientBorder(
colors: List<Color>,
borderSize: Dp = 2.dp,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
Expand All @@ -53,6 +53,7 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
Expand All @@ -67,13 +68,8 @@ import com.example.jetsnack.ui.utils.mirroringIcon

private val HighlightCardWidth = 170.dp
private val HighlightCardPadding = 16.dp

// The Cards show a gradient which spans 3 cards and scrolls with parallax.
private val gradientWidth
@Composable
get() = with(LocalDensity.current) {
(3 * (HighlightCardWidth + HighlightCardPadding).toPx())
}
private val Density.cardWidthWithPaddingPx
get() = (HighlightCardWidth + HighlightCardPadding).toPx()

@Composable
fun SnackCollection(
Expand Down Expand Up @@ -129,28 +125,33 @@ private fun HighlightedSnacks(
onSnackClick: (Long) -> Unit,
modifier: Modifier = Modifier
) {
val scroll = rememberScrollState(0)
val rowState = rememberLazyListState()
val cardWidthWithPaddingPx = with(LocalDensity.current) { cardWidthWithPaddingPx }

val scrollProvider = {
// Simple calculation of scroll distance for homogenous item types with the same width.
val offsetFromStart = cardWidthWithPaddingPx * rowState.firstVisibleItemIndex
offsetFromStart + rowState.firstVisibleItemScrollOffset
}

val gradient = when ((index / 2) % 2) {
0 -> JetsnackTheme.colors.gradient6_1
else -> JetsnackTheme.colors.gradient6_2
}
// The Cards show a gradient which spans 3 cards and scrolls with parallax.
val gradientWidth = with(LocalDensity.current) {
(6 * (HighlightCardWidth + HighlightCardPadding).toPx())
}

LazyRow(
state = rowState,
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(start = 24.dp, end = 24.dp)
) {
itemsIndexed(snacks) { index, snack ->
HighlightSnackItem(
snack,
onSnackClick,
index,
gradient,
gradientWidth,
scroll.value
snack = snack,
onSnackClick = onSnackClick,
index = index,
gradient = gradient,
scrollProvider = scrollProvider
)
}
}
Expand Down Expand Up @@ -214,17 +215,13 @@ private fun HighlightSnackItem(
onSnackClick: (Long) -> Unit,
index: Int,
gradient: List<Color>,
gradientWidth: Float,
scroll: Int,
scrollProvider: () -> Float,
modifier: Modifier = Modifier
) {
val left = index * with(LocalDensity.current) {
(HighlightCardWidth + HighlightCardPadding).toPx()
}
JetsnackCard(
modifier = modifier
.size(
width = 170.dp,
width = HighlightCardWidth,
height = 250.dp
)
.padding(bottom = 16.dp)
Expand All @@ -239,12 +236,22 @@ private fun HighlightSnackItem(
.height(160.dp)
.fillMaxWidth()
) {
val gradientOffset = left - (scroll / 3f)
Box(
modifier = Modifier
.height(100.dp)
.fillMaxWidth()
.offsetGradientBackground(gradient, gradientWidth, gradientOffset)
.offsetGradientBackground(
colors = gradient,
width = {
// The Cards show a gradient which spans 6 cards and scrolls with parallax.
6 * cardWidthWithPaddingPx
},
offset = {
val left = index * cardWidthWithPaddingPx
val gradientOffset = left - (scrollProvider() / 3f)
gradientOffset
}
)
)
SnackImage(
imageUrl = snack.imageUrl,
Expand Down Expand Up @@ -312,8 +319,7 @@ fun SnackCardPreview() {
onSnackClick = { },
index = 0,
gradient = JetsnackTheme.colors.gradient6_1,
gradientWidth = gradientWidth,
scroll = 0
scrollProvider = { 0f }
)
}
}

0 comments on commit d8d341b

Please sign in to comment.