mac*_*our 17 android android-jetpack-compose
我正在尝试在 Jetpack Compose 中绘制渐变背景,并且我希望渐变具有固定角度,无论我要绘制的对象的形状如何。
然而,使用Modifier.background(brush=...)
,我能找到的最好的方法是linearGradient
计算渐变的固定起点和终点的角度。
例如,有没有一种方法可以在不知道最终大小的情况下指定我想要 45 度角的渐变?
编辑:我想要一个可以适用于任何给定角度的解决方案,而不仅仅是 45 度。
Gab*_*tti 21
您可以使用参数start
和end
来实现 45 度角。
就像是:
val gradient45 = Brush.linearGradient(
colors = listOf(Color.Yellow, Color.Red),
start = Offset(0f, Float.POSITIVE_INFINITY),
end = Offset(Float.POSITIVE_INFINITY, 0f)
)
Run Code Online (Sandbox Code Playgroud)
Ehs*_*msz 16
您可以使用Modifier.drawBehind()
并计算点的坐标来绘制渐变颜色。
fun Modifier.gradientBackground(colors: List<Color>, angle: Float) = this.then(
Modifier.drawBehind {
val angleRad = angle / 180f * PI
val x = cos(angleRad).toFloat() //Fractional x
val y = sin(angleRad).toFloat() //Fractional y
val radius = sqrt(size.width.pow(2) + size.height.pow(2)) / 2f
val offset = center + Offset(x * radius, y * radius)
val exactOffset = Offset(
x = min(offset.x.coerceAtLeast(0f), size.width),
y = size.height - min(offset.y.coerceAtLeast(0f), size.height)
)
drawRect(
brush = Brush.linearGradient(
colors = colors,
start = Offset(size.width, size.height) - exactOffset,
end = exactOffset
),
size = size
)
}
)
Run Code Online (Sandbox Code Playgroud)
例子:
Modifier
.gradientBackground(listOf(Color.Red, Color.Green), angle = 45f)
Run Code Online (Sandbox Code Playgroud)
Thr*_*ian 10
我创建了一个 GradientOffset 类,它允许您将渐变旋转 45 度。
存储旋转角度的枚举和存储Offset
s 的数据类。
/**
* Offset for [Brush.linearGradient] to rotate gradient depending on [start] and [end] offsets.
*/
data class GradientOffset(val start: Offset, val end: Offset)
enum class GradientAngle {
CW0, CW45, CW90, CW135, CW180, CW225, CW270, CW315
}
Run Code Online (Sandbox Code Playgroud)
旋转功能
/**
*
* Get a [GradientOffset] that rotate a gradient clockwise with specified angle in degrees.
* Default value for [GradientOffset] is [GradientAngle.CW0] which is 0 degrees
* that returns a horizontal gradient.
*
* Get start and end offsets that are limited between [0f, Float.POSITIVE_INFINITY] in x and
* y axes wrapped in [GradientOffset].
* Infinity is converted to Composable width on x axis, height on y axis in shader.
*
* Default angle for [Brush.linearGradient] when no offset is 0 degrees in Compose ,
* [Brush.verticalGradient] is [Brush.linearGradient] with 90 degrees.
*
* ```
* 0 degrees
* start = Offset(0f,0f),
* end = Offset(Float.POSITIVE_INFINITY,0f)
*
* 45 degrees
* start = Offset(0f, Float.POSITIVE_INFINITY),
* end = Offset(Float.POSITIVE_INFINITY, 0f)
*
* 90 degrees
* start = Offset(0f, Float.POSITIVE_INFINITY),
* end = Offset.Zero
*
* 135 degrees
* start = Offset.Infinity,
* end = Offset.Zero
*
* 180 degrees
* start = Offset(Float.POSITIVE_INFINITY, 0f),
* end = Offset.Zero,
*
* ```
*/
fun GradientOffset(angle: GradientAngle = GradientAngle.CW0): GradientOffset {
return when (angle) {
GradientAngle.CW45 -> GradientOffset(
start = Offset.Zero,
end = Offset.Infinite
)
GradientAngle.CW90 -> GradientOffset(
start = Offset.Zero,
end = Offset(0f, Float.POSITIVE_INFINITY)
)
GradientAngle.CW135 -> GradientOffset(
start = Offset(Float.POSITIVE_INFINITY, 0f),
end = Offset(0f, Float.POSITIVE_INFINITY)
)
GradientAngle.CW180 -> GradientOffset(
start = Offset(Float.POSITIVE_INFINITY, 0f),
end = Offset.Zero,
)
GradientAngle.CW225 -> GradientOffset(
start = Offset.Infinite,
end = Offset.Zero
)
GradientAngle.CW270 -> GradientOffset(
start = Offset(0f, Float.POSITIVE_INFINITY),
end = Offset.Zero
)
GradientAngle.CW315 -> GradientOffset(
start = Offset(0f, Float.POSITIVE_INFINITY),
end = Offset(Float.POSITIVE_INFINITY, 0f)
)
else -> GradientOffset(
start = Offset.Zero,
end = Offset(Float.POSITIVE_INFINITY, 0f)
)
}
}
Run Code Online (Sandbox Code Playgroud)
使用方法非常简单,通过设置顺时针旋转任意渐变GradientAngle.CW
// Offsets for gradients based on selected angle
var gradientOffset by remember {
mutableStateOf(GradientOffset(GradientAngle.CW45))
}
Brush.linearGradient(
listOf(Color.Red, Color.Green, Color.Blue),
start = gradientOffset.start,
end = gradientOffset.end
)
Run Code Online (Sandbox Code Playgroud)
结果
如果您想尝试的话,请使用Repo 链接
要具有可以旋转到任何角度的渐变,您需要实现自己的LinearGradient
类,该类扩展ShaderBrush
然后使用简单的三角函数计算旋转到位置。
我意识到原始代码中有一个错误,它扭曲了渐变角度。需要更多的三角函数,以便将渐变开始和结束限制在画布区域内(如果这是所需的),同时保留渐变角度。这是更新的解决方案,带有额外的 ASCII 艺术。
\n fun Modifier.angledGradientBackground(colors: List<Color>, degrees: Float) = this.then(\n drawBehind {\n /*\n Have to compute length of gradient vector so that it lies within\n the visible rectangle.\n --------------------------------------------\n | length of gradient ^ / |\n | ---> / / |\n | / / <- rotation angle |\n | / o --------------------| y\n | / / |\n | / / |\n | v / |\n --------------------------------------------\n x\n\n diagonal angle = atan2(y, x)\n (it's hard to draw the diagonal)\n\n Simply rotating the diagonal around the centre of the rectangle\n will lead to points outside the rectangle area. Further, just\n truncating the coordinate to be at the nearest edge of the\n rectangle to the rotated point will distort the angle.\n Let \xce\xb1 be the desired gradient angle (in radians) and \xce\xb3 be the\n angle of the diagonal of the rectangle.\n The correct for the length of the gradient is given by:\n x/|cos(\xce\xb1)| if -\xce\xb3 <= \xce\xb1 <= \xce\xb3, or \xcf\x80 - \xce\xb3 <= \xce\xb1 <= \xcf\x80 + \xce\xb3\n y/|sin(\xce\xb1)| if \xce\xb3 <= \xce\xb1 <= \xcf\x80 - \xce\xb3, or \xcf\x80 + \xce\xb3 <= \xce\xb1 <= 2\xcf\x80 - \xce\xb3\n where \xce\xb3 \xe2\x88\x88 (0, \xcf\x80/2) is the angle that the diagonal makes with\n the base of the rectangle.\n\n */\n\n val (x, y) = size\n val gamma = atan2(y, x)\n\n if (gamma == 0f || gamma == (PI / 2).toFloat()) {\n // degenerate rectangle\n return@drawBehind\n }\n\n val degreesNormalised = (degrees % 360).let { if (it < 0) it + 360 else it }\n\n val alpha = (degreesNormalised * PI / 180).toFloat()\n\n val gradientLength = when (alpha) {\n // ray from centre cuts the right edge of the rectangle\n in 0f..gamma, in (2*PI - gamma)..2*PI -> { x / cos(alpha) }\n // ray from centre cuts the top edge of the rectangle\n in gamma..(PI - gamma).toFloat() -> { y / sin(alpha) }\n // ray from centre cuts the left edge of the rectangle\n in (PI - gamma)..(PI + gamma) -> { x / -cos(alpha) }\n // ray from centre cuts the bottom edge of the rectangle\n in (PI + gamma)..(2*PI - gamma) -> { y / -sin(alpha) }\n // default case (which shouldn't really happen)\n else -> hypot(x, y)\n }\n\n val centerOffsetX = cos(alpha) * gradientLength / 2\n val centerOffsetY = sin(alpha) * gradientLength / 2\n\n drawRect(\n brush = Brush.linearGradient(\n colors = colors,\n // negative here so that 0 degrees is left -> right\n and 90 degrees is top -> bottom\n start = Offset(center.x - centerOffsetX,center.y - centerOffsetY),\n end = Offset(center.x + centerOffsetX, center.y + centerOffsetY)\n ),\n size = size\n )\n }\n)\n
Run Code Online (Sandbox Code Playgroud)\n这是我基于@Ehan msz 代码的最终解决方案。我调整了他的解决方案,使 0 度对应于从左到右的渐变方向,90 度对应于从上到下的方向。
\nfun Modifier.angledGradient(colors: List<Color>, degrees: Float) = this.then(\nModifier.drawBehind {\n val rad = (degrees * PI / 180).toFloat()\n val diagonal = sqrt(size.width * size.width + size.height * size.height)\n val centerOffsetX = cos(rad) * diagonal / 2\n val centerOffsetY = sin(rad) * diagonal / 2\n\n // negative so that 0 degrees is left -> right and 90 degrees is top -> bottom\n val startOffset = Offset(\n x = (center.x - centerOffsetX).coerceIn(0f, size.width),\n y = (center.y - centerOffsetY).coerceIn(0f, size.height)\n )\n val endOffset = Offset(\n x = (center.x + centerOffsetX).coerceIn(0f, size.width),\n y = (center.y + centerOffsetY).coerceIn(0f, size.height)\n )\n\n drawRect(\n brush = Brush.linearGradient(\n colors = colors,\n start = startOffset,\n end = endOffset\n ),\n size = size\n )\n}\n
Run Code Online (Sandbox Code Playgroud)\n
归档时间: |
|
查看次数: |
14469 次 |
最近记录: |