事の発端
私の友人にMarinDeckを勧めたら下の画像のように聞かれた
まあ、確かにそうやね
私も結構MarinDeckを使ってて不便だなとは思ってたからこの際解決しようということでカスタムCSS, jsを書いた
MairnDeckを知らない人は下のリンク先を見るなりググるなりしてね
大変なことが発覚した(2022/4/28 00:03追記)
Android版のMarinDeckには「Display images in the original aspect ratio」の項目があって、この記事でできるようになることは公式に設定が可能なのでAndroidをお使いの方々はそっちを使うのがいいと思います
私みたいにiPadで使ってるぜって方々はこの記事が役に立つかも?
やったこと
取り敢えず調査
先の画像にあった「TweetDeckではサムネの縦長表示ができる」というのは多分「Better TweetDeckによってできる」ということなのでGithubからBetter TweetDeckのソースコードを読んだり、実際にブラウザ拡張機能を入れて開発者モードでCSSを見たりしてどうやって実現してるのかを確認した
ざっと見たところ以下のCSSが書かれていることがわかった
.media-size-medium.btd-aspect-ratio-thumbnail, .media-size-large.btd-aspect-ratio-thumbnail {
height: 0 !important;
padding-top: min(var(--btd-max-og-ratio-height), calc(var(--btd-thumb-height) / var(--btd-thumb-width) * 100%));
}
画像の高さを0にしてpadding-top
で画像表示領域を確保しているらしい
--btd-max-og-ratio-height
は最大の高さで、--btd-thumb-height
, --btd-thumb-width
はそれぞれ画像の縦と横の大きさでそれらを使って元画像と同じアスペクト比で幅いっぱいに広げてるらしい
jsの方は以下のようになっていた(一部抜粋)
quotedMediaNode.classList.add('btd-aspect-ratio-thumbnail');
quotedMediaNode.style.setProperty('--btd-thumb-height', quotedSizeObject.height.toString());
quotedMediaNode.style.setProperty('--btd-thumb-width', quotedSizeObject.width.toString());
どうやら画像に対して上のCSSを適用するようにclassを追加して、上で上げた画像の縦横の大きさパラメータをスタイルシートとして渡しているらしい
まあ、同じような動作をするように書けば動くでしょう
カスタムCSSを書く
カスタムCSSは以下のようにした
.aspect-ratio {
height: 0 !important;
padding-top: min(50vh, calc(var(--thumb-height) / var(--thumb-width) * var(--media-width)));
}
やってることはBetter TweetDeckのCSSと同じ
(クラス名とかの命名がダサいのは気にしないでください)
カスタムjsを書く
カスタムjsは以下のようにした
const aspect = () => {
const mediaItems = document.getElementsByClassName('js-media-image-link');
for (const mediaItem of mediaItems) {
const imageURL = mediaItem.style.getPropertyValue('background-image').replace(/^url\(["']?/, '').replace(/["']?\)$/, '');
const image = new Image()
image.src = imageURL;
if(mediaItem.childElementCount == 0) {
mediaItem.classList.add('aspect-ratio');
mediaItem.style.setProperty('--thumb-height', image.naturalHeight);
mediaItem.style.setProperty('--thumb-width', image.naturalWidth);
mediaItem.style.setProperty('--media-width', mediaItem.clientWidth + 'px');
// 複数画像のときは圧縮しないで全部縦に並べる
if (mediaItem.classList.contains('media-image')) {
mediaItem.classList.remove('pin-all');
mediaItem.parentElement.classList.remove('position-rel');
mediaItem.style.setProperty('border-radius', '14px');
mediaItem.parentElement.style.setProperty('margin-bottom', '8px');
mediaItem.parentElement.parentElement.classList.remove('media-grid-2', 'media-grid-3', 'media-grid-4');
mediaItem.parentElement.parentElement.parentElement.style.setProperty('height', 'auto', 'important');
}
}else {
mediaItem.classList.remove('aspect-ratio');
}
}
}
const body = document.body;
const observer = new MutationObserver(() => {
aspect()
});
const config = {
childList: true,
attributes: false,
characterData: false,
subtree: true
}
observer.observe(body, config);
aspect();
2022/4/28 00:03追記
ツイート単体を表示した際に画像が見えない状態になったので修正
2022/4/28 09:44追記
複数の画像を含んだツイートでも全ての画像が大きく表示されるようにした
こっちもやってることはBetter TweetDeckと同じだけどところどころ解説していく
const imageURL = mediaItem.style.getPropertyValue('background-image').replace(/^url\(["']?/, '').replace(/["']?\)$/, '');
const image = new Image()
image.src = imageURL;
サムネ画像はbackground-image
に設定されているっぽいので正規表現を使ってURLの文字列を取り出して、画像オブジェクトとして持つ
if(mediaItem.childElementCount == 0) {
mediaItem.classList.add('aspect-ratio');
mediaItem.style.setProperty('--thumb-height', image.naturalHeight);
mediaItem.style.setProperty('--thumb-width', image.naturalWidth);
}else {
mediaItem.classList.remove('aspect-ratio');
}
ここでやってるのはBetter TweetDeckと全く同じ
クラスを付与して先に取得した画像オブジェクトから縦横の大きさを取得してスタイルで保持
2022/4/28 00:03追記
ツイート単体を表示しているときはmedia-item
のクラスを持つタグのbackground-image
ではなくそのタグの子要素としてimgタグが画像を表示しているため、子要素が存在する場合はaspect-ratio
クラスを剥奪して画像がちゃんと表示されるようにした
2022/4/28 09:43追記
複数の画像付きツイートの場合は圧縮表示を無効化して全て大きく表示するようにした
const body = document.body;
const observer = new MutationObserver(() => {
aspect()
});
const config = {
childList: true,
attributes: false,
characterData: false,
subtree: true
}
observer.observe(body, config);
aspect();
あとはオブザーバーでbody全体を子要素も含めて監視させて変化があったとき(新たなツイートが流れてきたときとか)に上で書いた処理をやらせる
MarinDeckに適用
あとはMarinDeckの設定からカスタムCSS, jsをそれぞれ入力してリロードすれば完成
おわりに
- MarinDeckはスマホ・タブレットでTweetDeckが使える素晴らしいアプリ
- ただ、カスタムCSS, jsの書き方というかリファレンスみたいなやつを出してくれるとありがたい(これをMarinDeckに対して言うのは違う気がする)
- Android版には公式でこの機能はあるからAndroid版を使ってる人たちはアプリの設定からこれを使ったほうが楽(MarinDeckのテーマを作ってる人の引用リツイートで気づいた)