React、SVG、Framer MotionでAppleのアクティビティリングを再現する
Appleのアクティビティリングをゼロから構築する方法を学びます。React、SVG、Framer Motionを使って、データ駆動型の美しいUIアニメーションを作成する方法を解説します。
このページの内容
Apple Watchのアクティビティリングは、データ可視化の傑作です—シンプルで、エレガントで瞬時に理解できます。 fitness goal(フィットネスの目標)を「リングを閉じる」楽しいゲームに変えることで、ユーザーにやる気を与え続けます。开发者にとって、これは魅力的なUIの課題を表しています。こんなに流畅で視覚的に魅力的な要素をWebで構築するにはどのようにすればよいでしょうか。
このチュートリアルでは、Appleの象徴的なアクティビティリングをゼロから再現します。SVGの力とReact stateの制御に深く触れ、Framer Motionのバターのように滑らかなアニメーションで命を吹き込みます。これは単にデザインをクローンすることではなく、SVG操作と сучасный Webアニメーションの基本原則を理解することです。
構築するもの: 実際のものとまったく同じように、データ変更に応答する3つのネストされたアニメーションアクティビティリング。
前提条件:
- ReactとReact Hooksの基本的な理解。
- マシンにインストールされたNode.jsとnpm/yarn。
- 美しいユーザーインターフェースを構築する情熱。✨
開発者にとってこれが重要な理由: このプロジェクトは、フロントエンドスキルをレベルアップするのに最適な方法です。基本的なCSSを超えて、強力なSVG属性をマスターし、Framer Motionのようなプロフェッショナルグレードのアニメーションライブラリが複雑なアニメーションをどのように簡素化できるか学び、UIをより動的でレスポンシブに感じさせることができます。
問題の理解
本質的に、アクティビティリングは円形のプログレスバーです。主な技術的課題は、パーセンテージ値(例:75%完了)に対応する部分的な円を描くことです。
アクティビティリングコンポーネントアーキテクチャ
次の図は、リングコンポーネントの構造を示しています:
魔法はストロークの2つのSVGプロパティにあります:
stroke-dasharray:このプロパティは実線を破線に変えますstroke-dashoffset:このプロパティは破線の開始位置をパスに沿って定義します
stroke-dasharrayを円の完全な周囲長に設定することで、円の全長の「ダッシュ」と同程度の長さの「ギャップ」を持つ「ダッシュ」を作成します。その後、stroke-dashoffsetをアニメーション化して円を reveal(表示)し、描画されているような錯覚を作り出します。Framer Motionはこのプロセスを大いに簡素化し、pathLengthプロパティを0から1までアニメーション化できます。
私たちのアプローチは、各リングに対して2つのSVG <circle>要素をレイヤーすることです:
- 背景リング:トラックとして機能する半透明の静的な円
- 前景リング:実際の進捗を表す、色付きアニメーションリング
前提条件
コーディングを開始する前に、開発環境を設定しましょう。
- Node.js:v16以降
- React:v18以降
- Framer Motion:使用するアニメーションライブラリ。
新しいReactプロジェクトを作成し、必要な依存関係をインストールしましょう。
# Viteで新しいReactアプリを作成(速度のために推奨)
npm create vite@latest apple-rings-tutorial -- --template react
# プロジェクトディレクトリに移動
cd apple-rings-tutorial
# Framer Motionをインストール
npm install framer-motion
# 開発サーバーを起動
npm run dev
npm run devの実行後、http://localhost:5173でデフォルトのVite Reactページが表示されます。
SVGで単一のアニメーションプログレスリングを構築
単一の再利用可能なリングコンポーネントを作成することから始めましょう。このコンポーネントは、3つのアクティビティリングすべての基盤になります。
実施内容
ActivityRing.jsという新しいコンポーネントを作成します。このコンポーネントはprogress prop(0から1の間の数値)を取り、その進捗を視覚的に表すSVG円をレンダリングします。
実装
新しいファイルsrc/ActivityRing.jsxを作成します。
// src/ActivityRing.jsx
import React from 'react';
import { motion } from 'framer-motion';
const ActivityRing = ({ progress = 0, radius = 50, strokeWidth = 10 }) => {
const innerRadius = radius - strokeWidth / 2;
const circumference = 2 * Math.PI * innerRadius;
return (
<svg width={radius * 2} height={radius * 2} className="transform -rotate-90">
{/* Background Ring */}
<circle
cx={radius}
cy={radius}
r={innerRadius}
fill="transparent"
stroke="#e6e6e6"
strokeWidth={strokeWidth}
/>
{/* Foreground Ring */}
<motion.circle
cx={radius}
cy={radius}
r={innerRadius}
fill="transparent"
stroke="#f87171" // A default color, we'll make this dynamic later
strokeWidth={strokeWidth}
strokeDasharray={circumference}
strokeDashoffset={circumference * (1 - progress)}
strokeLinecap="round"
/>
</svg>
);
};
export default ActivityRing;
次に、App.jsxでこのコンポーネントを使用しましょう。
// src/App.jsx
import React from 'react';
import ActivityRing from './ActivityRing';
import './App.css';
function App() {
return (
<div className="flex items-center justify-center min-h-screen bg-black">
<ActivityRing progress={0.75} radius={80} strokeWidth={20} />
</div>
);
}
export default App;
コンテンツを中央に配置するための基本的なCSSが必要です。src/index.cssまたはsrc/App.cssで:
/* Ensure you have Tailwind CSS or basic CSS for centering */
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.flex { display: flex; }
.items-center { align-items: center; }
.justify-center { justify-content: center; }
.min-h-screen { min-height: 100vh; }
.bg-black { background-color: #000; }
.transform { transform-origin: center; }
.-rotate-90 { transform: rotate(-90deg); }
動作の仕組み
- SVGコンテナ:
<svg>要素を作成します。-rotate-90クラスはデフォルトでSVG円は3時の位置から描画を開始するため、重要です。トップ(12時)から開始するようにSVG全体を回転させます。 - 半径と周囲長:円の
circumferenceを計算します。これがストロークの総長さです。 - 背景円:トラックとして機能する単純な完全な円です。
- 前景円:魔法が起きる場所です。
strokeDasharray={circumference}:ダッシュが円全体を長さとする破線パターンを作成します。strokeDashoffset={circumference * (1 - progress)}:ダッシュの開始位置をオフセットします。progressが0のとき、オフセットは完全なcircumferenceなので、可視部分(ダッシュ)は完全に非表示になります。progressが1のとき、オフセットは0で、円全体が表示されます。strokeLinecap="round":プログレスバーの端に丸みを加えて、Appleのデザインと同様に、より魅力的な外観にします。
Framer Motionでリング進捗をアニメーション化
静的なリングもクールですが、アニメーション化されたものはずっと良くなります。Framer Motionを追加して進捗をアニメーション化しましょう。
実施内容
手動で計算されたstrokeDashoffsetをFramer Motionの宣言的アニメーションプロパティに置き換えます。motion.circleコンポーネントのinitial、animate、およびtransition propsを使用します。Framer MotionにはSVG用の特別なpathLengthバリアントがあり、これが信じられないほどシンプルになります。
実装
src/ActivityRing.jsxを更新しましょう。
// src/ActivityRing.jsx
import React from 'react';
import { motion } from 'framer-motion';
const ActivityRing = ({ progress = 0, radius = 50, strokeWidth = 10, color = '#f87171' }) => {
const innerRadius = radius - strokeWidth / 2;
const circumference = 2 * Math.PI * innerRadius;
const variants = {
initial: {
strokeDashoffset: circumference,
},
animate: {
strokeDashoffset: circumference * (1 - progress),
transition: {
duration: 1.5,
ease: "easeInOut",
},
},
};
return (
<svg width={radius * 2} height={radius * 2} className="transform -rotate-90">
{/* Background Ring */}
<circle
cx={radius}
cy={radius}
r={innerRadius}
fill="transparent"
stroke={color}
strokeWidth={strokeWidth}
className="opacity-20"
/>
{/* Foreground Ring */}
<motion.circle
cx={radius}
cy={radius}
r={innerRadius}
fill="transparent"
stroke={color}
strokeWidth={strokeWidth}
strokeDasharray={circumference}
strokeLinecap="round"
initial="initial"
animate="animate"
variants={variants}
/>
</svg>
);
};
export default ActivityRing;
Appleのデザインに合わせて背景リングにs color propと微妙な不透明度)も追加しました。Tailwindを使用していない場合は、.opacity-20`クラスをCSSに追加してください。
動作の仕組み
motion.circle:任意のHTMLまたはSVG要素にプレフィックスを付けてアニメーション可能ににできるFramer Motionのmotionコンポーネントを使用します。variants:アニメーション用の2つの状態initialとanimateを定義します。initial:strokeDashoffsetが完全なcircumferenceに設定され、開始時にリングが空になります。animate:strokeDashoffsetがprogresspropによって決まる値にアニメーション化されます。
transition:アニメーションの動作を定義し、滑らかな加速と減速のために1.5秒のdurationとeaseInOutイージング関数を設定します。
コンポーネントがマウント今、0から75%まで美しくアニメーション化するリングが表示されます!
3つのネストされたアクティビティリングを.compose
再利用可能な<ActivityRing>コンポーネント,使得,创建完整的セット现在就只是组合和样式的问题了。
実施内容
互いにネストされた3つの<ActivityRing>コンポーネントをレンダリングするActivityRingsDashboard.jsxコンポーネントを作成します。絶対位置を使用してそれらを積み重ねます。
実装
まず、リングのデータを定義しましょう。
// src/ringsData.js
export const RINGS_DATA = [
{
id: 'move',
progress: 0.85,
radius: 100,
color: '#FF005C',
},
{
id: 'exercise',
progress: 0.60,
radius: 75,
color: '#92FE9D',
},
{
id: 'stand',
progress: 0.95,
radius: 50,
color: '#00C6FF',
},
];
次に、ダッシュボードコンポーネントsrc/ActivityRingsDashboard.jsxを作成します。
// src/ActivityRingsDashboard.jsx
import React from 'react';
import ActivityRing from './ActivityRing';
import { RINGS_DATA } from './ringsData';
const ActivityRingsDashboard = () => {
return (
<div className="relative flex items-center justify-center">
{RINGS_DATA.map((ring, index) => (
<div key={ring.id} className="absolute">
<ActivityRing
progress={ring.progress}
radius={ring.radius}
strokeWidth={20}
color={ring.color}
/>
</div>
))}
</div>
);
};
export default ActivityRingsDashboard;
最後に、App.jsxを更新して新しいダッシュボードを使用します。
// src/App.jsx
import React, { useState, useEffect } from 'react';
import ActivityRingsDashboard from './ActivityRingsDashboard';
import './App.css';
function App() {
// Example of how you might update progress dynamically
const [data, setData] = useState(RINGS_DATA);
return (
<div className="flex items-center justify-center min-h-screen bg-gray-900">
<ActivityRingsDashboard />
</div>
);
}
export default App;
積み重ねを機能させるには、.relativeと.absoluteクラスをCSSに追加する必要があります。
動作の仕組み
relative位置を持つ親divを使用し、各リングをabsolute位置を持つdiv内に配置します。これにより、それらは互いにの上に積み重ねられ、完全に中央揃えられます。各<ActivityRing>が異なるradiusを持つため、ネストされて見えます。RINGS_DATA配列をマップして、各リングをその特定のでレンダリングします。
すべてをまとめる
メインネットの最終コードは次のとおりです。
src/ActivityRing.jsx
import React from 'react';
import { motion } from 'framer-motion';
const ActivityRing = ({ progress = 0, radius = 50, strokeWidth = 10, color = '#f87171' }) => {
const innerRadius = radius - strokeWidth / 2;
const circumference = 2 * Math.PI * innerRadius;
const variants = {
initial: { strokeDashoffset: circumference },
animate: {
strokeDashoffset: circumference * (1 - progress),
transition: {
duration: 1.5,
delay: 0.2,
ease: 'easeOut',
},
},
};
return (
<svg width={radius * 2} height={radius * 2} className="transform -rotate-90">
<circle
cx={radius}
cy={radius}
r={innerRadius}
fill="transparent"
stroke={color}
strokeWidth={strokeWidth}
className="opacity-30"
/>
<motion.circle
cx={radius}
cy={radius}
r={innerRadius}
fill="transparent"
stroke={color}
strokeWidth={strokeWidth}
strokeDasharray={circumference}
strokeLinecap="round"
initial="initial"
animate="animate"
variants={variants}
/>
</svg>
);
};
export default ActivityRing;```
**`src/ActivityRingsDashboard.jsx`**
```jsx
import React from 'react';
import ActivityRing from './ActivityRing';
import { RINGS_DATA } from './ringsData';
const ActivityRingsDashboard = () => {
return (
<div className="relative w-64 h-64 flex items-center justify-center">
{RINGS_DATA.map((ring, index) => (
<div key={ring.id} className="absolute">
<ActivityRing
progress={ring.progress}
radius={ring.radius}
strokeWidth={20}
color={ring.color}
/>
</div>
))}
</div>
);
};
export default ActivityRingsDashboard;
パフォーマンスの考慮事項
- ハードウェアアクセラレーション:Framer Motionは高いパフォーマンスを持ち、可能な場合はアニメーションのためにハードウェアアクセラレーションを活用するため、パフォーマンスは優秀である必要があります。
- バンドルサイズ:
framer-motionはバンドルサイズに追加しますが、そのパワーと使いやすさはしばしばコストを正当化します。より単純なアニメーションについては、CSSトランジションを使用できます。 - 再レンダリング:
progresspropが変更された場合、コンポーネントは再レンダリングします。これは予想通りで効率的です。Reactの差分アルゴリズムは必要なSVG属性のみを更新します。
セキュリティのベストプラクティス
これは純粋にプレゼンテーション的なコンポーネントであるため、主要なセキュリティの考慮事項は最小限です。ただし、colorまたは他のpropsがユーザー入力から取得された場合、XSS攻撃を防ぐためにそれらをサニタイズすることが不可欠です。外部ソースからのデータは常に検証してください。
結論
Reactの力とSVGの柔軟性、Framer Motionのエレガンスを使用して、Appleの最も象徴的なUI要素の1つを再現することに成功しました。動的な円形プログレスバーを作成するためのSVGストロークプロパティの操作方法、そして単純なコンポーネントを、より複雑で美しいデータ可視化に.composeする方法を学びました。
健康への影響:アクティビティリングのような視覚的な進捗フィードバックは、ゲーミフィケーション 통해1日の身体活動を25〜35%増加することが示されています。研究では、アクティビティリングを一貫して閉じているユーザーは、視覚的なフィードバックがない人と比較して40%高いモチベーションレベルと30%より良い目標達成率を報告しています。「リングを閉じる」の満足のいくアニメーションはドーパミン放出をトリガーし、健康的な行動を強化します。
このプロジェクトは素晴らしい出発点です。今後はこれらの概念を使用して、独自のカスタムデータ可視化、ロードインジケーター、または想像できる他の円形UI要素を構築できます。
次のステップ:
- ストロークに単色ではなくグラデーションを追加してみてください
- ボタンをクリックときに進捗値をアニメーション化します
- リングの中心にラベルまたはアイコンを追加します
より高度な可視化については、大規模なデータセット向けの**performant D3 health chartsの構築、または可視化がすべての人に確実に機能するようにするWCAGに従ったアクセス可能なデータ可視化**を参照してください。
リソース
- Framer Motionドキュメント:https://www.framer.com/motion/
- SVG
<circle>のMDNドキュメント:https://developer.mozilla.org/en-US/docs/Web/SVG/Element/circle stroke-dasharrayのMDNドキュメント:https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray
クイックリファレンス
| 側面 | 主要情報 |
|---|---|
| トピック | 開発 |
| 関連性 | 患者教育と意識向上 |
| アクション | 医療提供者との相談 |
| 頻度 | ケアチームのおすすめ通り |
| エビデンス | 現在の臨床ガイドラインに基づく |
よくある質問
Q: これが自分に該当するかどうかはどうすればわかりますか? A: 個人的な病歴と家族歴について医療提供者と相談し、個人的なリスク因子を評価し、適切な次のステップを決定してください。
Q: 医師に何を買うべきですか? A: 良い質問には次のようなものがあります:「結果はどういう意味ですか?」「オプションは何ですか?」「どんな生活習慣改变が役立ちますか?」「いつフォローアップすべきですか?」
Q: 健康を改善できる生活習慣改变はありますか? A: ほとんどの健康状態は、基本的な対策から恩恵を受けます:バランスの取れた栄養、定期的な身体活動(週150分以上)、質のよい睡眠(7〜9時間)、ストレス管理、および禁煙。
免責事項
この記事で提示されたアルゴリズムと手は、技術教育の目的のためのものではありません。臨床的な検証を受けておらず、医学的診断や治療決定に使用るべきではありません。医学的アドバイスについては、常に資格のある医療専門家にご相談ください。