Размытие в Compose

Эффекты размытия смягчают визуальные детали, создают фотографическую глубину резкости и привлекают внимание к элементам пользовательского интерфейса на переднем плане. В Jetpack Compose используйте Modifier.blur, чтобы размыть контент, отрисованный внутри composable-функции, включая изображения, фигуры, текст и контейнеры макета. Применение Modifier.blur отрисовывает composable-функцию в отдельном графическом слое.

Слева: исходное изображение. По центру: изображение с равномерным размытием по всей поверхности. Справа: изображение с прогрессивным размытием, которое увеличивается сверху вниз.
Рисунок 1. Слева: исходное изображение. По центру: изображение с равномерным размытием по всей поверхности. Справа: изображение с прогрессивным размытием, которое увеличивается сверху вниз.

В зависимости от целей дизайна вы можете изменять размытие двумя способами:

  • Равномерное размытие. Применяет постоянный радиус размытия ко всей composable-функции, либо одинаково по обеим осям, либо с независимыми горизонтальными и вертикальными радиусами (radiusX, radiusY).
  • Прогрессивное размытие. Радиус размытия меняется в зависимости от положения в composable-функции. Для этого используются градиенты BlurRadiusSpec или настраиваемый класс Shader.

Как применить равномерное размытие

…

Чтобы применить постоянное размытие ко всей composable-функции, передайте радиус Dp в Modifier.blur. Передайте одно значение radius, чтобы размыть контент одинаково по горизонтальной (X) и вертикальной (Y) осям:

@Composable
fun UniformBlurExample() {
    Image(
        painter = painterResource(id = R.drawable.dog),
        contentDescription = stringResource(id = R.string.dog_content_description),
        contentScale = ContentScale.Crop,
        modifier = Modifier
            .size(150.dp)
            .blur(
                radius = 8.dp,
                edgeTreatment = BlurredEdgeTreatment(RoundedCornerShape(8.dp)),
            ),
    )
}

Собака, равномерно размытая по горизонтальной и вертикальной осям.
Рисунок 2. Собака, равномерно размытая по горизонтальной и вертикальной осям.

Чтобы создать эффект размытия в движении, передайте отдельные значения radiusX и radiusY в Modifier.blur. Чтобы применить эффект размытия, значения radiusX и radiusY должны быть больше 0.dp, как показано в следующем примере:

@Composable
fun DirectionalBlurExample() {
    Image(
        painter = painterResource(id = R.drawable.dog),
        contentDescription = stringResource(id = R.string.dog_content_description),
        contentScale = ContentScale.Crop,
        modifier = Modifier
            .size(150.dp)
            .blur(
                radiusX = 10.dp,
                radiusY = 2.dp,
                edgeTreatment = BlurredEdgeTreatment(RoundedCornerShape(8.dp)),
            ),
    )
}

Собака с сильным эффектом размытия при горизонтальном движении.
Рисунок 3. Квадратное изображение собаки с сильным эффектом размытия по горизонтали.

Как применить прогрессивное размытие

Прогрессивное размытие изменяет радиус размытия по всей composable-функции, создавая плавный переход между резкими и размытыми областями.

Передайте BlurRadiusSpec в Modifier.blur. Например, используйте BlurRadiusSpec.verticalGradient с startRadius = 0.dp и endRadius = 6.dp, чтобы верхняя часть изображения оставалась резкой, а нижняя постепенно размывалась, как показано в следующем примере:

@Composable
fun ProgressiveBlurExample() {
    Image(
        painter = painterResource(id = R.drawable.dog),
        contentDescription = stringResource(id = R.string.dog_content_description),
        contentScale = ContentScale.Crop,
        modifier = Modifier
            .size(150.dp)
            .blur(
                // Sharp at the top (0.dp), progressively blurred toward the bottom (6.dp)
                blurRadiusSpec = BlurRadiusSpec.verticalGradient(
                    startRadius = 0.dp,
                    endRadius = 6.dp,
                ),
                edgeTreatment = BlurredEdgeTreatment(RoundedCornerShape(8.dp)),
            ),
    )
}

Собака, которая четко видна вверху и постепенно становится размытой внизу.
Рисунок 4. Изображение собаки, которое вверху выглядит четким, а внизу постепенно размывается.

Укажите градиенты размытия с помощью BlurRadiusSpec

В BlurRadiusSpec размеры задаются в Dp и автоматически корректируются в соответствии с размером слоя и плотностью экрана при отрисовке. Размытие с пространственным варьированием после преобразования ограничивается 150 пикселями, а у BlurRadiusSpec.uniform ограничений нет.

В таблице ниже перечислены фабричные функции, доступные в BlurRadiusSpec.

BlurRadiusSpec завод Описание
BlurRadiusSpec.verticalGradient Плавно меняет радиус размытия по вертикали от верхнего (0f) до нижнего (1f) края слоя.
BlurRadiusSpec.horizontalGradient Плавное изменение радиуса размытия по горизонтали от левого края (0f) до правого (1f).
BlurRadiusSpec.linearGradient Применяет переход с направленным размытием вдоль прямой линии, соединяющей определенные координаты DpOffset.
BlurRadiusSpec.radialGradient Размытие распространяется от точки фокусировки к границе fallOffRadius.
BlurRadiusSpec.uniform Применяет равномерный радиус размытия ко всему слою.
BlurRadiusSpec.shader Использует альфа-канал пользовательского Shader для управления и масштабирования maxRadius в каждом пикселе.

Настройте распределение размытия с помощью параметра BlurStop.

Стандартные градиентные размытия интерполируются линейно между 0f и 1f. Чтобы настроить распределение размытия по поверхности, например размыть несколько краев или оставить среднюю часть полностью резкой, передайте List<BlurStop>, содержащий от 2 до 16 записей BlurStop, в verticalGradient, horizontalGradient, linearGradient или radialGradient. Для каждой записи BlurStop укажите следующее:

  • fraction – относительное положение вдоль градиента от 0.0f до 1.0f. Остановки автоматически сортируются по доле.
  • radius – радиус размытия в Dp, применяемый в этой позиции.

Например, четыре точки размывают верхний и нижний края изображения, сохраняя при этом полную резкость в середине, как показано в следующем примере:

@Composable
fun MultiStopBlurExample() {
    Image(
        painter = painterResource(id = R.drawable.dog),
        contentDescription = stringResource(id = R.string.dog_content_description),
        contentScale = ContentScale.Crop,
        modifier = Modifier
            .size(150.dp)
            .blur(
                // Blurred at the top and bottom edges, sharp in the middle (0.3f..0.7f)
                blurRadiusSpec = BlurRadiusSpec.verticalGradient(
                    stops = listOf(
                        BlurStop(fraction = 0.0f, radius = 20.dp),
                        BlurStop(fraction = 0.3f, radius = 0.dp),
                        BlurStop(fraction = 0.7f, radius = 0.dp),
                        BlurStop(fraction = 1.0f, radius = 20.dp),
                    ),
                ),
                edgeTreatment = BlurredEdgeTreatment.Unbounded,
            ),
    )
}

Собака с размытыми верхним и нижним краями, при этом средняя горизонтальная полоса находится в резком фокусе.
Рисунок 5. Изображение собаки с размытыми верхним и нижним краями и четкой горизонтальной полосой посередине.

Как настроить свойства слоя с помощью BlurScope

Вместо того чтобы передавать blurRadiusSpec напрямую, вы можете передать блок конфигурации в Modifier.blur { ... }. Этот блок выполняется внутри BlurScope, что дает вам прямой доступ к плотности макета и следующим свойствам:

  • size: DpSize – измеренные ширина и высота размытого слоя в Dp, которые определяют пространство координат для геометрии градиента.
  • blurRadiusSpec: BlurRadiusSpec – радиус размытия по всей поверхности. Значение по умолчанию – BlurRadiusSpec.uniform(0.dp) (без размытия).
  • edgeTreatment: BlurredEdgeTreatment – стратегия, используемая для выборки и обрезки пикселей за пределами границ контента. Значение по умолчанию – BlurredEdgeTreatment.Rectangle.
  • alpha: Float – непрозрачность размытого слоя в диапазоне 0f..1f. Значение по умолчанию – 1f.

Поскольку блок BlurScope выполняется на этапе графического слоя, после измерения и макета, вы можете вычислить геометрию градиента из size без запуска рекомпозиции.

Управление обрезкой краев с помощью BlurredEdgeTreatment

Поскольку при размытии используются соседние пиксели в пределах радиуса размытия, размытое изображение выходит за пределы оригинального контента на величину радиуса размытия. BlurredEdgeTreatment определяет, как выбираются пиксели за пределами исходных границ и обрезается ли результат по форме.

BlurredEdgeTreatment работает как с равномерным, так и с прогрессивным размытием:

BlurredEdgeTreatment Поведение Результат
BlurredEdgeTreatment.Rectangle (по умолчанию) Обрезает размытый результат по прямоугольным границам исходного контента и выбирает ближайшие пиксели по краям (TileMode.Clamp). Размытое изображение собаки, обрезанное по четкой прямоугольной границе.
BlurredEdgeTreatment(shape) Обрезает результат размытия до указанного значения Shape (например, RoundedCornerShape(8.dp)) и выбирает ближайшие пиксели по краям (TileMode.Clamp). Сохраняет резкость размытого результата на границе фигуры, не выходя за исходные границы. Размытое изображение собаки, обрезанное по форме с закругленными углами и острыми краями.
BlurredEdgeTreatment.Unbounded Результат размытия не обрезается (TileMode.Decal). Пиксели за пределами контента становятся прозрачными черными, из-за чего внешние края изображения выглядят нечеткими и размытыми за пределами исходных границ. Размытое изображение собаки, края которого плавно исчезают за исходными границами.

Чтобы узнать, как эти API сочетаются в полных шаблонах пользовательского интерфейса, например, при затухании краев прокручиваемого контента, выделении фокуса или маскировании размытия с помощью пользовательского шейдера, ознакомьтесь с распространенными вариантами использования размытия.