Designshipのライブ配信サイトについて
2021年10月23日、24日にデザインカンファレンスである、Designship 2021が開催されました。
Designship自体については公式のnoteの方で紹介されています。
この記事では、Designshipのライブ配信サイトについて書きたいと思います。
既に配信サイトを作ることになった経緯や全体の構成、機能については、sakito氏の「デザインカンファレンスのライブ配信サイトを開発した話」で紹介されています。また、技術的に工夫した点については、kenchan0130氏の「Designship 2021のライブ配信サイトの開発周りについて」で紹介されています。
そのため、今回はUIの実装面で工夫したことについて書きたいと思います。
ライブ配信サイトの概要
ライブ配信サイトはこんな感じになっています。
⬇︎ログイン画面(左がPC、右がスマホ)
⬇︎視聴画面(左がPC、右がスマホ)
⬇︎タイムテーブル(左がPC、右がスマホ)
視聴画面で用意されているコンテンツ
・視聴枠
・セッション情報(概要、スピーカー情報)
・アナウンス枠(運営からのお知らせ枠)
・チャット
・字幕表示
・通常モード/シアターモードの切り替え
・タイムテーブル
・Twitterで#Designship2021で呟くためのボタン
・バナー(スポンサーバナー、グラレコやアーカイブ販売ページに遷移するためのバナー)
・フッター(リンク集)
上記に示したように結構盛り沢山のコンテンツです。
これを約2ヶ月間で作りました。
ライブ配信サイトのディレクトリ構成について
ライブ配信サイトは以下のような簡易的なAtomic Designで設計・実装しています。
・componentsディレクトリ
→ PCとスマホのコンポーネント群、allは共通で使うもの
・styleディレクトリ
→ 共通のstyleを定義
(下記の構成に載っているものはUIに関わる部分だけです。また、2ヶ月という短期間での開発だったため、正確なAtomic Designにはなっていません。)
app
┣ components
┃ ┣ all
┃ ┃ ┗ organisms
┃ ┃ ┣ Layout
┃ ┃ ┗ Video
┃ ┣ hooks
┃ ┃ ┗ usePrevious
┃ ┣ pc
┃ ┃ ┣ organisms
┃ ┃ ┃ ┣ Announcement
┃ ┃ ┃ ┣ Banners
┃ ┃ ┃ ┣ Chat
┃ ┃ ┃ ┣ Footer
┃ ┃ ┃ ┣ Header
┃ ┃ ┃ ┣ Links
┃ ┃ ┃ ┣ Loading
┃ ┃ ┃ ┣ LoadingLayer
┃ ┃ ┃ ┣ Password
┃ ┃ ┃ ┣ SessionNext
┃ ┃ ┃ ┣ SessionOverview
┃ ┃ ┃ ┣ Sponsors
┃ ┃ ┃ ┣ Timetable
┃ ┃ ┃ ┗ ViewModeChange
┃ ┃ ┗ template
┃ ┃ ┣ LoadingPage
┃ ┃ ┣ LoginPage
┃ ┃ ┗ ViewPage
┃ ┗ sp
┃ ┣ organisms
┃ ┃ ┣ ActionButtons
┃ ┃ ┣ Announcement
┃ ┃ ┣ Banners
┃ ┃ ┣ Chat
┃ ┃ ┣ ChatButton
┃ ┃ ┣ Footer
┃ ┃ ┣ Header
┃ ┃ ┣ Links
┃ ┃ ┣ Loading
┃ ┃ ┣ LoadingLayer
┃ ┃ ┣ Password
┃ ┃ ┣ SessionNext
┃ ┃ ┣ SessionOverview
┃ ┃ ┣ Sponsors
┃ ┃ ┗ Timetable
┃ ┗ template
┃ ┣ LoadingPage
┃ ┣ LoginPage
┃ ┗ ViewPage
┣ pages
┃ ┣ 404.tsx
┃ ┣ _app.tsx
┃ ┣ _document.tsx
┃ ┣ index.tsx
┃ ┣ loading.tsx
┃ ┗ view.tsx
┣ style
┃ ┣ settings
┃ ┃ ┣ variables
┃ ┃ ┃ ┣ color.scss
┃ ┃ ┃ ┣ font.scss
┃ ┃ ┃ ┗ index.scss
┃ ┃ ┗ index.scss
┃ ┣ default.scss
┃ ┣ index.scss
┃ ┗ reset.scss
┗ constant.ts
なぜこの設計にしたのか
・Next.js + TypeScriptという技術構成
・PCとスマホでUIがだいぶ違うので、スタイルでメディアクエリを使ってスマホのUIを作るのではなく、そもそも別のものとして作るようにする
・Figmaのデータに合わせてコンポーネント化していきたい
以上の要件から簡易的なAtomic Designを採用しました。
また、共通のstyleを定義しておくことにより、デフォルトのスタイルを変えやすくしています。デフォルトのスタイルを変えやすくすることにより、フォントの変更が入った時などの全体のスタイルに対して変更が加わる時の変更コストの削減になります。
工夫点
スタイルの指定
運用面を意識して、scssの記述はBEM方式で記述しています。
BEM方式で記述するメリットとしては、
・スタイルを当てる全てのDOM(element)に対して、classを付与するので後で変更が入った時でも他のelementに干渉することなくスタイルを変えることができる
・ライブ配信サイトの暗転時などのスタイルを定義する際も、modifierで
.〇〇 {
&.〇〇--break {
...
}
&.〇〇--onAir {
...
}
}
のように指定するため、わかりやすく実装できる
このメリットにより、実装者以外も読みやすいscssとなり運用しやすくなります。
余白(margin)の取り方
コンポーネント間の余白は、各コンポーネント内では取らずに、templateの方で取るようにしています。これは、ページのレイアウトという概念はtemplateの方に寄せたいためです。
また、余白はコンポーネントの上側で取るようにしています。
コンポーネントの上側で取ることによりtemplateでコンポーネントを削除する場合に削除対象のコンポーネントだけ消せば、scssを触ることなく削除す対応ができます。
(下の図は下余白で余白を取った場合と、上余白で余白を取った場合の違いを示しています。)
じゃあ、コンポーネント1を消したら、コンポーネント2の上余白が残ってしまうのではと思うかもしれませんが、隣接セレクタでコンポーネント1とコンポーネント2が隣接したら、コンポーネント2に上余白を取ると指定しておけば、コンポーネント1が削除されても余白が残ることはありません。
定数の管理
ページのステータス(開始前、休憩時、セッション中、障害発生中、終了後)、閲覧モード(通常モード、シアターモード)などのページ全体に関すする定数は、コンポーネントとは別の場所(ディレクトリ構成の中のconstant.ts)で管理することにより、定数が散らばったり、ダブって似たような定数が生み出されないようにしています。
ハマった点
暗転時にセッション情報のマスキングが浮いた感じになる
これを解消するために、トランジションが完了したかどうかを管理するstate(isTransitionEnd)を用意して、
const [isTransitionEnd, setIsTransitionEnd] = useState(false);
------
onTransitionEnd={() => {
setIsTransitionEnd(true);
}}
というふうに、トランジションが完了したらisTransitionEndのフラグをtrueにするようにしています。
この、isTransitionEndを見て、
useEffect(() => {
if (isTransitionEnd) {
セッション情報にマスクをつける処理をする
}
}, [isTransitionEnd]);
のように、isTransitionEndが変化したら発火するuseEffectを定義して、その中でisTransitionEndのフラグがtrueだったら、セッション情報にマスキングをつける処理をするようにしました。
こうすることにより、トランジションが完了した後にセッション情報にマスキングがかかるようになりました。
今回は一旦このように解決しましたが、他にももっと良い方法があるかと思います。なので、もし良い方法があれば教えていただきたいです。
終わりに
当日はライブ配信サイト、カンファレンス自体共に大きな問題はなく、無事に終了することができました。
もちろん、配信サイトは短期間での開発だったので、改善するべきところはたくさんありますが、それは来年以降で改善していきたいと思っています。
当日参加できなかった方はアーカイブを販売しているので、ぜひこの機会にご覧ください。