BaseSVGLine

BaseSVGLine

专为 SVG 的线类元素打造。继承自 sd.BaseSVG。在线类元素上可以存在一个价值物(value)。这是一个抽象类,不应该被实例化。

API 列表

class BaseSVGLine extends BaseSVG {
    constructor(target: SDNode | RenderNode, tag: string);

    markerStart(): string;
    markerStart(marker: string): this;
    markerMid(): string;
    markerMid(marker: string): this;
    markerEnd(): string;
    markerEnd(marker: string): this;
    arrow(arrow?: boolean | undefined | null): this;
    revArrow(arrow?: boolean | undefined | null): this;
    doubleArrow(arrow?: boolean | undefined | null): this;
    pointStoT(): this;
    pointTtoS(): this;
    fadeStoT(): this;
    fadeTtoS(): this;
    at(k: number): [number, number];
    getPointAtLength(length: number): [number, number];
    totalLength(): number;
    text(): string;
    text(text: string): this;
    intValue(): number;
    value(): SDNode | undefined;
    value(value: any, rule?: SDRule): this;
    valueFromExist(value: SDNode, rule?: SDRule): this;
    drop(): SDNode;
}

markerStart & markerMid & markerEnd

class BaseSVGLine {
    markerStart(): string;
    markerStart(marker: string): this;
    markerMid(): string;
    markerMid(marker: string): this;
    markerEnd(): string;
    markerEnd(marker: string): this;
}

在 SVG 中,可以为线类元素上的起点位置、路径的所有中间顶点、终点位置添加标记(marker),标记的样式是自定义的。目前画布中自带了两种样式:arrowadaptiveArrow

const svg = sd.svg();
const styles = ["arrow", "adaptiveArrow"];
const lines = [];
styles.forEach((style, i) => { 
    const line = new sd.Line(svg);
    line.source(0, i * 40);
    line.target(200, i * 40);
    line.markerStart(style).markerEnd(style);
    // 对于 sd.Line 而言,markerMid 是无效的,因为路径没有中间顶点
    lines.push(line);
});
sd.main(async () => {
    await sd.pause();
    lines.forEach(line => {
        line.startAnimate().strokeWidth(3).endAnimate();
    });
});

试一试,adaptive 表示这个标记的大小会随着线段的粗细而变化。

arrow & revArrow & doubleArrow

class BaseSVGLine {
    arrow(arrow?: boolean | undefined | null): this;
    revArrow(arrow?: boolean | undefined | null): this;
    doubleArrow(arrow?: boolean | undefined | null): this;
}

管理箭头的设置,分别表示正向箭头,反向箭头,双向箭头。其内部是通过设置 marker 来实现的。

const svg = sd.svg();
const l1 = new sd.Line(svg).x(100).y(100);
const l2 = new sd.Line(svg).x(200).y(100);
const l3 = new sd.Line(svg).x(300).y(100);
sd.main(async () => {
    await sd.pause();
    l1.arrow();
    l2.revArrow();
    l3.doubleArrow();
});

pointStoT & pointTtoS & fadeStoT & fadeTtoS

class BaseSVGLine {
    pointStoT(): this; // 从起点到终点浮现
    pointTtoS(): this; // 从终点到起点浮现
    fadeStoT(): this; // 从起点到终点消失
    fadeTtoS(): this; // 从终点到起点消失
}

用于让线类元素的出现和消失更加丝滑。内部是通过 strokeDashArray 和 strokeDashOffset 实现的。

const svg = sd.svg();
const l1 = new sd.Line(svg).opacity(0).source(0, 0).target(100, 0);
const l2 = new sd.Line(svg).opacity(0).source(0, 40).target(100, 40);
const l3 = new sd.Line(svg).source(0, 80).target(100, 80);
const l4 = new sd.Line(svg).source(0, 120).target(100, 120);
sd.main(async () => {
    await sd.pause();
    l1.opacity(1).startAnimate().pointStoT().endAnimate();
    l2.opacity(1).startAnimate().pointTtoS().endAnimate();
    l3.startAnimate().fadeStoT().endAnimate();
    l4.startAnimate().fadeTtoS().endAnimate();
});

at & getPointAtLength & totalLength

class BaseSVGLine {
    at(k: number): [number, number]; // 获取路径上占总长比值为 k 的点的位置,0<=k<=1
    getPointAtLength(length: number): [number, number]; // 获取路径上,距离起点 length 的点
    totalLength(): number; // 获取路径总长度
}

用于获取线类元素上的轨迹信息。

text

class BaseSVGLine {
    text(): string;
    text(text: string): this;
}

管理 value 的文本。当且仅当 value 是文本元素时才有效。

const svg = sd.svg();
const line1 = new sd.Line(svg, "A");
const line2 = new sd.Line(svg, new sd.Rect(svg).width(12).height(12)).x(100);
console.log(line1.text()); // "A"
// console.log(line2.text()); // this is invalid invoke
sd.main(async () => {
	await sd.pause();
	line1.text("B");
});

intValue

class BaseSVGLine {
    intValue(): number;
}

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

const svg = sd.svg();
const line1 = new sd.Line(svg, "1");
const line2 = new sd.Line(svg, "1.8").x(100);
const line3 = new sd.Line(svg).x(200);
const line4 = new sd.Line(svg, "A").x(300);
console.log(line1.intValue()); // 1
console.log(line2.intValue()); // 1
console.log(line3.intValue()); // 0
// console.log(line4.intValue()); // this is invalid invoke

value & valueFromExist

class BaseElement {
    value(): SDNode | undefined;
    value(value: any, rule?: SDRule): this;
    valueFromExist(value: SDNode, rule?: SDRule): this;
}

管理 value 节点。可以在添加 value 节点时使用自定义布局规则,若不提供则使用默认规则(即把 value 放在路径中点位置)。

const svg = sd.svg();
const EN = sd.enter();
const svg = sd.svg();
const EN = sd.enter();
const line1 = new sd.Line(svg);
const line2 = new sd.Line(svg).x(100);
const line3 = new sd.Line(svg).x(200);
const line4 = new sd.Line(svg).x(300);
const value2 = new sd.Text(svg, "B").cy(100).cx(line2.cx());
const value3 = new sd.Circle(svg).r(5).cy(100).cx(line3.cx());
const value4 = new sd.Text(svg, "C").cy(100).cx(line4.cx());
sd.main(async () => {
	await sd.pause();
	line1.startAnimate().value("A").endAnimate();
	line2.startAnimate().value(value2).endAnimate();
	line3.startAnimate().value(value3.onEnter(EN.moveTo())).endAnimate();
	line4.startAnimate().valueFromExist(value4).endAnimate();
});

试一试,前两个 line 中,value 是浮现在 box 的正中心的;而后两个 line 中,value 是从自己原本的位置平移过去的。这就是 value 和 valueFromExist 的区别。valueFromExist 会指定 value 进入元素的方式是从当前位置平移过去

如果一个线类元素已存在 value 的情况下设置其 value,则原来那个 value 会被删掉,这里的删除将会导致原始 value 永远地离开场景。

const svg = sd.svg();
const line = new sd.Line(svg, "A");
sd.main(async () => {
    await sd.pause();
    line.startAnimate().value("B").endAnimate(); // "A" 被删除
    await sd.pause();
    line.startAnimate().value(null).endAnimate(); // "B" 被删除
});

drop

class BaseElement {
    drop(): SDNode;
}

前文提到,如果 value 方法去删除已存在的 value,会导致 value 永远地离开场景,但部分情况下,我们可能希望被删除的 value 只是被元素丢弃了,它仍然存在于场景之中。有两种达成这个目的的解决方案,最直接的便是 drop 方法。

const svg = sd.svg();
const EX = sd.exit();
const line11 = new sd.Line(svg, "A");
const line12 = new sd.Line(svg).x(100);
const line21 = new sd.Line(svg, "B").y(50);
const line22 = new sd.Line(svg).x(100).y(50);
sd.main(async () => {
    await sd.pause();
    line12.startAnimate().valueFromExist(line11.drop()).endAnimate(); // 直接使用 drop 方法丢弃 box11 中的 value
    const b = line21.value().onExit(EX.drop()); // 设置 box21 的 value 以 drop 的形式从 box21 中删除
    line21.value(null); // 删除 box21 中的 value
    line22.startAnimate().valueFromExist(b).endAnimate();
});

试一试,在上例中,当 line11 或者 line21 中的 value 被删除时,它们并不会被移除到场景之外,而仅仅是被元素所丢弃了。所以我们能在之后的代码中,将这两个 value 分别转交给 line12 和 line22。