初始提交:短剧库源码(含转码优化 ultrafast)
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
import { element, reconcileChildren, withFocus } from './ui-core.js';
|
||||
|
||||
export function createCardViewport({container, scroller, create, update, key, rendered}) {
|
||||
let items = [], emptyNode = null, frame = 0, revision = 0, drawnRevision = -1;
|
||||
let columns = 1, rowHeight = 0, measuredWidth = 0, first = -1, last = -1;
|
||||
let mounted = new Map();
|
||||
const before = element('div', 'cards-spacer'), after = element('div', 'cards-spacer');
|
||||
before.setAttribute('aria-hidden', 'true');
|
||||
after.setAttribute('aria-hidden', 'true');
|
||||
container.setAttribute('role', 'list');
|
||||
|
||||
function schedule() {
|
||||
if (!frame) frame = requestAnimationFrame(() => {frame = 0; draw();});
|
||||
}
|
||||
|
||||
function draw() {
|
||||
const width = container.clientWidth;
|
||||
if (!width || !scroller.clientHeight) return;
|
||||
const style = getComputedStyle(container);
|
||||
const tracks = style.gridTemplateColumns.split(/\s+/).filter(value => /^\d/.test(value));
|
||||
const nextColumns = Math.max(1, tracks.length);
|
||||
if (width !== measuredWidth || columns !== nextColumns) {
|
||||
measuredWidth = width;
|
||||
columns = nextColumns;
|
||||
rowHeight = 0;
|
||||
first = -1;
|
||||
}
|
||||
const gap = parseFloat(style.rowGap) || 0;
|
||||
const step = (rowHeight || (parseFloat(tracks[0]) || width) * 4 / 3 + 60) + gap;
|
||||
const top = Math.max(0, scroller.getBoundingClientRect().top - container.getBoundingClientRect().top);
|
||||
const totalRows = Math.ceil(items.length / columns);
|
||||
const startRow = Math.max(0, Math.min(Math.max(0, totalRows - 1), Math.floor(top / step) - 1));
|
||||
const endRow = Math.min(totalRows, Math.ceil((top + scroller.clientHeight) / step) + 1);
|
||||
const start = Math.min(items.length, startRow * columns), end = Math.min(items.length, endRow * columns);
|
||||
if (first === start && last === end && drawnRevision === revision) return;
|
||||
const nextMounted = new Map(), nodes = [], changed = [];
|
||||
if (startRow > 0 && items.length) {before.style.height = Math.max(0, startRow * step - gap) + 'px'; nodes.push(before);}
|
||||
for (let index = start; index < end; index++) {
|
||||
const item = items[index], renderKey = key(item);
|
||||
let card = mounted.get(item.id);
|
||||
if (!card || card.renderKey !== renderKey) {card = create(item); changed.push(card);}
|
||||
else if (drawnRevision !== revision) {update(card, item); changed.push(card);}
|
||||
card.setAttribute('role', 'listitem');
|
||||
card.setAttribute('aria-posinset', String(index + 1));
|
||||
card.setAttribute('aria-setsize', String(items.length));
|
||||
nextMounted.set(item.id, card);
|
||||
nodes.push(card);
|
||||
}
|
||||
if (endRow < totalRows) {after.style.height = Math.max(0, (totalRows - endRow) * step - gap) + 'px'; nodes.push(after);}
|
||||
if (!items.length && emptyNode) nodes.push(emptyNode);
|
||||
const active = document.activeElement?.closest?.('.card');
|
||||
if (active && container.contains(active) && !nextMounted.has(active.dataset.dramaId)) scroller.focus({preventScroll: true});
|
||||
withFocus(container, () => reconcileChildren(container, nodes));
|
||||
mounted = nextMounted;
|
||||
first = start;
|
||||
last = end;
|
||||
drawnRevision = revision;
|
||||
container.setAttribute('aria-busy', 'false');
|
||||
if (!rowHeight && mounted.size) {
|
||||
rowHeight = mounted.values().next().value.getBoundingClientRect().height;
|
||||
first = -1;
|
||||
schedule();
|
||||
}
|
||||
if (changed.length) rendered(changed);
|
||||
}
|
||||
|
||||
scroller.addEventListener('scroll', schedule, {passive: true});
|
||||
window.addEventListener('resize', schedule);
|
||||
if (typeof ResizeObserver === 'function') new ResizeObserver(schedule).observe(container);
|
||||
return {
|
||||
setItems(next, placeholder = null) {items = next; emptyNode = placeholder; revision++; container.setAttribute('aria-busy', 'true'); schedule();},
|
||||
refresh() {first = -1; schedule();}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user