環境
main @ 0c327f7,courses/gym(未改動任何資料),
Chromium 141.0.7390.37 headless,390×844(iPhone 尺寸)。
現象
手機寬度下進入上課頁,點開影片下方的 <details>「選片理由與自我檢核」,
影片立刻縮成一小塊、播放清單同時被擠掉,而且頁面不能捲動,
沒有任何方法把影片變回正常大小,只能再把 <details> 收合。
重現與實測
COURSE=courses/gym make build
- 390×844 開
dist/gym/index.html?tab=player&play=324
(CH9「什麼是動力鏈?如何增加殺球能力?」,why + assessment 合計 425 字,
是 gym 裡最長的一個;44 個主課全部都有 why,中位數合計 296 字)
- 點一下 summary「選片理由與自我檢核」
| 狀態 |
影片尺寸 |
資訊區高度 |
清單高度 |
頁面可捲動 |
| 收合(預設) |
366×206 |
162px |
279px |
false |
| 展開(點一下) |
206×116 |
624px |
107px |
false |
390px 寬的手機上影片只剩 206px 寬,左右各空掉 92px,比一半還小;
播放清單從 279px 掉到 107px。window.scrollTo(0, 99999) 之後 scrollY 仍然是 0,
scrollHeight === innerHeight,捲不動。
資訊區再長一點(我的 fork 在影片頁多放了講者資歷、查核日期、字幕提示,實測到 1111px),
就會有 553px 的內容直接被 overflow: hidden 裁掉,同樣捲不到。
所以在原版是「影片被壓扁」,資訊欄位更多的課程會惡化成「內容根本看不到」。
根因
三個地方各自成立,湊在一起才壞:
1. .Player 在窄螢幕仍然是固定視窗高+裁切
player.css:6-15 的 height: calc(100dvh - var(--stick-top)) 與 overflow: hidden,
在 player.css:322-327 的窄螢幕區塊裡沒有被解除,只換成上下堆疊。
上下堆疊之後總內容高度會超過一個視窗,但容器仍然不許它長高、也不許捲。
2. fitFrame() 用「舞台高 − 資訊區高」搶剩餘空間(player.js:185-197)
左右並排時這個算法是對的(資訊區在影片下方、共用一欄高度);
上下堆疊時它會讓影片與資訊區互相爭高度,資訊區越長影片越小。
另外 if (avail <= 0) return; 會保留上一次算出來的 --frame-w,
所以資訊區長到一定程度之後影片尺寸就凍在殘值上,看起來像是「有時候又會好」,
實際上只是沒有重算。
watchFrame() 的 ResizeObserver 有 ro.observe(info),所以 <details> 一展開
就即時重算 fitFrame()——壓扁是當下發生的,因果很直接。
3. scrollIntoView 連文件一起捲(app.js:303)
$(".PlaylistItem.is-playing")?.scrollIntoView({ block: "nearest" })。
目前頁面不能捲所以看不出來;一旦照下面讓頁面可捲,進上課頁的瞬間影片會被推出畫面外。
修 1 之前不會顯現、修 1 之後一定顯現,所以一起提。
我的修法
/* player.css,@media (max-width: 1012px) 內 */
.Player {
grid-template-columns: minmax(0, 1fr);
grid-template-rows: auto auto;
height: auto;
min-height: calc(100dvh - var(--stick-top));
overflow: visible;
}
.Player__list { height: 70dvh; min-height: 200px; }
// player.js,fitFrame() 開頭
// 窄螢幕是上下堆疊、整頁可捲:影片按寬度定高就好,不跟資訊區搶高度
if (matchMedia("(max-width: 1012px)").matches) {
frame.style.setProperty("--frame-w", `${stage.clientWidth}px`);
return;
}
// app.js,playAt() 內,取代 scrollIntoView
const playing = $(".PlaylistItem.is-playing");
const listBox = $("#playlist");
if (playing && listBox) {
const er = playing.getBoundingClientRect();
const sr = listBox.getBoundingClientRect();
listBox.scrollTop += er.top - sr.top - (listBox.clientHeight - er.height) / 2;
}
我的 fork 上驗過:390×844 影片完整 16:9、載入停在頁頂、整頁可捲;
1280×900 桌面行為不變(影片 465px、清單滿高、整頁不捲)。
環境
main@0c327f7,courses/gym(未改動任何資料),Chromium 141.0.7390.37 headless,390×844(iPhone 尺寸)。
現象
手機寬度下進入上課頁,點開影片下方的
<details>「選片理由與自我檢核」,影片立刻縮成一小塊、播放清單同時被擠掉,而且頁面不能捲動,
沒有任何方法把影片變回正常大小,只能再把
<details>收合。重現與實測
COURSE=courses/gym make builddist/gym/index.html?tab=player&play=324(CH9「什麼是動力鏈?如何增加殺球能力?」,
why+assessment合計 425 字,是 gym 裡最長的一個;44 個主課全部都有
why,中位數合計 296 字)390px 寬的手機上影片只剩 206px 寬,左右各空掉 92px,比一半還小;
播放清單從 279px 掉到 107px。
window.scrollTo(0, 99999)之後scrollY仍然是 0,scrollHeight === innerHeight,捲不動。資訊區再長一點(我的 fork 在影片頁多放了講者資歷、查核日期、字幕提示,實測到 1111px),
就會有 553px 的內容直接被
overflow: hidden裁掉,同樣捲不到。所以在原版是「影片被壓扁」,資訊欄位更多的課程會惡化成「內容根本看不到」。
根因
三個地方各自成立,湊在一起才壞:
1.
.Player在窄螢幕仍然是固定視窗高+裁切player.css:6-15的height: calc(100dvh - var(--stick-top))與overflow: hidden,在
player.css:322-327的窄螢幕區塊裡沒有被解除,只換成上下堆疊。上下堆疊之後總內容高度會超過一個視窗,但容器仍然不許它長高、也不許捲。
2.
fitFrame()用「舞台高 − 資訊區高」搶剩餘空間(player.js:185-197)左右並排時這個算法是對的(資訊區在影片下方、共用一欄高度);
上下堆疊時它會讓影片與資訊區互相爭高度,資訊區越長影片越小。
另外
if (avail <= 0) return;會保留上一次算出來的--frame-w,所以資訊區長到一定程度之後影片尺寸就凍在殘值上,看起來像是「有時候又會好」,
實際上只是沒有重算。
watchFrame()的 ResizeObserver 有ro.observe(info),所以<details>一展開就即時重算
fitFrame()——壓扁是當下發生的,因果很直接。3.
scrollIntoView連文件一起捲(app.js:303)$(".PlaylistItem.is-playing")?.scrollIntoView({ block: "nearest" })。目前頁面不能捲所以看不出來;一旦照下面讓頁面可捲,進上課頁的瞬間影片會被推出畫面外。
修 1 之前不會顯現、修 1 之後一定顯現,所以一起提。
我的修法
我的 fork 上驗過:390×844 影片完整 16:9、載入停在頁頂、整頁可捲;
1280×900 桌面行為不變(影片 465px、清單滿高、整頁不捲)。