Compose でのぼかし

ぼかし効果は、視覚的なディテールを和らげ、写真の被写界深度を作り出し、前景の UI 要素にフォーカスを合わせます。Jetpack Compose では、Modifier.blur を使用して、画像、シェイプ、テキスト、レイアウト コンテナなど、コンポーザブル内に描画されたコンテンツをぼかします。Modifier.blur を適用すると、コンポーザブルが別のグラフィック レイヤにレンダリングされます。

左: 元の画像。中央: 表面全体に均一なぼかしが適用された画像。右: 上部が鮮明で、下部に向かってぼかしが強くなる、段階的なぼかしが適用された画像。
図 1. 左: 元の画像。中央: 表面全体に均一なぼかしが適用された画像。右: 上部が鮮明で、下部に向かってぼかしが強くなる、段階的なぼかしが適用された画像。

デザインの目標に応じて、次の 2 つの方法でぼかしを変化させることができます。

  • 均一なぼかし: コンポーザブル全体に一定のぼかし半径を適用します。両方の軸に均等に適用するか、水平方向と垂直方向の半径を個別に適用します(radiusX、radiusY)。
  • プログレッシブ ブラー: BlurRadiusSpec グラデーションまたはカスタム Shader を使用して、コンポーザブル全体でぼかし半径を空間的に変化させます。

均一なぼかしを適用する

コンポーザブル全体に一定のぼかしを適用するには、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. 強い水平方向のモーション ブラー効果が適用された犬の正方形の画像。

プログレッシブぼかしを適用する

プログレッシブ ブラーは、コンポーズ可能な関数全体でぼかし半径を変化させ、シャープな領域とぼやけた領域の間のスムーズな遷移を作成します。

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 の間で線形補間されます。サーフェス全体でぼかしの分布をカスタマイズするには(複数のエッジをぼかす、中央部分を完全にシャープに保つなど)、2 ~ 16 個の BlurStop エントリを含む List<BlurStop> を verticalGradient、horizontalGradient、linearGradient、または radialGradient に渡します。BlurStop エントリごとに、次の項目を定義します。

  • fraction: 0.0f から 1.0f までのグラデーションに沿った相対位置。ストップは、割合によって自動的に並べ替えられます。
  • radius: その割合の位置に適用される Dp のぼかし半径。

たとえば、次の例に示すように、4 つのストップを使用すると、画像の上端と下端をぼかし、中央を完全にシャープに保つことができます。

@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 を組み合わせて、スクロールするコンテンツの端をフェードする、フォーカス ポイントをスポットライトする、カスタム シェーダーでぼかしをマスクするなど、完全な UI パターンを実現する方法については、一般的なぼかしのユースケースをご覧ください。