/** * 边滚动边加载的列表控件 * * Created by GUY on 2017/5/23. * @class BI.ListView * @extends BI.Widget */ import { Widget, shortcut } from "../../core"; @shortcut() export class ListView extends Widget { props() { return { baseCls: "bi-list-view", overscanHeight: 100, blockSize: 10, scrollTop: 0, el: {}, items: [], itemFormatter: (item, index) => { return item; }, }; } init() { this.renderedIndex = -1; this.cache = {}; } static xtype = "bi.list_view"; render() { const { el } = this.options; return { type: "bi.vertical", items: [BI.extend({ type: "bi.vertical", scrolly: false, ref: (_ref) => { this.container = _ref; }, }, el)], element: this, }; } // mounted之后绑定事件 mounted() { const o = this.options; // 这里无法进行结构,因为存在赋值操作,如果使用结构则this.options的值不会跟随变化 o.items = BI.isFunction(o.items) ? this.__watch(o.items, (context, newValue) => { this.populate(newValue); }) : o.items; this._populate(); this.element.scroll((e) => { o.scrollTop = this.element.scrollTop(); this._calculateBlocksToRender(); }); let lastWidth = this.element.width(), lastHeight = this.element.height(); BI.ResizeDetector.addResizeListener(this, () => { if (!this.element.is(":visible")) { return; } const width = this.element.width(), height = this.element.height(); if (width !== lastWidth || height !== lastHeight) { lastWidth = width; lastHeight = height; this._calculateBlocksToRender(); } }); } _renderMoreIf() { const { scrollTop, overscanHeight, blockSize, items, itemFormatter } = this.options; const height = this.element.height(); const minContentHeight = scrollTop + height + overscanHeight; let index = (this.cache[this.renderedIndex] && (this.cache[this.renderedIndex].index + blockSize)) || 0; let cnt = this.renderedIndex + 1; let lastHeight; const getElementHeight = () => { return this.container.element.height(); } lastHeight = getElementHeight(); while ((lastHeight) < minContentHeight && index < items.length) { const itemsArr = items.slice(index, index + blockSize); this.container.addItems(itemsArr.map((item, i) => { return itemFormatter(item, index + i); }), this); const addedHeight = getElementHeight() - lastHeight; this.cache[cnt] = { index: index, scrollTop: lastHeight, height: addedHeight, }; this.renderedIndex = cnt; cnt++; index += blockSize; lastHeight = getElementHeight(); } } _calculateBlocksToRender() { // BI-115750 不可见状态下依赖元素实际尺寸构造的线段树会分段错误,所以不进行后续计算和线段树的初始化。 // 这样从不可见状态变为可见状态能够重新触发线段树初始化 if (!this.element.is(":visible")) { return; } this._renderMoreIf(); } _populate(items) { const { scrollTop } = this.options; if (items && this.options.items !== items) { this.options.items = items; } this._calculateBlocksToRender(); this.element.scrollTop(scrollTop); } restore() { this.renderedIndex = -1; this.container.empty(); this.cache = {}; } scrollTo(scrollTop) { this.options.scrollTop = scrollTop; this._calculateBlocksToRender(); this.element.scrollTop(scrollTop); } populate(items) { if (items && this.options.items !== items) { this.restore(); } this._populate(items); } beforeDestroy() { BI.ResizeDetector.removeResizeListener(this); this.restore(); } }