【React】React + GSAP(TextPlugin)で1文字ずつ表示されるアニメーションを実装する(Next.js)
先日に引き続き、今回も GSAP で実装したアニメーションをご紹介。 今回は、タイプライター風アニメーションというのか、1 文字ずつ文字が表示されるアニメーションを実装した。
TextPlugin
TextPlugin は、GSAP のプラグインの 1 つである。このプラグインの特徴は以下の通りである。
- DOM要素にテキストコンテンツを 1 文字ずつ埋め込む(区切り文字をスペースなどに設定することで、1 単語ごとに埋め込むこともできる)
- アニメーションが終了すると、DOM要素にはテキストが埋め込まれて、完全に置き換えられる
- 表示速度を設定できる
使い方
import { gsap } from 'gsap' import { TextPlugin } from 'gsap/TextPlugin' useEffect(() => { if (process.browser) { gsap.registerPlugin(TextPlugin) setAnimation() } }, []) const setAnimation = () => { gsap.to("#テキストを挿入するDOM要素", { duration: 2, //アニメーション時間(秒) text: { value: "This is the new text", //表示するテキスト delimiter: "", //区切り文字 }, ease: "ease", // アニメーションのタイミング・進行割合を指定する }) }
詳細は、公式ドキュメント を参照して欲しい。
React での実装例
React で実装するにあたり、TextAnimationコンポーネントを作成した。
テキストを埋め込む領域を確保しておかないと、改行されるたびに表示領域が大きくなるので、事前に Height を確保することにした。処理の流れは以下の通りである。
- DOMがレンダリングされる
- DOM内のテキストを読み込む
- DOMの高さを取得する
- DOM内のテキストを削除し、高さを設定する
- scrollTrigger をトリガーとして、テキストアニメーションを行う
import { ReactElement, useCallback } from 'react' import { gsap } from 'gsap' import { TextPlugin } from 'gsap/TextPlugin' import { ScrollTrigger } from 'gsap/ScrollTrigger' type Props = { children: React.ReactNode section: string } const TextAnimation = (props: Props): ReactElement => { const textRef = useCallback((node) => { if (node !== null) { const text = node.innerHTML //テキストを読み込む const height = node.clientHeight //高さを取得する node.innerHTML = '' //テキストを削除する node.style.height = height + 'px' //高さを設定する setAnimation(text) } }, []) const setAnimation = (text) => { const numText = text.length const selector = '#' + props.section gsap.registerPlugin(TextPlugin) gsap.registerPlugin(ScrollTrigger) gsap.to(`${selector} .animation-text`, { duration: numText * 0.03, text: { value: text, }, ease: 'none', scrollTrigger: { trigger: selector, start: 'top 40%', end: 'bottom 40%', }, }) } return ( <p ref={textRef} className="animation-text"> {props.children} </p> ) } export default TextAnimation
今回、useCallback
を使って、コールバック形式の ref を使っている。最初は、useRef
を使って実装したが、その場合に TextAnimation のコンポーネントを使い回すことができなかった。(どのコンポーネントでも、同じテキストが表示されてしまった。)
useRef の特徴は以下の通りである。
- useRef は毎回のレンダーで同じ ref オブジェクトを返す。
- useRef は中身が変更になってもそのことを通知しない。.current プロパティを書き換えても再レンダーは発生しない。
詳細は、公式ドキュメント useRef 参照。
実際に、TextAnimation を使う場合は、以下のようにする。
import { ReactElement } from 'react' import TextAnimation from '~/components/TextAnimation' const Component = (): ReactElement => { return ( <section id="souseki"> <TextAnimation section="souseki"> 吾輩は猫である。 <br /> 名前はまだ無い。どこで生れたかとんと見当がつかぬ。 <br /> 何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。 <br /> 吾輩はここで始めて人間というものを見た。 <br /> しかもあとで聞くとそれは書生という人間中で一番獰悪な種族であったそうだ。 <br /> 夏目漱石 </TextAnimation> </section> ) } export default Component
まとめ
GSAP の TextPlugin の使い方を簡単に紹介した。簡単にタイプライター風のアニメーションが実現できて、便利であった。
また、今回の実装を通して、React の useRef やコールバック形式の ref についても学ぶことができた。
それでは、ステキな開発ライフを。
関連する記事
【React】React + GSAP(ScrollTrigger)でスクロールアニメーションを実装する(Next.js)
スクロールアニメーションを実装するにあたり、GSAP の ScrollTrigger を使うことで簡単に実装できたので、今回はその実装方法を紹介する。
GSAP とは
GSAP(GreenSock Animation Platform)は、高速・軽量のHTML5 アニメーションライブラリである。主なモジュールとして、アニメーションを実装する TweenMax
とそのアニメーションのタイムラインを制御する TimelineMax
がある。そして、2020年5月、スクロールアニメーションのためのプラグインライブラリ「ScrollTrigger」がリリースされた。
ScrollTrigger の特徴
様々な機能が盛り沢山であるが、簡単に便利だと思った機能を紹介する。
- 特定の要素に、それぞれ個別にアニメーションを設定できる
- トリガー位置を柔軟に設定できる
- 特定のスクロール位置で要素を固定する、ピン留めも設定可能
- onEnter, onLeaveなどのcallbackが設定できる
- トリガー位置のマーカー表示が簡単にできる
- パフォーマンス最適化
詳細は、公式ドキュメント を参照して欲しい。
React での実装例
インストール
GSAPのモジュールをインストールする。(詳細は、Docs 参照。)
$ yarn add gsap
使い方
gsap.registerPlugin()
メソッドで、ScrollTrigger
を登録する。(実装では、Next.js を使用したため、client側でのみ実行したいため、process.browser
が true
の場合のみ登録している。)
import { gsap } from 'gsap' import { ScrollTrigger } from 'gsap/ScrollTrigger' useEffect(() => { if (process.browser) { gsap.registerPlugin(ScrollTrigger) setAnimation() } }, []) const setAnimation = () => { gsap.to('#アニメーションさせたい要素', { // アニメーション内容 scrollTrigger: { trigger: '#トリガー要素', start: 'top 40%', //開始時のトリガー条件 end: 'bottom 40%', //終了時のトリガー条件 onEnter: () => {}, //スクロールイン時 onEnterBack: () => {}, //スクロールバック時 markers: true // マーカー表示 } }) }
gsapのメソッドはたくさんあるので、ここでは主に使うものだけを紹介する。
- gsap.to():(現在の状態) → Bになる状態を設定する
- gsap.from():A → (現在の状態)になる状態を設定する
- gsap.fromTo():A → Bになる状態を設定する
- gsap.timeline():タイムライン制御をする際に用いる
const tl = gsap.timeline({repeat: 2, repeatDelay: 1}) tl.to("#id", {x: 100, duration: 1}) tl.to("#id", {y: 50, duration: 1}) tl.to("#id", {opacity: 0, duration: 1})
そして、gsapのメソッドの引数である、第二要素の「アニメーション内容」の部分に、scrollTrigger
を定義する。
ちょっとわかりにくい、アニメーションの開始時/終了時のトリガー条件は、以下の通りである。
start: "${#トリガー要素の基準点} ${#ブラウザの画面の位置}"
start: 'top 40%'
の場合、「トリガー要素のトップが、ブラウザのトップから40%のところにきたら、アニメーション開始」となる。
参考例
以下のコードは、スクロールに応じて、文字が下から上にフワッと浮き上がってくるようなアニメーションである。
import { ReactElement, useEffect } from 'react' import { gsap } from 'gsap' import { ScrollTrigger } from 'gsap/ScrollTrigger' const Component = (): ReactElement => { useEffect(() => { if (process.browser) { gsap.registerPlugin(ScrollTrigger) setAnimation() } }, []) const setAnimation = () => { gsap.fromTo( '#wrapper-a p', { opacity: 0, y: 10 }, //fromの設定 { //toの設定 opacity: 1, y: 0, duration: 2, scrollTrigger: { trigger: '#wrapper-a', start: 'top center', //要素のトップが、画面の中央まできたら開始 end: 'bottom center', //要素のボトムが、画面の中央まできたら終了 onEnter: () => { console.log('scroll In') }, onEnterBack: () => { console.log('scroll Back') }, }, } ) } return ( <div className="wrapper" id="wrapper-a"> <p>TEST ANIMATION</p> </div> ) } export default Component
まとめ
GSAP の ScrollTrigger の使い方を簡単に紹介した。細かい設定までできるので、使い勝手がよく、とても便利であると思えた。
それでは、ステキな開発ライフを。
【JS / React】ブラウザバックを制御する
今回、HP 内で意図的にブラウザバックを制御して、UX を向上させられるようにしたので、実装方法を紹介する。
ブラウザバックの制御に関して
ブラウザバックを制御するためには、セッション履歴を操作する必要がある。制御の流れは以下である。
- ページがレンダリングされたら、ダミーのセッション履歴を追加しておく
- ユーザーがブラウザの「戻る」ボタンを押す(1. でダミーの履歴を挿入していたので、実際にはブラウザバックが動作しない)
popstate
イベントが発火する- 条件に応じて、以下を実行する
a. ブラウザバックをする
b. 任意の操作をする(再度、ブラウザバックをさせないように、ダミーのセッション履歴をまた追加しておく。)
ブラウザのセッション履歴の操作は、History API で実施する。
ダミーのセッション履歴は、History.pushState() を用いる。
history.pushState(null, null, null)
実装例
今回示す例では、同ページに、Step1 と Step2 のコンポーネントがあり、stateのstep
に応じて、コンポーネントを切り替える。
ただし、step2 の場合、ブラウザバックを禁止して、ブラウザバックせずに、step1に戻るようにする。(特にスマホの操作において、ブラウザバックでstep1に戻るようにしておくと、UXが向上する。)
import { ReactElement, useEffect, useRef, useState } from 'react' import Step1 from '~/components/Step1' import Step2 from '~/components/Step2' const Component = (): ReactElement => { const [step, setStep] = useState(1) const stepRef = useRef(null) useEffect(() => { history.pushState(null, null, null) stepRef.current = step window.addEventListener('popstate', overridePopstate, false) return () => window.removeEventListener('popstate', overridePopstate, false) }, []) useEffect(() => { stepRef.current = step }, [step]) const overridePopstate = () => { if (stepRef.current === 2) { history.pushState(null, null, null) setStep(1) } else { history.back() } } return ( <section> <div className="wrapper"> {step === 1 ? ( <Step1 goToNext={setStep(2)} /> ) : ( <Step2 goToBack={setStep(1)} /> )} </div> </section> ) } export default Component
特筆すべきは、リスナ内関数では state の変更を参照することができず、値渡しになっている。そのため、useRef
を用いて、ref.current
で参照するようにした。
まとめ
History API があることは知っていたが、今回初めて使って実装を行った。ブラウザバックに関して、意図したように制御ができてよかった。
それでは、ステキな開発ライフを。
【GitHub Actions】Next.js + Firebase Hosting をビルド&デプロイする
GitHub Actions は、最高に便利である。
今回は、Next.js で作った HP を Firebase Hosting に自動デプロイする GitHub Actions を紹介する。
GitHub Actions の構成
今回、Actions のトリガーは以下の2つを想定している。
- main ブランチに push された場合
- Contentful で、記事が更新された場合(repository_dispatch:GitHubのWebhookイベントを使用する)
そして、処理の流れは以下の通りである。
- リポジトリをチェックアウトする
- node環境のセットアップ
- yarn install
- Google Cloud SDKをセットアップする
- envファイルをダウンロードする
- yarn build & yarn export(SSG)
- Firebase Hosting にデプロイする
- Slackにデプロイ完了を通知する
workflowの新規作成
GitHub 上でも、Actions のタブから workflow の template を選択して、新規作成することができる。(とてもわかりやすくて、便利。)ただ、私はローカルのエディタで作成して、GitHub に push を行った。
1. root ディレクトリに、.github/workflows
フォルダを作成する。(←フォルダ名が「workflow」の場合、Actionsとして認識されないので、注意。私は一度フォルダ名を間違えて、末尾のs
を抜かしてしまい、やらかしたw)
$ sudo mkdir -p .github/workflows
2. 任意の名前で設定ファイル(.yml)を作成する。今回は、Firebaseへのデプロイ用だったため、firebase.yml
とした。
$ sudo touch firebase.yml
name: Deploy to Firebase Hosting on: push: branches: - main repository_dispatch: jobs: deploy: name: Build & Deploy runs-on: ubuntu-latest strategy: matrix: node-version: [14.x] steps: - name: Check out code uses: actions/checkout@v2.0.0 - name: Set up Node uses: actions/setup-node@v2.1.2 with: node-version: ${{ matrix.node-version }} - name: Get yarn cache id: yarn-cache run: echo "::set-output name=dir::$(yarn cache dir)" - uses: actions/cache@v2 with: path: ${{ steps.yarn-cache.outputs.dir }} key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- - name: yarn install if: steps.yarn-cache.outputs.cache-hit != 'true' run: yarn install --frozen-lockfile - name: Set up gcloud CLI uses: google-github-actions/setup-gcloud@master with: version: 'latest' service_account_email: ${{ secrets.GCP_SA_EMAIL }} service_account_key: ${{ secrets.GCP_SA_KEY }} export_default_credentials: true - name: Download local environment variables run: | gsutil cp gs://<---storage path---> .env - name: yarn build & export run: yarn export - name: Deploy to Firebase uses: w9jds/firebase-action@master with: args: deploy --only hosting env: FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }} - name: Notification to Slack env: SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} uses: pullreminders/slack-action@master with: args: ‘{\”channel\”:\”<--channelId-->\",\"text\”:\”Firebaseへのデプロイが完了しました\”}’
特筆しておくべきところは特にないが、actions/cache を使って、キャッシュを再利用することでビルドを効率的にしている。
依存関係をキャッシュしてワークフローのスピードを上げる 参照
また、設定ファイルの中に出てくる${{ secrets.XXXX }}
はGitHubのSettings >
Secrets の Repository secrets に登録しておく必要がある。
以降で、設定する項目の取得方法を紹介する。
GCPの設定
1. GitHubActions用のサービスアカウントを発行する
基本的には、上記のリンク先の操作方法を参照して欲しい。 ただ、ロール設定に関しては、今回 Cloud Storageの読み取りができればいいので、その権限のみを付与する。
以下のようなjsonファイルがダウンロードされる。
{ "type": "service_account", "project_id": "project-id", "private_key_id": "key-id", "private_key": "-----BEGIN PRIVATE KEY-----\nprivate-key\n-----END PRIVATE KEY-----\n", "client_email": "service-account-email", "client_id": "client-id", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://accounts.google.com/o/oauth2/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/service-account-email" }
3. GitHub の Secrets に登録する
GCP_SA_EMAIL
は、上記のclient_email
を GitHub の Secrets にコピーするだけでいい。
GCP_SA_KEY
は、ダウンロードしたサービスアカウントキーを base64 エンコードした文字列をいれる必要があるので、以下のコマンドを実行する。
$ cat service-account-key.json | base64 | pbcopy
上記コマンドを実行することで、クリップボードにエンコードされた文字列が保存されるので、あとは GitHub の Secrets に貼り付けるだけでいい。
Firebaseの設定
GitHub Action for Firebase を参照
GCP_SA_KEY
を取得しているので、サービスアカウントの権限を追加することで、FIREBASE_TOKEN
は不要であるが、今回Cloud Storageの権限しか付与しなかったので、FIREBASE_TOKEN
を取得するようにした。
ローカルで、以下のコマンドを実行する。
$ firebase login:ci ✔ Success! Use this token to login on a CI server: 1//**** Example: firebase deploy --token "$FIREBASE_TOKEN"
ブラウザが開き、ログインすることで、トークンが発行される。この FIREBASE_TOKEN
を GitHub の Secrets に登録する。
Slack Appの設定
2. GitHub の Secrets に登録する
Slack App の OAuth & Permissions
にある OAuth Tokens for Your Team の Bot User OAuth Access Token
の値をコピーして、GitHub の Secrets に登録する。
3. チャンネルIDを取得する
アプリの場合、Slack Bot のメッセージを投稿したいチャンネルを選択して、右クリック > リンクをコピー をする。
https://XXXX.slack.com/archives/C0154PW1XUM
末尾の「C0154PW1XUM」がチャンネルIDとなる。
これで全ての設定が完了である。
処理実行
実際に、mainブランチに push して動作確認を行う。
実行結果に関しては、GitHub の Actions で確認することができる。エラーが発生した場合は、各実行項目でのエラー内容なども確認できるので、内容に応じて修正を行う。
まとめ
GitHub Actions の設定方法を紹介した。
正直、これが最適なのかはまだわかっていない。今後も、より最適な方法を模索していきたい。
それでは、ステキな開発ライフを。
【SSH】踏み台サーバーを経由した多段SSH接続のやり方(.ssh/configの利用)
最近、サーバー運用などを勉強している。
VPC 環境を構築する時に、サブネットを多段に分割してセキュリティレベルを分け、踏み台サーバー経由でしかSSH接続できないようにする構成が多いようだ。
踏み台サーバーを経由した多段 SSH 接続で、AWS(EC2)で運営されているサーバーにアクセスしたので、備忘録として残しておく。
(通常の)SSH 接続
まずは、通常の SSH 接続方法を確認する。
$ ssh -i [秘密鍵] [ユーザー名]@[アドレス] # EC2の場合の例 $ ssh -i ~/.ssh/config.d/aws.pem ec2-user@xxx.xxx.xxx.xxx
この場合、毎回秘密鍵のパスやログインユーザー名、アドレスを思い出して、入力するのは、面倒である。そこで、.ssh/config
に設定を記載しておくことで、ホスト名のみでSSH接続ができるようにしておく。(秘密鍵は、.ssh/config.d
に入れるようにした。)
$ sudo mkdir ~/.ssh/config.d
$ sudo vim ~/.ssh/config
Host appX-server HostName xxx.xxx.xxx.xxx User ec2-user IdentityFile ~/.ssh/config.d/aws.pem StrictHostKeyChecking no IdentitiesOnly yes
このように設定しておくことで、以下のようにして SSH 接続することができる。
$ ssh appX-server
非常に便利である。
多段 SSH 接続
いよいよ本題である。
構成
まずは、簡単であるが、構成図を示す。public の踏み台サーバーを経由して、アプリケーションサーバーにアクセスする。
接続方法
基本的には、.ssh/config
に設定を書いて接続を行うが、上記に沿って、コマンドで接続する方法も記載しておく。
ProxyCommand
を使う。
$ ssh -i [(app)秘密鍵] -o ProxyCommand='ssh -i [(step)秘密鍵] -W %h:%p [(step)ユーザー名]@[(step)アドレス]' [(app)ユーザー名]@[(app)アドレス]
- -W:フォワードの設定を行う
- %h:ホスト
- %p:ポート番号
上記が置換されることで、多段 SSH 接続ができる。
また、.ssh/config
は以下の書くことができる。
Host step-server
HostName xxx.xxx.xxx.xxx
User ec2-user
IdentityFile ~/.ssh/config.d/aws.pem
StrictHostKeyChecking no
IdentitiesOnly yes
Host appX-server
HostName xxx.xxx.xxx.xxx
User ec2-user
IdentityFile ~/.ssh/config.d/application.pem
ProxyCommand ssh -W %h:%p step-server
StrictHostKeyChecking no
IdentitiesOnly yes
$ ssh appX-server
.ssh/config
なくしては、踏み台を介した SSH 接続はできないですね。
まとめ
今回は、サーバーアクセスの要である SSH 接続について整理してみた。
それでは、ステキな開発ライフを。