GitHubの画像diffはどのように実装されているのか
GitHubの画像diffって便利だし何だか面白いですよね。
ところで、このSwipe Diff機能がどのように実現されているか、皆様はパッとわかるでしょうか?
・
・
・
私は20秒ほど考えてわからなかったので、DevToolsから仕組みを調べてみました。
実装方法
わかってしまえば非常にシンプルで、
- 2枚の画像を重ねる
- 上層の画像のWrapper幅を、スライダーの位置と連動させる
という発想でできていました。
上層のWrapper幅が小さくなると、下に隠されていた画像が覗いて、あたかも連続的に変化しているように見えるという寸法ですね。
実装
Reactで簡易的に実装したコードです。マウスホバー/スワイプで境界を動かせます。
(スマホからだとスワイプが少し難しいためPCで開いたほうがわかりやすいです)
要点のみ解説すると、
- onPointerMoveイベントでポインタのX座標を取得し、上層画像のWrapperのwidthと連動させる
- Wrapperに
overflow-x: hidden
を付け、幅が狭まったときに下の画像が見えるようにする
といった感じです。
Onion Skin diffの作り方
もう一つのモードであるOnion Skin Diffの作り方も見ていきましょう。
Onion Skin diffは、スライダーの位置に応じて新しい画像が浮かび上がったり消えたりするモードです。
実装方法
先ほどの応用で、Wrapper幅と連動させる代わりにopacity
と連動させるだけですね。
実装
今回は面倒なのでスライダーは実装していません。画像領域内を横にホバー/スワイプしてみてください。
Swipe diffコンポーネントとの差分は実質的にはこの1行のみです。
style={{
- width: `${xPosRatio * 100}%`
+ opacity: xPosRatio
}}
Differenceモードの作り方
今は無くなってしまいましたが、以前は新旧画像で変化した箇所だけ浮かび上がらせる Difference というモードもあったようです。
せっかくなので最後にこちらを実装してみましょう。
追加された眼鏡の部分だけが浮かび上がっている
実装方法
以前はPhotoshopのblend modeを模倣したOSSを使って実装していたとのことなので、現代ではCSSのmix-blend-modeプロパティを使えば同じものが実装できそうです。
新旧画像を重ねて同じ色の箇所を打ち消しあうようにすれば、変更された箇所だけ浮かび上がるはずなので、mix-blend-mode: difference
を使っていきます。
実装
ポインタ連動機能を削除してmix-blend-mode: difference
を加えただけで、全く同じものができました。
+.deleted-image {
+ mix-blend-mode: difference;
+}
Discussion