X-Spreadsheet实现隐藏索引行列功能

593 阅读2分钟

有了隐藏索引行、列的需求,但源码未实现,那就自己简单实现一下吧。

  1. 添加 indexHeight ,方便对 A B C D ... 这个索引行的单独控制

src/core/data_proxy.js

const defaultSettings = {
  ...
  row: {
    len: 100,
    height: 25,
    indexHeight: 25// 索引行高度,方便对其高度单独设置,如果不设置,只能借用 height,这样就会导致索引行与单元格行同高
  },
  ...
};
  
/**
 * 根据 Y 坐标获取所在行的下标
 * @ignore
 * @param {number} y Y坐标
 * @param {number} scrollOffsetY 滚动条 Offset Y
 * @returns {Object} 带有 ri 行下标的对象
 */
function getCellRowByY(y, scrollOffsetY) {
  const { rows } = this;
  const fsh = this.freezeTotalHeight();
  let inits = rows.indexHeight; // rows.height -> rows.indexHeight,修复引用
  if (fsh + rows.indexHeight < y) { // rows.height -> rows.indexHeight,修复引用
    inits -= scrollOffsetY;
  }

  ...
  top -= height;

  // 表示鼠标在 A B C D 行所在的位置,这时为了标尺功能正确,直接重置 top 为 0
  if (y <= rows.indexHeight) {
    top = 0;
  }

  if (top <= 0 && rows.indexHeight > 0) { // 修复整列选中bug
    return { ri: -1, top: 0, height: rows.indexHeight }; // height -> height: rows.indexHeight,修复引用
  }
  return { ri: ri - 1, top, height };
}

/**
 * 根据 X 坐标获取所在行的下标
 * @ignore
 * @param {number} x X坐标
 * @param {number} scrollOffsetX 滚动条 Offset X
 * @returns {Object} 带有 ri 行下标的对象
 */
function getCellColByX(x, scrollOffsetX) {
  ...
  if (left <= 0 && cols.indexWidth > 0) { // 修复整行选中bug
    return { ci: -1, left: 0, width: cols.indexWidth };
  }
  ...
}

...
/**
 * 判断当前鼠标点击坐标是否在选中范围内。
 * @param {number} x 鼠标X坐标值
 * @param {number} y 鼠标Y坐标值
 * @returns {boolean}
 */
xyInSelectedRect(x, y) {
  const { left, top, width, height } = this.getSelectedRect();
  const x1 = x - this.cols.indexWidth;
  const y1 = y - this.rows.indexHeight; // rows.height -> rows.indexHeight,修复引用
  return x1 > left && x1 < left + width && y1 > top && y1 < top + height;
}

src/core/row.js

class Rows {
  /**
   * @hideconstructor
   * @param {Object} option
   * @param {number} option.len 总行数
   * @param {number} option.height 单行高度
   */
  constructor({ len, height, indexHeight }) {
    this._ = {};
    this.len = len;
    // default row height
    this.height = height;
    this.indexHeight = indexHeight; // 与data_proxy中相对应,这里也添加一下
  }
  ...
}
  1. 根据 indexHeight 与 indexWidth 绘制索引行与列

src/component/table.js,这个文件中的 Table 类是单元格绘制的实现类,具体方法为 render, 它有4个内部函数:

  • renderContentGrid,绘制单元格,也就是那些网格线
  • renderContent, 绘制单元格内容
  • renderFixedHeaders, 绘制索引行与列,即 A B C D 所在行与 1 2 3 4 所在列
  • renderFixedLeftTopCell, 索引行与列的交汇点,

我们明白之后,只要修改 renderFixedHeaders 就行了。实现方法可参考:

...
/**
 * 渲染表格
 */
render() {
  // resize canvas
  const { data } = this;
  const { rows, cols } = data;
  // fixed width of header
  let fixedIndexWidth = cols.indexWidth;
  // fixed height of header
  let fixedIndexHeight = rows.indexHeight; // 这里使用我们第1步定义的扩展属性
  ...
}
...

function renderFixedHeaders(type, viewRange, w, h, tx, ty) {
  const { draw, data } = this;
  const sumHeight = viewRange.h; // rows.sumHeight(viewRange.sri, viewRange.eri + 1);
  const sumWidth = viewRange.w; // cols.sumWidth(viewRange.sci, viewRange.eci + 1);
  const nty = ty + h;
  const ntx = tx + w;

  // 非编辑模式,不渲染标题栏
  if (data.settings.mode !== 'edit') return;

  // 如果都为0,则不渲染,判断方法根据条件即可,我这里认为为0时即隐藏,你也可以传递一个boolean值。
  if (w === 0 && h === 0) {
    // 画边线
    draw.save();
    draw.attr(tableFixedHeaderStyle());
    draw.line([0, 0], [0, sumHeight]);
    draw.restore();

    return;
  }
  ...
}
...

src/component/sheet.js

// private methods
/**
 * 表格覆盖层鼠标按下移动事件。
 * @ignore
 * @param evt
 */
function overlayerMousemove(evt) {
  ...
  // 当前鼠标坐标在 sheet(即单元格) 上时,隐藏标尺,并直接返回。
  if (offsetX > cols.indexWidth && offsetY > rows.indexHeight) { // rows.height -> rows.indexHeight
    rowResizer.hide();
    colResizer.hide();
    return;
  }
  ...
  // 当前鼠标坐标在左侧索引列时
  if (cRect.ri === -1 && cRect.ci >= 0) {
    cRect.height = rows.indexHeight; // rows.height -> rows.indexHeight
    ...
  }
}

...
/**
 * 获得当前 sheet 的宽度、高度和偏移量
 * width: 不包含 index 索引列,即最左侧 1、2、3... 列
 * height: 不包含 title 行,即 最上面 A、B、C... 行
 * left: 左偏移量
 * top: 顶偏移量
 * @returns {{top, left: (number|*), width: number, height: number}}
 */
getTableOffset() {
  const { rows, cols } = this.data;
  const { width, height } = this.getRect();
  return {
    width: width - cols.indexWidth,
    height: height - rows.indexHeight, // rows.height -> rows.indexHeight,修复引用
    left: cols.indexWidth,
    top: rows.indexHeight, // rows.height -> rows.indexHeight,修复引用
  };
}

  1. 使用

在初始化时传递的自定义参数中如下设置:

...
  row: {
    len: 100, // 行数,默认为100
    height: 25, // 行高,默认为25
    indexHeight: 0, // 行索引高度,默认为25,就是 A B C D ... 的高度
  },
  col: {
    len: 26, // 列数,默认为26
    width: 100, // 列宽,默认为100
    indexWidth: 0, // 索引列宽度,默认为60,就是 1 2 3 4 ... 的宽度
    minWidth: 20, // 最小列宽,默认为60
  },
  ...

好了,刷新看一下吧。