はじめに
スケルトンスクリーンについて興味を持ったので、軽く実装してみました。
最先端のUIだから難しい?というイメージがありましたが、案外そうでもなかったです。
作ったもの:Web APIからユーザー一覧を表示する画面
完成:https://li8vx.csb.app/
こちらの例はReact+TypeScriptですが、別事例にも十分応用できると思います。
TL:DR
- HTML/CSSでスケルトン(?)を用意します。
- JSでAPIのローディング時にスケルトンを表示する実装を書きます。
HTML/CSS
ゼロから考えるのは面倒だったので既存コンポーネントを丸パクリしてCSSを解読しようとしました。
結果、思ったより単純でした。
コンポーネントライブラリはあの有名なMaterial-UIをを参考にしました。
https://material-ui.com/ja/api/skeleton/
このサンプルに当たっているCSSを開発者ツールで見てコピペしました。
高さはpropsで受け取れるようにしています。
Skelton.tsx
import*asReactfrom"react";import"./Skelton.css";exportfunctionSkelton({height}:{height:any}){return<divclassName="Skelton"style={{height:height}}></div>;
}Skelton.css
.Skelton{/* root */display:block;background-color:rgba(0,0,0,0.1);/* transform */transform:scale(1,0.5);transform-origin:060%;/* box model */margin-top:0;margin-bottom:0;border-radius:4px;/* animation */animation:Skeleton1.5sease-in-out0.5sinfinite;}@keyframesSkeleton{0%{opacity:1;}50%{opacity:0.4;}100%{opacity:1;}}JS
この処理のヒントも先ほどのMaterial-UIリンクをちょっと拝借させて頂きました。
APIはJSONPlaceholderを使っています。
またローディングの処理が分かるように一時停止する関数を使っています。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Using_promises
こちらの
constwait=ms=>newPromise(resolve=>setTimeout(resolve,ms));この部分を参考にしました。
UserList.tsx
import*asReactfrom"react";import{Users}from"./types";import{Skelton}from"./Skelton";import{Box}from"./Box";constwait=(ms:number)=>newPromise((resolve)=>setTimeout(resolve,ms));typeUserListState={isLoaded:boolean;users:Users};exportclassUserListextendsReact.Component<{},UserListState>{constructor(props:Readonly<{}>){super(props);this.state={isLoaded:false,users:[]};}componentDidMount(){fetch("https://jsonplaceholder.typicode.com/users").then((response)=>response.json()).then((result)=>{wait(4000).then(()=>{constusers=result;this.setState({isLoaded:true,users:users});});});}render(){const{isLoaded,users}=this.state;return(<BoxoverflowY={!isLoaded?"hidden":"auto"}><ul>{(!isLoaded?Array.from(newArray(30)):users).map((user,i)=>(<liclassName="pb4"key={i}>{user?(<div><pclassName="font-bold">{user.username}</p>
<p>{user.email}</p>
</div>
):(<div><Skeltonheight={25}/>
<Skeltonheight={25}/>
</div>
)}</li>
))}</ul>
</Box>
);}}おわりに
Material-UIに助けられました!
