Shadow: 本質、モバイルアプリケーションにおける影の作成

著者: IT Sectr 公開日: 2026-02-28 読了時間: 9 分

Shadow(影)は、モバイルインターフェースにおける視覚効果で、要素間の奥行きと階層の錯覚を生み出します。影は、どの要素がz平面でより高く、したがってより重要またはインタラクティブであるかを示します。Material Design Guidelines(Google、2026)によると、システムの影は5つの主要な高さ(elevation)を使用します:0 dp(フラット)、1~4 dp(ボタン)、6~12 dp(カード)、16~24 dp(ダイアログ)、24+ dp(モーダルウィンドウ)。要素の高さの詳細については、Elevationに関する記事をご覧ください。

重要なポイント

  • 影(shadow) — 要素の下の領域をエッジのぼかしで暗くすることで奥行きを生み出す視覚効果。
  • iOSでは、影はCALayerのプロパティ(shadowColor、shadowOffset、shadowRadius、shadowOpacity)で設定します。
  • Androidでは、影はelevation(Material Design)またはOutlineProviderによるカスタムレンダリングで管理します。
  • Jetpack ComposeはModifier.shadow()およびCard/Surfaceのelevationを介して影をサポートします。
  • Flutterでは、影はBoxDecoration内のBoxShadowまたはPhysicalModelを介して実装します。

UIにおける影とは?

影(shadow)は、ユーザーインターフェースにおいて照明と奥行きをシミュレートする視覚効果です。影は、要素の暗くぼやけたコピーをオリジナルからオフセットして描画することで作成されます。モバイルデザインにおいて、影には3つの機能があります:階層を示す(高い要素=長い影)、インタラクティブ性を知らせる(影付きのボタンは押せるように見える)、コンテンツを背景から分離する(影付きのカードは個別のブロックとして読める)。

GoogleのMaterial Designでは、影はelevationの概念と不可分に結びついています。各要素はz座標で特定の高さを占め、影は上部からの照明の投影です。AppleのHuman Interface Guidelinesは厳格な高さシステムを規定していませんが、インターフェースに視覚的階層と奥行きを作成するために影を使用することを推奨しています。

Nielsen Norman Groupの調査(2024)によると、適切に配置された影はインタラクティブな要素を見つける時間を24%短縮します。影は視覚障害のあるユーザーが要素の境界を区別するのにも役立ちます。ただし、影の過剰な使用(画面に3レベル以上)は認知負荷を増加させ、知覚を損なわせます。

iOSの影:CALayerとSwiftUI

iOSでは、影はCALayerのプロパティを介して実装されます:shadowColorは影の色を設定し、shadowOffset — CGSizeでのオフセット(x、y)、shadowRadius — ぼかし半径、shadowOpacity — 0から1までの不透明度。パフォーマンスのために、shadowPath(影の形状を記述するUIBezierPath)も設定することをお勧めします。そうすることでCore Animationが自動的に計算しなくなります。

swift
// UIKit: CALayerによる影の設定
import UIKit

let cardView = UIView()
cardView.backgroundColor = .systemBackground
cardView.layer.cornerRadius = 12

// 影の設定
cardView.layer.shadowColor = UIColor.black.cgColor
cardView.layer.shadowOpacity = 0.15
cardView.layer.shadowOffset = CGSize(width: 0, height: 4)
cardView.layer.shadowRadius = 8

"> 最適化: 明示的なshadowPath
cardView.layer.shadowPath = UIBezierPath(
    roundedRect: cardView.bounds,
    cornerRadius: 12
).cgPath

SwiftUIでは、影は.shadow()修飾子でcolor、radius、x、yのパラメータで設定します。SwiftUIは自動的にshadowPathを適用し、レンダリングパフォーマンスを管理します。高度な影(内側の影、複数の影)には、LinearGradientとoverlay()を使用します。

swift
// SwiftUI: shadow修飾子による影
import SwiftUI

VStack {
    Text("影付きカード")
        .padding()
        .background(Color.white)
        .cornerRadius(16)
        .shadow(
            color: .black.opacity(0.15),
            radius: 10,
            x: 0,
            y: 4
        )
}

iOSでの影のパフォーマンス:shadowPathが設定されていない場合、Core Animationは毎フレーム、レイヤーの形状に基づいて影を計算するため、アニメーション中に遅延が発生する可能性があります。静的要素の場合は、常にshadowPathを設定してください。アニメーション要素の場合は、CALayerでshouldRasterize = trueを使用して、影を別のレイヤーにキャッシュします。SwiftUIでは、ラスタライズは.drawingGroup()で管理されます。

Androidの影:elevationとカスタム影

Androidでは、Material Designの影はelevation(z座標での要素の高さを設定するViewのプロパティ)を介して実装されます。システムはelevationとoutlineProvider(要素の輪郭)に基づいて自動的に影をレンダリングします。elevationが高いほど、影は大きくぼやけます。Material Designを継承していないView(例:AppCompat)の場合、影はハードウェアアクセラレーションが有効な場合にのみ機能します。

xml
<!-- XML: 属性によるelevation -->
<androidx.cardview.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:cardCornerRadius="12dp"
    app:cardElevation="6dp">
    <!-- カードの内容 -->
</androidx.cardview.widget.CardView>

Android View Systemでのカスタム影には、OutlineProviderを使用します。ViewOutlineProviderを使用すると、outline.setRoundRect()またはoutline.setPath()で任意の形状を設定できます。OutlineProviderがない場合、影はViewの長方形の形状に基づいてレンダリングされ、角の丸みが考慮されません。

kotlin
// Kotlin: 影のカスタムOutlineProvider
import android.graphics.Outline
import android.view.View
import android.view.ViewOutlineProvider

val myView: View = findViewById(R.id.myView)
myView.outlineProvider = object : ViewOutlineProvider() {
    override fun getOutline(view: View, outline: Outline) {
        outline.setRoundRect(
            0, 0, view.width, view.height,
            12f  // 角の半径
        )
    }
}
myView.elevation = 8f  // px単位の影の高さ
myView.translationZ = 4f  // 押下時の追加の高さ

Jetpack Composeでは、影はModifier.shadow()とCardおよびSurfaceコンポーネントのelevationで管理されます。Modifier.shadow()はelevationとshapeを受け入れます。View Systemとは異なり、Composeは独自のレンダーパイプラインで影をレンダリングするため、すべてのAndroidバージョンでより予測可能な動作を提供します。

kotlin
// Jetpack Compose: Modifier.shadowによる影
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

Card(
    modifier = Modifier.shadow(
        elevation = 6.dp,
        shape = RoundedCornerShape(12.dp),
        clip = false
    ),
    colors = CardDefaults.cardColors(containerColor = Color.White)
) {
    Text("カスタム影付きCard")
}

影の使用に関する推奨事項

影の適切な使用はインターフェースの認識を向上させますが、不適切な使用は視覚的ノイズを生み出し、パフォーマンスを低下させます。Material Designは5つのelevationレベルを定義しています:0 dp(影なし)、1~4 dp(ボタン、チップ)、6~12 dp(カード、メニュー)、16~24 dp(ダイアログ、ボトムシート)、24+ dp(モーダルウィンドウ、FAB)。

レベルElevation要素影の特性
00 dpテキスト、アイコン、背景影なし、フラットレイヤー
11–4 dpボタン、入力フィールド軽い影、小さなオフセット
26–12 dpカード、Snackbar中程度の影、顕著なぼかし
316–24 dpダイアログ、メニュー深い影、大きなぼかし
424+ dpモーダルウィンドウ最大の影、要素が浮遊

ベストプラクティス:(1) 注意を引くべきでない要素(背景、区切り線)には影を使用しない、(2) 影の高さは要素の重要度に一致させるべき — 重要度が高いほど高く、(3) ボタンを押すときは、触覚フィードバックのためにアニメーションでelevationを減らす、(4) 暗い背景では影を避け、代わりにoverlay(明るく/暗く)を使用する。Material You(Android 12+)では、影はテーマに適応します:暗い背景では、elevationはカラーレイヤー(surface tint)に置き換えられます。

Flutterの影:BoxShadowとPhysicalModel

Flutterでは、影はBoxDecoration内のBoxShadowを介して実装されます。BoxShadowはcolor、offset、blurRadius、spreadRadiusを受け入れます。Flutterは複数の影をサポートしています:BoxShadowのリストを渡すことで、複雑な効果(例:外側の影+内側のハイライト)を作成できます。PhysicalModelは、形状(BeveledRectangleBorder、CircleBorder)に基づいて物理的な影をシミュレートする代替手段です。

dart
// Flutter: BoxDecoration + BoxShadowによる影
import 'package:flutter/material.dart';

Container(
    decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12.0),
        boxShadow: [
            BoxShadow(
                color: Colors.black.withOpacity(0.15),
                offset: Offset(0, 4),
                blurRadius: 8.0,
                spreadRadius: 0,
            ),
        ],
    ),
    child: Text('影付きコンテナ'),
)

// 物理的な影のためのPhysicalModel
PhysicalModel(
    color: Colors.white,
    elevation: 6.0,
    shadowColor: Colors.black.withOpacity(0.2),
    borderRadius: BorderRadius.circular(12.0),
    child: Text('影付き物理モデル'),
)

パフォーマンス:複数の影を持つBoxShadowは、変更のたびに再描画を引き起こす可能性があります。静的要素にはPhysicalModelを使用してください — 影を別のレイヤー(RepaintBoundary)にキャッシュします。Flutter 3.16+では、テーマ(ライト/ダーク)に応じて色とぼかしを変更するアダプティブ影を備えたMaterial 3 elevationがサポートされています。

よくある質問

elevationとshadowの違いは?

Elevationはz座標での要素の数値的な高さであり、shadow(影)はその高さによって作成される視覚効果です。Androidでは、elevationは自動的に影を生成するViewのプロパティです。デザインでは、elevationとshadowはしばしば互換的に使用されますが、技術的にはelevationが原因でshadowが結果です。

Androidで影が表示されないのはなぜ?

確認事項:(1) Viewにelevationが0より大きく設定されている、(2) outlineProviderが設定されている(ViewがMaterialライブラリからのものでない場合)、(3) ハードウェアアクセラレーションが有効(マニフェストでandroid:hardwareAccelerated="true")。CardViewは自動的にoutlineProviderを管理します。カスタムViewの場合は、view.outlineProvider = ViewOutlineProvider.BACKGROUNDを呼び出します。

iOSで内側の影(inner shadow)を作成するには?

CALayerは内側の影を直接サポートしていません。マスク内で黒色と透明度を持つCAGradientLayerを使用するか、ラスター内側の影を持つUIImageViewをオーバーレイします。SwiftUIでは、内側の影はLinearGradientとblendMode(.multiply)を使用したoverlayで実装されます。

ボタンを押したときの影をアニメーション化するには?

Androidでは、アニメーションにtranslationZを使用します:通常時elevation = 2 dp、押下時translationZ = 4 dp(合計高さ6 dp)。iOSでは、ボタンの変形と同時にshadowOffsetとshadowRadiusをアニメーション化します。SwiftUIでは、Modifier.shadow()でelevationを変更してwithAnimationを使用します。Flutterでは、BoxShadowを変更してAnimatedContainerを使用します。

1つの画面でいくつの影を使用できますか?

Material Designは、1つの画面で3レベルまでのelevationを推奨しています。影のある要素がそれ以上ある場合は、同じ高さでグループ化してください。過剰な影は視覚的ノイズを生み出し、ミッドレンジおよび低価格帯のデバイスでのレンダリングパフォーマンスを低下させます。

まとめ

  • 影(shadow)は、z座標での要素のelevationに基づく視覚的な奥行き効果です。
  • iOSでは、影はCALayer(shadowColor、shadowOffset、shadowRadius、shadowOpacity)で設定します。
  • Androidでは、影はelevationとカスタム形状用のOutlineProviderで管理します。
  • Material Designは5つのelevationレベルを定義しています:0、1~4、6~12、16~24、24+ dp。
  • Jetpack Composeは、影の管理にCard/SurfaceのModifier.shadow()とelevationを使用します。
  • FlutterはBoxShadow(BoxDecoration)とPhysicalModel(物理的な影)で影を実装します。
  • 最適化:shadowPathの設定(iOS)、outlineProvider(Android)、PhysicalModelの使用(Flutter)。

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください