React.jsのアニメーションライブラリ「Framer Motion」を使ってみよう。制作事例を元に解説

Frame Motion

「Framer Motion」は、React.jsで使用できるオープンソースのアニメーションライブラリ。複雑なレイアウトを簡単にアニメーション化できる、とても優秀なライブラリです。

今回は、最先端のWebデザイン制作におすすめのFramer Motionの使いかたについて解説します。

今回制作するアニメーション

Framer Motion Animation

▲出典:UX Planet

上記が今回制作するアニメーションです。

いろいろな要素が動いていますが、おもな構成要素は以下のとおり。

  • 上下から交互にあらわれる画像
  • 左上のタイトル
  • 拡大縮小する画像
  • 画像下部から出てくる詳細情報

では、いよいよアニメーションを制作していきましょう。

アニメーションその1. 上下から交互にあらわれる画像

白いカーテンを上下に引くようなこのアニメーションには、「カーテンを上げる」「カーテンを下げる」という2つの異なる動きが必要になるため、variantを使用します。

const alternateはvariantで、あとで使用できるアニメーションを複数保持しています。また、ベジェ曲線の標準的な表記方法を使って簡単に書けるように、transitionを定義しました。

export default function Curtain({ number }) {
const alternate = {
  even: {
   y: “100vh”,
   transition: transition,
  },
  odd: {
   top: 0,
   y: “-100vh”,
   transition: transition,
  },
};
const transition= {
duration: 1,
ease: [1,.01,.49,1.05],
delay: 0.4
}
... other code ...

これで左端から数えて偶数のカーテンが下がり、奇数のカーテンが上がるというアニメーションができるようになります。

つぎは、これらにアニメーションを割り当てましょう。

return (
<motion.div
id="curtains">
{[…Array(number)].map((x, i) => (
<motion.div
animate = { i%2 == 0 ? “odd”: “even” }
className=”curtain”
variants={alternate}
></motion.div>
))}
</motion.div>
); // the return closing
} // the full function closing

これらはコピー&ペーストでOK。

このうち、numberは以下のような関数に渡す値です。

function Curtains({number}) {
 return(“what we need”);

カーテンの枚数を決めるのに使う予定ですが、必須ではありません。

map関数のiを使って2で割った残りを計算し、偶数か奇数のどちらにあたるかを把握し、正しいvariantを割り当てます。

animate = { i%2 == 0 ? “odd”: “even” }

あとは必要なものは、スタイルだけ。

CSSだけでなく、motion.div内にinitialを使って直接指定することも可能です。

initial = {{ background: “white”, height: "100vw" }} // for example

こうすることで、インデックスと数学に基づいた特別なルールを定義できます。

CSSバージョンは以下のとおり。

#curtains { /* this is the container of all curtains */
width: 100vw;
position: absolute;
overflow: hidden;
top: 0;
left: 0;
}
.curtain { /* a single curtain */
background: white ;
borderRight: 1px rgb(247,247,247) solid;
height: 100vh;
width: 25vw; /* change if you want more columns */
float: left;
display: inline-block;
}

カーテンが動きまわったり、レイアウトが変わったりしないように、コンテナのoverflowをhiddenにしましょう。

アニメーションその2. 左上のタイトル

最近のWebデザインでもよく見かける、下からニュッと現れるアニメーション。さまざまな場面で応用できるので、ぜひ参考にしてみてください。

ただ下から現れるのではなく、見えないブロックに切り取られているように見せたいので、overflowを活用します。

<span id=”overflower”>
<motion.h1
initial={{
opacity: 0,
y: “100%”,
}}
animate={{
opacity: 1,
y: 0,
transition: {

duration: 0.45, delay: 1.4, ease: “easeInOut”},
}}
>
photographer
</motion.h1>
</span>

タイトルには下から上に動くシンプルなアニメーションをつけました。

切り取りには以下のようにCSSを使っています。

#overflower {
display: block;
overflow: hidden;
}
#overflower h1 {
font-family: “Volux”;
color: white;
font-size: 80px;
position: relative;
width: 50%;
padding: 30px;
}

アニメーションその3. 拡大縮小する画像

先ほどと同様の原理で、画像の動きにもvariantを使います。

画像のアニメーションは2種類。ひとつは画像が表示された際のアニメーション、もうひとつはホバー時のアニメーションです。

function Gallery() {
const hover = {
scaleX: 1.1,
scaleY: 1.1,
filter: “brightness(1)”,
transition: {
duration: 0.3,
ease: “linear”,
},
};
const zoom = {
initial: { scale: 1, x: -80, filter: “brightness(0.78)” },
animate: { x: 0, scale: 1.2, transition: { duration: 1.8 } },
};

画像をすこし拡大し、左(-80)から右(0)に移動させます。またホバーしたときに再度拡大し、明るさを0.78から1まで上げましょう。

scaleではなくscaleXとscaleYを使った理由は、ホバーをはずす際にFramer Motionが1.8秒のアニメーション時間を使わないようにするため。不要な機能に対するトリックのようなものです。

returnの一部も見てみましょう。

return (
<> { /** this is a placeholder **/
<div id=”gallery”>
<div className="item">
<motion.img
variants={zoom}
initial=”initial”
animate=”animate”
whileHover={hover}
src=”assets/pic4.jpg”
></motion.img>
<Detail title=”Araki Toshida” role=”photographer” />
</div>
... other "items"
...

画像と<Derail />ブロックをどちらも保持する「item」がありますね。

画像の親コンテナ(item)を使うことで、画像のスケーリングによるoverflowを防ぎましょう。

スタイリングはとてもシンプルです。

#gallery {
overflow: hidden;
position: absolute;
height: 100vh;
}
#gallery .item {
width: 25%;
height: 100%;
float: left;
overflow: hidden;
position: relative;
}
#gallery .item img {
width: 100%;
height: 100%;
object-fit: cover;
}

overflowはhiddenに設定し、画像を拡大してコンテナを覆います。

アニメーションその4. 画像下部から出てくる詳細情報

もっともシンプルなブロックが、この詳細情報です。基本的にCSSのみで対応できます。

function Detail({ title, role }) {
return (
<div className=”details”>
<h3>{title}</h3>
<p>{role}</p>
<span> > </span>
</div>
);
}

たとえばtitleとroleをパラメータとして受け取り、それらを内部でテキストとして使用するシンプルなコンポーネント関数を定義してみましょう。

<Detail title={“something”} role={“something else”} />

CSSはすこし長めです。

#gallery .item .details {
background: white;
width: 100%;
position: absolute;
bottom: -300px;
height: 300px;
transition: bottom 0.5s;
transition-timing-function: cubic-bezier(0.785, 0.135, 0.15, 0.86);
padding: 20px;
}
#gallery .item:hover .details {
bottom: 0px;
}
.details h3 {
font-size: 40px;
font-family: “Bambi”;
}
.details span {
width: 80px;
height: 80px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background: black;
color: white;
position: absolute;
bottom: 30px;
right: 30px;
transition: background 0.5s, color 0.5s;
transition-timing-function: cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.details span:hover {
background: rgba(0,0,0,0);
border: 2px black solid;
color: black;
cursor: pointer;
}

基本的には以下の5つのパーツを定義しました。

  • positionとbottomによって、ブロックが見えないように設定
  • .itemにホバーした際に表示
  • テキストのスタイリング
  • ボタンのスタイル、位置をabsoluteに設定
  • ボタンのホバー

おわりに

今回はFramer Motionの使いかたをお伝えするために、シンプルなコンポーネントを定義し、値を渡し、variantなどを使ってアニメーションを実装しました。

JavaScriptのおかげで、このようなアニメーションのワークフローはCSSよりもパフォーマンスが高く、またスムーズにレンダリングできます。

最後に、モーションコーディングのヒントをお伝えします。

  • 単一のコンポーネントで考える
  • アニメーションを制作する場合、overflowはたいていの場合hiddenに設定する
  • 標準のイージングよりもベジェ曲線のほうが上品に見える

ぜひこの記事を参考に、Framer Motionを使ってみてください。

(執筆:Lorenzo Doremi 翻訳:中島あすか 編集:齊藤颯人)

※Workship MAGAZINEでは日々情報の更新に努めておりますが、掲載内容は最新のものと異なる可能性があります。当該情報について、その有用性、適合性、完全性、正確性、安全性、合法性、最新性等について、いかなる保証もするものではありません。修正の必要に気づかれた場合は、サイト下の問い合わせ窓口よりお知らせください。

30,000人以上が使う日本最大級のお仕事マッチングサービス『Workship』

「フリーランスとして、もっと大きな仕事にかかわりたいな……」
「企業で働いてるけど、副業でキャリアを広げていきたいな……」
「報酬が低くて疲弊している。もっと稼げるお仕事ないかな……」

フリーランス・複業・副業向けお仕事マッチングサービス『Workship(ワークシップ)』が、そんな悩みを解決します!

  • 30,000人以上のフリーランス、パラレルワーカーが登録
  • 朝日新聞社、mixi、リクルートなど人気企業も多数登録
  • 公開中の募集のうち60%以上がリモートOKのお仕事
  • 土日、週1、フルタイムなどさまざまな働き方あり
  • 時給1,500円〜10,000円の高単価案件のみ掲載
  • お仕事成約でお祝い金10,000円プレゼント!

登録から案件獲得まで、利用料は一切かかりません。一度詳細をのぞいてみませんか?

>フリーランス・複業・副業ワーカーの方はこちら

>法人の方はこちら

Workship CTA

SHARE

RELATED

  • お問い合わせ
  • お問い合わせ
  • お問い合わせ