Как сделать градиент, который опускается в центре и поднимается на углах?
Я пытаюсь создать градиент, который поднимается из правого и левого углов, но понижается в центре, что-то вроде этого:
Для этого я объединил два градиента — один в нижнем левом углу, а другой в нижнем правом углу — так, чтобы вместе они визуально выглядели как единый градиент, который снижается в центре. Но есть две проблемы:
- Переход в середине экрана слишком резкий. Я показал это на скриншоте ниже. Там показано "Currently", как это выглядит в данный момент, и в "Expected" представлена более плавная кривая, которую я хочу получить от градиента.
- Вторая проблема заключается в том, что в центре, где эти два градиента накладываются друг на друга, эффект градиента уменьшается.
Что я тут пропускаю?
Код:
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import kotlin.math.PI
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.hypot
import kotlin.math.sin
@Composable
fun JustGradient() {
val gradientColorsList: List<Color> = listOf(
Color.Transparent,
Color.Transparent,
Color.Transparent,
Color.Transparent,
Color.Transparent,
Color.Green.copy(alpha = 0.2f),
Color.Green.copy(alpha = 0.3f),
Color.Green.copy(alpha = 0.4f),
Color.Green.copy(alpha = 0.5f),
Color.Green.copy(alpha = 0.9f)
)
Box(
modifier = Modifier
.fillMaxWidth()
.height(400.dp)
.background(Color.Red.copy(alpha = .3f))
.applyGradient(gradientColorsList)
)
}
private fun Modifier.applyGradient(gradientColorsList: List<Color>?): Modifier = this
.let {
if (gradientColorsList?.isNotEmpty() == true) {
it.angledGradientBackground(
colors = gradientColorsList,
degrees = 45f
)
} else {
it
}
}
.let {
if (gradientColorsList?.isNotEmpty() == true) {
it.angledGradientBackground(
colors = gradientColorsList,
degrees = 135f
)
} else {
it
}
}
private fun Modifier.angledGradientBackground(colors: List<Color>, degrees: Float) = this
.drawBehind {
/*
Have to compute length of gradient vector so that it lies within
the visible rectangle.
--------------------------------------------
| length of gradient ^ / |
| ---> / / |
| / / <- rotation angle |
| / o --------------------| y
| / / |
| / / |
| v / |
--------------------------------------------
x
diagonal angle = atan2(y, x)
(it's hard to draw the diagonal)
Simply rotating the diagonal around the centre of the rectangle
will lead to points outside the rectangle area. Further, just
truncating the coordinate to be at the nearest edge of the
rectangle to the rotated point will distort the angle.
Let α be the desired gradient angle (in radians) and γ be the
angle of the diagonal of the rectangle.
The correct for the length of the gradient is given by:
x/|cos(α)| if -γ <= α <= γ, or π - γ <= α <= π + γ
y/|sin(α)| if γ <= α <= π - γ, or π + γ <= α <= 2π - γ
where γ ∈ (0, π/2) is the angle that the diagonal makes with
the base of the rectangle.
*/
val (x, y) = size
val gamma = atan2(y, x)
if (gamma == 0f || gamma == (PI / 2).toFloat()) {
// degenerate rectangle
return@drawBehind
}
val degreesNormalised = (degrees % 360).let { if (it < 0) it + 360 else it }
val alpha = (degreesNormalised * PI / 180).toFloat()
val gradientLength: Float = when (alpha) {
// ray from centre cuts the right edge of the rectangle
in 0f..gamma, in (2* PI - gamma)..2* PI -> { x / cos(alpha) }
// ray from centre cuts the top edge of the rectangle
in gamma..(PI - gamma).toFloat() -> { y / sin(alpha) }
// ray from centre cuts the left edge of the rectangle
in (PI - gamma)..(PI + gamma) -> { x / -cos(alpha) }
// ray from centre cuts the bottom edge of the rectangle
in (PI + gamma)..(2* PI - gamma) -> { y / -sin(alpha) }
// default case (which shouldn't really happen)
else -> hypot(x, y)
}
val centerOffsetX = cos(alpha) * gradientLength / 2
val centerOffsetY = sin(alpha) * gradientLength / 2
drawRect(
brush = Brush.linearGradient(
colors = colors,
// negative here so that 0 degrees is left -> right
// and 90 degrees is top -> bottom
start = Offset(center.x - centerOffsetX, center.y - centerOffsetY),
end = Offset(center.x + centerOffsetX, center.y + centerOffsetY)
),
size = size
)
}