BaseGrid

BaseGrid

这类节点拥有二维数组的结构,意味着可以用两个索引去获取其内部的每个元素。我们把二维数组称为网格。

网格内的每个元素被抽象为一个个 element。

这是一个抽象类,不应该被实例化。

API 列表

class BaseGrid extends SD2DNode {
    // 索引管理
    startN(): number;
    startN(start: number): this;
    startM(): number;
    startM(start: number): this;
    endN(): number;
    endM(): number;
    endM(i: number): number;
    idxN(i: number): number;
    idxM(j: number): number;
    n(n: number): this;
    m(m: number): this;

    // 元素访问管理
    element(i: number, j: number): SDNode;
    forEachElement(callback: (element: SDNode, rowId: number, colId: number) => void): this;

    // 元素属性管理
    opacity(i: number, j: number): number;
    opacity(i: number, j: number, opacity: number): this;
    color(color: SDColor): this;
    color(i: number, j: number): SDColor;
    color(i: number, j: number, color: SDColor): this;
    text(i: number, j: number): string;
    text(i: number, j: number, text: string): this;
    intValue(i: number, j: number): number;
    value(i: number, j: number): SDNode;
    value(i: number, j: number, value: any): this;

    // 插入管理
    insert(i: number, j: number, value?: any): this;
    insertFromExistValue(i: number, j: number, value: SDNode): this;
    insertFromExistElement(i: number, j: number, element: SDNode): this;
    pushCol(): this;
    pushCol(count: number): this;
    pushRow(): this;
    pushRow(count: number): this;

    // 删除管理
    erase(i: number, j: number): this;
    dropElement(i: number, j: number): SDNode | undefined;
    dropValue(i: number, j: number): SDNode | undefined;
    popCol(): this;
    popRow(): this;
}

索引管理

class BaseGrid {
    startN(): number; // 获取第一维度起始索引
    startN(start: number): this; // 设置第一维度起始索引
    startM(): number; // 获取第二维度起始索引
    startM(start: number): this; // 设置第二维度起始索引
    endN(): number; // 获取第一维度结束索引
    endM(): number; // 获取第二维度结束索引
    endM(i: number): number; // 获取第一维度指定的第二维度的结束索引
    idxN(i: number): number; // 将第一维度的逻辑索引转化为物理索引,对用户而言用不到
    idxM(j: number): number; // 将第二维度的逻辑索引转化为物理索引,对用户而言用不到
    n(): number; // 获取第一维度的长度
    n(n: number): this; // 设置第一维度的长度
    m(): number; // 获取第二维度的长度
    m(m: number): this; // 设置第二维度的长度
}

上文提到的二维数组并不一定是规整的,例如,二维数组可能是这样的:

[
    [1, 2, 3],
    [4, 5],
    [],
    [6, 7, 8, 9]
];

我们用第一维度指外层数组,用第二维度指内层数组。对于第一维度而言,它一定只有一个长度,对于第二维度而言,它可能就有不同的长度了,所以 endM 相比于 endN 多了一种重载。

默认情况下,当我们说第二维度的长度,而没有指定具体第一维度时,我们在说第二维度最长的那个长度。例如上述二维数组的例子中,其第二维度的长度就是 4。

通过 n 和 m 这两个方法,我们可以很方便地初始化一个规整的二维数组出来。

const svg = sd.svg();
const grid = new sd.Grid(svg).n(3).m(6);

元素访问管理

class BaseGrid {
    element(i: number, j: number): SDNode;
    forEachElement(callback: (element: SDNode, rowId: number, colId: number) => void): this;
}

元素属性管理

各种各样的管理元素属性的方法。

opacity

class BaseGrid {
    opacity(i: number, j: number): number; // 获取目标元素的透明度
    opacity(i: number, j: number, opacity: number): this; // 设置目标元素的透明度
}

管理 element 的透明度。

color

class BaseGrid {
    color(color: SDColor): this; // 设置每一个元素的颜色
    color(i: number, j: number): SDColor; // 获取目标元素的颜色
    color(i: number, j: number, color: SDColor): this; // 设置目标元素的颜色
}

管理 element 的颜色。

text

class BaseGrid {
    text(i: number, j: number): string;
    text(i: number, j: number, text: string): this;
}

管理 element 的文本。当 element 本身就是文本元素,或者 element 的 value 是文本元素时有效。

intValue

class BaseGrid {
    intValue(i: number, j: number): number;
}

element 对应的文本解析为整数类型,并返回。如果 value 不存在,或者对应文本是空串,则会解析为整数 0。在解析其他非数值文本的情况下会抛出错误。

const svg = new sd.svg();
const grid = new sd.Grid(svg).insert(0, 0, "1").insert(0, 1, "2.8").insert(0, 2).insert(1, 0, "A");
console.log(grid.intValue(0, 0)); // 1
console.log(grid.intValue(0, 1)); // 2
console.log(grid.intValue(0, 2)); // 0
// console.log(grid.intValue(1, 0)); // this is an invalid invoke

value

class BaseGrid {
    value(i: number, j: number): SDNode; // 获取目标元素的价值物
    value(i: number, j: number, value: any): this; // 设置目标元素的价值物
}

管理 element 的 value。

插入管理

class BaseGrid {
    insert(i: number, j: number, value?: any): this;
    insertFromExistValue(i: number, j: number, value: SDNode): this;
    insertFromExistElement(i: number, j: number, element: SDNode): this;
    pushCol(): this;
    pushCol(count: number): this;
    pushRow(): this;
    pushRow(count: number): this;
}

insert

insert 类型的插入需要指定插入的位置。有三种不同类型的 insert,下面演示它们的区别:

const svg = sd.svg();
const grid = new sd.Grid(svg).n(1).m(3);
const value1 = new sd.Circle(svg).cx(grid.element(0, 0).cx()).cy(120);
const value2 = new sd.Text(svg, "A").cx(grid.element(0, 1).cx()).cy(120);
const value3 = new sd.Box(svg, "B").cx(grid.element(0, 2).cx()).cy(120);
sd.main(async () => {
    await sd.pause();
    grid.startAnimate().insert(1, 0, value1).endAnimate();
    await sd.pause();
    grid.startAnimate().insertFromExistValue(1, 1, value2).endAnimate();
    await sd.pause();
    grid.startAnimate().insertFromExistElement(1, 2, value3).endAnimate();
});

pushCol & pushRow

我们把二维数组的第一维度中每个元素看作是一行(第一维度上每个元素是一个数组),第二维度中每个元素看作位于某列上(第二维度上每个元素是一个 element)。这两个 push 方法提供了快速添加一行一列的方案。

const svg = sd.svg();
const grid = new sd.Grid(svg).pushRow(3).pushRow(2).pushRow(1);
/*
[
    [e, e, e],
    [e, e],
    [e]
]
*/
const svg = sd.svg();
const grid = new sd.Grid(svg).pushCol(3).pushCol(2).pushCol(1);
// pushCol(1).pushCol(2).pushCol(3) 的效果与 pushCol(3).pushCol(2).push(1) 是一样的
// 对 pushCol 而言,如果其作用于第一维度上的某个数组,则新元素会被附加到该数组的末尾
/*
[
    [e, e, e],
    [e, e],
    [e],
]
*/

删除管理

用来删掉网格中的元素。

erase

class BaesGrid {
    erase(i: number, j: number): this; // 删除目标元素
}

顾名意思,把某个 element 从网格中删除。默认情况下删除 element 会导致其永远地离开场景。

const svg = sd.svg();
const grid = new sd.Grid(svg).n(3).m(6);
sd.main(async () => {
    await sd.pause();
    grid.startAnimate().erase(1, 2).endAnimate();
});

drop

class BaseGrid {
    dropElement(i: number, j: number): SDNode | undefined; // 丢弃目标元素
    dropValue(i: number, j: number): SDNode | undefined; // 丢弃目标元素的价值物
}

前文提到,使用 erase 一类的方法去删除某个 element,会导致 element(及其内部的 value,如果有)永远地离开场景。但部分情况下,我们可能希望被删除的 element 或者 value 只是被网格丢弃了,它仍然存在于场景之中。有两种达成这个目的的解决方案,最直接的便是 drop 方法。下面的代码以交换两个网格中的元素为例进行演示:

const svg = sd.svg();
const EN = sd.enter();
const grid1 = new sd.Grid(svg).insert(0, 0, 1).insert(0, 1, 2).insert(0, 2, 3);
const grid2 = new sd.Grid(svg).y(100).insert(0, 0, "A").insert(0, 1, "B").insert(0, 2, "C");
function swapByValue(x) {
    grid1.startAnimate();
    grid2.startAnimate();
    const v1 = grid1.dropValue(0, x);
    const v2 = grid2.dropValue(0, x);
    grid1.element(x).valueFromExist(v2);
    grid2.element(x).valueFromExist(v1);
    grid1.endAnimate();
    grid2.endAnimate();
}
function swapByElement(x) {
    grid1.startAnimate();
    grid2.startAnimate();
    const e1 = grid1.dropElement(0, x);
    const e2 = grid2.dropElement(0, x);
    grid1.insertFromExistElement(0, x, e2);
    grid2.insertFromExistElement(0, x, e1);
    grid1.endAnimate();
    grid2.endAnimate();
}
sd.main(async () => {
	await sd.pause();
    swapByValue(2);
    await sd.pause();
    swapByElement(1);
});

试一试,dropValue 和 dropElement 的区别。

popRow & popCol

class BaseGrid {
    popCol(): this; // 删掉最后一列
    popRow(): this; // 删掉最后一行
}