forked from fanruan/fineui
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
361 lines
14 KiB
361 lines
14 KiB
8 years ago
|
/**
|
||
|
* Created By Shichao on 2017/10/17
|
||
|
* @class BI.CanvasCollectionView
|
||
|
* @extends BI.Widget
|
||
|
*/
|
||
|
BI.CanvasCollectionView = BI.inherit(BI.Widget, {
|
||
|
_defaultConfig: function () {
|
||
|
return BI.extend(BI.CanvasCollectionView.superclass._defaultConfig.apply(this, arguments), {
|
||
|
baseCls: "bi-canvas-collection",
|
||
|
overflowX: true,
|
||
|
overflowY: true,
|
||
|
cellSizeAndPositionGetter: BI.emptyFn,
|
||
|
horizontalOverscanSize: 0,
|
||
|
verticalOverscanSize: 0,
|
||
|
scrollLeft: 0,
|
||
|
scrollTop: 0,
|
||
|
items: []
|
||
|
})
|
||
|
},
|
||
|
|
||
|
_init: function () {
|
||
|
BI.CanvasCollectionView.superclass._init.apply(this, arguments);
|
||
|
var self = this, o = this.options;
|
||
|
this.renderedCells = [];
|
||
|
this.renderedKeys = [];
|
||
|
this._scrollLock = false;
|
||
|
this.scrollTop = this.scrollLeft = 0;
|
||
|
this.summaryScrollTop = this.summaryScrollLeft = 0;
|
||
|
this._debounceRelease = BI.debounce(function () {
|
||
|
self._scrollLock = false;
|
||
|
}, 1000 / 60);
|
||
|
this.canvas = BI.createWidget({
|
||
|
type: "bi.canvas_new"
|
||
|
});
|
||
|
$(document).keydown(BI.bind(this._onResize, this)); // 防止在使用ctrl + 滚轮调整窗口大小时触发mousewheel事件
|
||
|
$(document).keyup(BI.bind(this._onResizeRelease, this))
|
||
|
this.element.mousewheel(BI.bind(this._onMouseWheel, this))
|
||
|
BI.createWidget({
|
||
|
type: "bi.vertical",
|
||
|
element: this,
|
||
|
scrollable: false,
|
||
|
scrolly: false,
|
||
|
scrollx: false,
|
||
|
items: [this.canvas]
|
||
|
});
|
||
|
if (o.items.length > 0) {
|
||
|
this._calculateSizeAndPositionData();
|
||
|
this._populate();
|
||
|
}
|
||
|
if (o.scrollLeft !== 0 || o.scrollTop !== 0) {
|
||
|
BI.nextTick(function () {
|
||
|
self.element.scrollTop(o.scrollTop);
|
||
|
self.element.scrollLeft(o.scrollLeft);
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_onMouseWheel: function (e) {
|
||
|
var o = this.options;
|
||
|
if (this._scrollLock) {
|
||
|
return;
|
||
|
}
|
||
|
if (!this._isCtrlPressed) {
|
||
|
o.scrollTop += -e.originalEvent.wheelDelta;
|
||
|
o.scrollTop = BI.clamp(o.scrollTop, 0, this.maxScrollTop);
|
||
|
this._calculateChildrenToRender();
|
||
|
this.fireEvent(BI.CanvasCollectionView.EVENT_SCROLL, {
|
||
|
scrollLeft: o.scrollLeft,
|
||
|
scrollTop: o.scrollTop
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_onResize: function (e) {
|
||
|
if (e.ctrlKey) {
|
||
|
this._isCtrlPressed = true;
|
||
|
} else {
|
||
|
this._isCtrlPressed = false;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_onResizeRelease: function (e) {
|
||
|
var Keys = {
|
||
|
CTRL: 17
|
||
|
};
|
||
|
var keyCode = e.keyCode;
|
||
|
|
||
|
if (keyCode === Keys.CTRL) {
|
||
|
this._isCtrlPressed = false;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_getMaxScrollTop: function () {
|
||
|
return this._height - this.options.height
|
||
|
},
|
||
|
|
||
|
_calculateSizeAndPositionData: function () {
|
||
|
var o = this.options;
|
||
|
var cellMetadata = [];
|
||
|
var sectionManager = new BI.SectionManager();
|
||
|
var height = 0;
|
||
|
var width = 0;
|
||
|
|
||
|
for (var index = 0, len = o.items.length; index < len; index++) {
|
||
|
var cellMetadatum = o.cellSizeAndPositionGetter(index);
|
||
|
|
||
|
if (cellMetadatum.height == null || isNaN(cellMetadatum.height) ||
|
||
|
cellMetadatum.width == null || isNaN(cellMetadatum.width) ||
|
||
|
cellMetadatum.x == null || isNaN(cellMetadatum.x) ||
|
||
|
cellMetadatum.y == null || isNaN(cellMetadatum.y)) {
|
||
|
throw Error();
|
||
|
}
|
||
|
|
||
|
height = Math.max(height, cellMetadatum.y + cellMetadatum.height);
|
||
|
width = Math.max(width, cellMetadatum.x + cellMetadatum.width);
|
||
|
|
||
|
cellMetadatum.index = index;
|
||
|
cellMetadata[index] = cellMetadatum;
|
||
|
sectionManager.registerCell(cellMetadatum, index);
|
||
|
}
|
||
|
|
||
|
this._cellMetadata = cellMetadata;
|
||
|
this._sectionManager = sectionManager;
|
||
|
if (this._height === height && this._width === width) {
|
||
|
this._isNeedReset = false;
|
||
|
} else {
|
||
|
this._height = height;
|
||
|
this._width = width;
|
||
|
this._isNeedReset = true;
|
||
|
}
|
||
|
this.maxScrollTop = this._getMaxScrollTop();
|
||
|
},
|
||
|
|
||
|
_cellRenderers: function (height, width, x, y) {
|
||
|
this._lastRenderedCellIndices = this._sectionManager.getCellIndices(height, width, x, y);
|
||
|
return this._cellGroupRenderer();
|
||
|
},
|
||
|
|
||
|
_cellGroupRenderer: function () {
|
||
|
var self = this, o = this.options;
|
||
|
var rendered = [];
|
||
|
BI.each(this._lastRenderedCellIndices, function (i, index) {
|
||
|
var cellMetadata = self._sectionManager.getCellMetadata(index);
|
||
|
rendered.push(cellMetadata);
|
||
|
});
|
||
|
return rendered;
|
||
|
},
|
||
|
|
||
|
_calculateChildrenToRender: function () {
|
||
|
var x, y, cellWidth, cellHeight, cellRow, cellCol, value, self = this, o = this.options;
|
||
|
var scrollLeft = o.scrollLeft;
|
||
|
var scrollTop = o.scrollTop;
|
||
|
var left = Math.max(0, scrollLeft - o.horizontalOverscanSize);
|
||
|
var top = Math.max(0, scrollTop - o.verticalOverscanSize);
|
||
|
var right = Math.min(this._width, scrollLeft + o.width + o.horizontalOverscanSize);
|
||
|
var bottom = Math.min(this._height, scrollTop + o.height + o.verticalOverscanSize);
|
||
|
if (right > 0 && bottom > 0) {
|
||
|
var childrenToDisplay = this._cellRenderers(bottom - top, right - left, left, top);
|
||
|
this.canvas.remove(this.scrollLeft, this.scrollTop, o.width, o.height);
|
||
|
for (var i = 0; i < childrenToDisplay.length; i++) {
|
||
|
var datum = childrenToDisplay[i];
|
||
|
var index = this.renderedKeys[datum.index] && this.renderedKeys[datum.index][1];
|
||
|
var rect_tl_x = datum.x,
|
||
|
rect_tl_y = datum.y,
|
||
|
rect_tr_x = rect_tl_x + datum.width,
|
||
|
rect_bl_y = rect_tl_y + datum.height,
|
||
|
cell = o.items[datum.index].cell || o.items[datum.index],
|
||
|
background, color, fontWeight, text;
|
||
|
while (BI.isNull(cell.styles) && !BI.isFunction(cell.styleGetter)) {
|
||
|
cell = cell.cell;
|
||
|
}
|
||
|
if (BI.isNull(cell.styles) && BI.isFunction(cell.styleGetter)) {
|
||
|
background = cell.styleGetter().background;
|
||
|
color = cell.styleGetter().color;
|
||
|
fontWeight = cell.styleGetter().fontWeight;
|
||
|
} else if (!BI.isNull(cell.styles)) {
|
||
|
background = cell.styles.background;
|
||
|
color = cell.styles.color;
|
||
|
fontWeight = cell.styles.fontWeight;
|
||
|
}
|
||
|
if (BI.isNull(cell.text)) {
|
||
|
text = "";
|
||
|
} else {
|
||
|
text = cell.text;
|
||
|
}
|
||
|
if (!BI.isString(text)) {
|
||
|
text = text.toString();
|
||
|
}
|
||
|
if (this.scrollLeft !== o.scrollLeft) {
|
||
|
this.canvas.translate(this.scrollLeft - o.scrollLeft, 0);
|
||
|
this.scrollLeft = o.scrollLeft;
|
||
|
}
|
||
|
if (this.scrollTop !== o.scrollTop) {
|
||
|
this.canvas.translate(0, this.scrollTop - o.scrollTop);
|
||
|
this.scrollTop = o.scrollTop;
|
||
|
}
|
||
|
if (datum.x === 0 && datum.y === 0) {
|
||
|
this.canvas.solid(rect_tl_x, rect_tl_y, rect_tl_x, rect_bl_y, rect_tr_x, rect_bl_y, rect_tr_x, rect_tl_y, rect_tl_x, rect_tl_y, {
|
||
|
strokeStyle: "rgb(212, 218, 221)",
|
||
|
fillStyle: background
|
||
|
});
|
||
|
} else if (datum.x === 0) {
|
||
|
this.canvas.solid(rect_tl_x, rect_tl_y, rect_tl_x, rect_bl_y, rect_tr_x, rect_bl_y, rect_tr_x, rect_tl_y, {
|
||
|
strokeStyle: "rgb(212, 218, 221)",
|
||
|
fillStyle: background
|
||
|
});
|
||
|
} else if (datum.y === 0) {
|
||
|
this.canvas.solid(rect_tl_x, rect_tl_y, rect_tr_x, rect_tl_y, rect_tr_x, rect_bl_y, rect_tl_x, rect_bl_y, {
|
||
|
strokeStyle: "rgb(212, 218, 221)",
|
||
|
fillStyle: background
|
||
|
});
|
||
|
} else {
|
||
|
this.canvas.solid(rect_tr_x, rect_tl_y, rect_tl_x, rect_tl_y, rect_tl_x, rect_bl_y, rect_tr_x, rect_bl_y, {
|
||
|
strokeStyle: "rgb(212, 218, 221)",
|
||
|
fillStyle: background
|
||
|
});
|
||
|
}
|
||
|
this.canvas.setFontWeight(fontWeight);
|
||
|
this.canvas.setFont();
|
||
|
var font = this.canvas.getContext().font,
|
||
|
textSize = this._getTextPixel(font),
|
||
|
textHeight = textSize,
|
||
|
textWidth = this.canvas.getContext().measureText(text).width,
|
||
|
dotsWidth = this.canvas.getContext().measureText("...").width;
|
||
|
var offsetX = this._calcOffsetX(textWidth, datum.width, "center"),
|
||
|
offsetY = this._calcOffsetY(textHeight, datum.height, "center");
|
||
|
if (textHeight > datum.height) {
|
||
|
this.canvas.text(datum.x + offsetX, datum.y + offsetY, "...", color);
|
||
|
} else if (textWidth + 4 > datum.width) {
|
||
|
var sliceIndex = Math.floor((datum.width - dotsWidth - 4) / textWidth * text.length);
|
||
|
this.canvas.text(datum.x + offsetX, datum.y + offsetY, text.slice(0, sliceIndex) + "...", color);
|
||
|
} else {
|
||
|
this.canvas.text(datum.x + offsetX, datum.y + offsetY, text, color);
|
||
|
}
|
||
|
this.canvas.stroke();
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_getTextPixel: function (font) {
|
||
|
var p = font.split("px")[0],
|
||
|
s = p.split(" ");
|
||
|
for (var c in s) {
|
||
|
var num;
|
||
|
num = BI.parseInt(s[c]);
|
||
|
if (!BI.isNaN(num)) {
|
||
|
return num;
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_calcOffsetX: function (textWidth, cellWidth, position) {
|
||
|
switch (position) {
|
||
|
case "center":
|
||
|
if (textWidth >= cellWidth) {
|
||
|
return 4;
|
||
|
} else {
|
||
|
return (cellWidth - textWidth) / 2;
|
||
|
}
|
||
|
case "right":
|
||
|
if (textWidth >= cellWidth) {
|
||
|
return 0;
|
||
|
} else {
|
||
|
return cellWidth - textWidth;
|
||
|
}
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_calcOffsetY: function (textHeight, cellHeight, position) {
|
||
|
switch (position) {
|
||
|
case "center":
|
||
|
if (textHeight >= cellHeight) {
|
||
|
return textHeight;
|
||
|
} else {
|
||
|
return (cellHeight + textHeight) / 2;
|
||
|
}
|
||
|
case "bottom":
|
||
|
if (textHeight >= cellHeight) {
|
||
|
return cellHeight;
|
||
|
} else {
|
||
|
return cellHeight + textHeight;
|
||
|
}
|
||
|
default:
|
||
|
return textHeight;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_populate: function (items) {
|
||
|
var o = this.options;
|
||
|
if (items && items !== this.options.items) {
|
||
|
this.options.items = items;
|
||
|
this._calculateSizeAndPositionData();
|
||
|
}
|
||
|
if (o.items.length > 0) {
|
||
|
this.canvas.setBlock();
|
||
|
this.canvas.setWidth(o.width);
|
||
|
this.canvas.setHeight(o.height);
|
||
|
this.restore();
|
||
|
this._calculateChildrenToRender();
|
||
|
this.element.scrollTop(o.scrollTop);
|
||
|
this.element.scrollLeft(o.scrollLeft);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
populate: function (items) {
|
||
|
if (items && items !== this.options.items) {
|
||
|
this.restore();
|
||
|
}
|
||
|
this._populate(items);
|
||
|
},
|
||
|
|
||
|
setWidth: function (width) {
|
||
|
BI.CanvasCollectionView.superclass.setWidth.apply(this, arguments);
|
||
|
this.options.width = width;
|
||
|
},
|
||
|
|
||
|
setHeight: function (height) {
|
||
|
BI.CanvasCollectionView.superclass.setHeight.apply(this, arguments);
|
||
|
this.options.height = height;
|
||
|
},
|
||
|
|
||
|
restore: function () {
|
||
|
this.canvas.remove(this.scrollLeft, this.scrollTop, this.options.width, this.options.height);
|
||
|
this.renderedKeys = [];
|
||
|
this._scrollLock = false;
|
||
|
},
|
||
|
|
||
|
setScrollLeft: function (scrollLeft) {
|
||
|
if (this.options.scrollLeft === scrollLeft) {
|
||
|
return;
|
||
|
}
|
||
|
this._scrollLock = true;
|
||
|
this.options.scrollLeft = scrollLeft;
|
||
|
this._debounceRelease();
|
||
|
this._calculateChildrenToRender();
|
||
|
this.element.scrollLeft(this.options.scrollLeft);
|
||
|
},
|
||
|
|
||
|
setScrollTop: function (scrollTop) {
|
||
|
if (this.options.scrollTop === scrollTop) {
|
||
|
return;
|
||
|
}
|
||
|
this._scrollLock = true;
|
||
|
this.options.scrollTop = scrollTop;
|
||
|
this._debounceRelease();
|
||
|
this._calculateChildrenToRender();
|
||
|
this.element.scrollTop(this.options.scrollTop);
|
||
|
},
|
||
|
|
||
|
getScrollLeft: function () {
|
||
|
return this.options.scrollLeft;
|
||
|
},
|
||
|
|
||
|
getScrollTop: function () {
|
||
|
return this.options.scrollTop;
|
||
|
}
|
||
|
})
|
||
|
BI.CanvasCollectionView.EVENT_SCROLL = "EVENT_SCROLL";
|
||
|
BI.shortcut("bi.canvas_collection_view", BI.CanvasCollectionView);
|