e-virt-table
Version:
A powerful data table based on canvas. You can use it as data grid、Microsoft Excel or Google sheets. It supports virtual scroll、cell edit etc.
137 lines • 5.03 kB
JavaScript
const PX_VALUE_RE = /^-?\d*\.?\d+px$/;
/** 内容缩放:逻辑坐标 ↔ 物理像素,canvas / DOM 共用 */
export default class ZoomScale {
constructor() {
Object.defineProperty(this, "value", {
enumerable: true,
configurable: true,
writable: true,
value: 1
});
}
get isDefault() {
return this.value === 1;
}
clamp(value, min, max) {
return Math.min(max, Math.max(min, value));
}
/** @returns 是否发生变化 */
set(value, min, max) {
const next = this.clamp(value, min, max);
if (Math.abs(next - this.value) < 0.0001) {
return false;
}
this.value = next;
return true;
}
/** 逻辑 -> 物理 */
toVisual(logical) {
return logical * this.value;
}
/** 物理 -> 逻辑 */
toLogical(physical) {
return physical / this.value;
}
toPx(logical) {
return `${this.toVisual(logical)}px`;
}
/** DOM 测量高度 -> 逻辑高度 */
domHeightToLogical(physicalHeight) {
return Math.round(physicalHeight / this.value);
}
/** 逻辑内容尺寸 -> 物理尺寸 */
contentToPhysical(logical) {
return logical * this.value;
}
scaleStyle(style) {
if (this.isDefault) {
return style;
}
const result = { ...style };
for (const key of Object.keys(result)) {
const val = result[key];
if (typeof val === 'string' && PX_VALUE_RE.test(val)) {
result[key] = `${parseFloat(val) * this.value}px`;
}
}
return result;
}
assignScaledStyle(el, style) {
Object.assign(el.style, this.scaleStyle(style));
}
/** 容器物理宽度 -> 逻辑可视宽度 */
containerWidth(containerPhysicalWidth) {
return Math.floor(containerPhysicalWidth / this.value);
}
resolveStageWidth(options) {
const containerLogical = this.containerWidth(options.containerPhysicalWidth);
const stageWidth = options.fillContainer
? containerLogical
: Math.min(Math.floor(options.contentWidth + options.scrollerTrackSize), containerLogical);
const stagePhysicalWidth = Math.min(Math.round(this.toVisual(stageWidth)), Math.floor(options.containerPhysicalWidth));
return { stageWidth, stagePhysicalWidth };
}
applyStageHeight(stageElement, physicalHeight) {
const stagePhysicalHeight = Math.floor(physicalHeight);
const stageHeight = Math.floor(stagePhysicalHeight / this.value);
stageElement.style.height = `${stagePhysicalHeight}px`;
return { stageHeight, stagePhysicalHeight };
}
/** floating-ui 锚点:逻辑单元格 -> 视口物理矩形 */
getViewportRect(logical, containerRect) {
const x = this.toVisual(logical.x) + containerRect.x;
const y = this.toVisual(logical.y) + containerRect.y;
const width = this.toVisual(logical.width);
const height = this.toVisual(logical.height);
return {
x,
y,
left: x,
top: y,
right: x + width,
bottom: y + height,
width,
height,
};
}
/** overlayer 单元格:外层物理定位 + 内层 zoom 缩放自定义 render */
createOverlayerCellElement(cell, cssPrefix) {
const cellEl = document.createElement('div');
this.assignScaledStyle(cellEl, cell.style);
Object.keys(cell.domDataset || {}).forEach((key) => {
cellEl.setAttribute(key, cell.domDataset[key]);
});
if (typeof cell.render !== 'function') {
return cellEl;
}
if (this.isDefault) {
cell.render(cellEl, cell);
return cellEl;
}
const logicalWidth = 'visibleWidth' in cell ? cell.visibleWidth : cell.width;
const logicalHeight = 'visibleHeight' in cell ? cell.visibleHeight : cell.height;
const isAutoHeight = 'autoRowHeight' in cell &&
cell.autoRowHeight &&
'renderType' in cell &&
cell.renderType === 'default';
const contentEl = document.createElement('div');
contentEl.className = `${cssPrefix}-overlayer-cell-content`;
contentEl.style.width = `${logicalWidth}px`;
contentEl.style.boxSizing = 'border-box';
if (isAutoHeight) {
contentEl.style.height = 'auto';
contentEl.style.minHeight = `${logicalHeight}px`;
cellEl.style.height = 'auto';
}
else {
contentEl.style.height = `${logicalHeight}px`;
cellEl.style.height = this.toPx(logicalHeight);
}
contentEl.style.zoom = String(this.value);
cellEl.style.width = this.toPx(logicalWidth);
cellEl.appendChild(contentEl);
cell.render(contentEl, cell);
return cellEl;
}
}
//# sourceMappingURL=ZoomScale.js.map