使用 Excel JavaScript API 处理形状

Excel 将形状定义为位于 Excel 绘图层上的任何对象。 这意味着单元格外部的任何内容都是一个形状。 本文介绍如何将几何形状、线条和图像与 ShapeShapeCollection API 结合使用。 图表 在自己的文章《 使用 Excel JavaScript API 处理图表》中进行了介绍。

下图显示了构成温度计的形状。 作为 Excel 形状制作的温度计的图像。

创建形状

形状通过 创建并存储在工作表的形状集合中, Worksheet.shapes () 。 ShapeCollection.add*有多种方法可用于此目的。 所有形状在添加到集合时都有为其生成的名称和 ID。 这些分别是 nameid 属性。 name 可由加载项设置,以便使用 ShapeCollection.getItem(name) 方法轻松检索。

以下类型的形状是使用关联的 方法添加的。

形状 Add 方法 签名
几何形状 addGeometricShape addGeometricShape(geometricShapeType: Excel.GeometricShapeType): Excel.Shape
图像 (JPEG 或 PNG) addImage addImage(base64ImageString: string): Excel.Shape
折线图 addLine addLine(startLeft: number, startTop: number, endLeft: number, endTop: number, connectorType?: Excel.ConnectorType): Excel.Shape
Svg addSvg addSvg(xml: string): Excel.Shape
文本框 addTextBox addTextBox(text?: string): Excel.Shape

几何形状

使用 ShapeCollection.addGeometricShape创建几何形状。 该方法采用 GeometricShapeType 枚举作为参数。

下面的代码示例创建一个名为 “Square” 的 150x150 像素矩形,该矩形位于工作表的顶部和左侧 100 像素处。

// This sample creates a rectangle positioned 100 pixels from the top and left sides
// of the worksheet and is 150x150 pixels.
await Excel.run(async (context) => {
    let shapes = context.workbook.worksheets.getItem("MyWorksheet").shapes;

    let rectangle = shapes.addGeometricShape(Excel.GeometricShapeType.rectangle);
    rectangle.left = 100;
    rectangle.top = 100;
    rectangle.height = 150;
    rectangle.width = 150;
    rectangle.name = "Square";

    await context.sync();
});

图像

JPEG、PNG 和 SVG 图像可以作为形状插入工作表中。 方法 ShapeCollection.addImage 采用 base64 编码的字符串作为参数。 这是字符串形式的 JPEG 或 PNG 图像。 ShapeCollection.addSvg 也采用字符串,但此参数是定义图形的 XML。

以下代码示例将 FileReader 加载的图像文件显示为字符串。 字符串具有元数据“base64”,在创建形状之前删除。

// This sample creates an image as a Shape object in the worksheet.
let myFile = document.getElementById("selectedFile");
let reader = new FileReader();

reader.onload = (event) => {
    Excel.run(function (context) {
        let startIndex = reader.result.toString().indexOf("base64,");
        let myBase64 = reader.result.toString().substr(startIndex + 7);
        let sheet = context.workbook.worksheets.getItem("MyWorksheet");
        let image = sheet.shapes.addImage(myBase64);
        image.name = "Image";
        return context.sync();
    }).catch(errorHandlerFunction);
};

// Read in the image file as a data URL.
reader.readAsDataURL(myFile.files[0]);

Lines

使用 ShapeCollection.addLine创建行。 该方法需要线条起点和终点的左边距和上边距。 它还需要一个 ConnectorType 枚举来指定两个终结点之间的换行方式。 下面的代码示例在工作表上创建一条直线。

// This sample creates a straight line from [200,50] to [300,150] on the worksheet.
await Excel.run(async (context) => {
    let shapes = context.workbook.worksheets.getItem("MyWorksheet").shapes;
    let line = shapes.addLine(200, 50, 300, 150, Excel.ConnectorType.straight);
    line.name = "StraightLine";
    await context.sync();
});

线条可以连接到其他 Shape 对象。 connectBeginShapeconnectEndShape 方法将线条的起点和终点附加到指定连接点处的形状。 这些点的位置因形状而异,但 Shape.connectionSiteCount 可用于确保外接程序不会连接到超出边界的点。 使用 disconnectBeginShapedisconnectEndShape 方法断开线条与任何附加形状的连接。

下面的代码示例将 “MyLine” 线连接到名为 “LeftShape”“RightShape”的两个形状。

// This sample connects a line between two shapes at connection points '0' and '3'.
await Excel.run(async (context) => {
    let shapes = context.workbook.worksheets.getItem("MyWorksheet").shapes;
    let line = shapes.getItem("MyLine").line;
    line.connectBeginShape(shapes.getItem("LeftShape"), 0);
    line.connectEndShape(shapes.getItem("RightShape"), 3);
    await context.sync();
});

移动形状和调整形状大小

形状位于工作表顶部。 其位置由 lefttop 属性定义。 它们充当工作表各自边缘的边距,左上角为 [0, 0]。 可以直接设置这些值,也可以使用 和 incrementTop 方法从当前位置incrementLeft进行调整。 形状从默认位置旋转多少也以这种方式建立,属性 rotation 为绝对量,方法 incrementRotation 调整现有旋转。

形状相对于其他形状的深度由 zorderPosition 属性定义。 这是使用 setZOrder 方法设置的,该方法采用 ShapeZOrdersetZOrder 调整当前形状相对于其他形状的顺序。

加载项有几个选项可用于更改形状的高度和宽度。 height设置 或 width 属性会更改指定的维度,而不会更改其他维度。 scaleHeightscaleWidth 根据提供的 ShapeScaleType) 的值,调整形状相对于当前大小或原始大小 (的相应尺寸。 可选的 ShapeScaleFrom 参数指定形状从何处缩放 (左上角、中间或右下角) 。 lockAspectRatio如果 属性为 true,则缩放方法通过同时调整另一个维度来保持形状的当前纵横比。

注意

对 和 width 属性的直接更改height只会影响该属性,而不管属性的值如何lockAspectRatio

下面的代码示例演示了一个形状被缩放到其原始大小的 1.25 倍,并旋转了 30 度。

// In this sample, the shape "Octagon" is rotated 30 degrees clockwise
// and scaled 25% larger, with the upper-left corner remaining in place.
await Excel.run(async (context) => {
    let sheet = context.workbook.worksheets.getItem("MyWorksheet");

    let shape = sheet.shapes.getItem("Octagon");
    shape.incrementRotation(30);
    shape.lockAspectRatio = true;
    shape.scaleWidth(
        1.25,
        Excel.ShapeScaleType.currentSize,
        Excel.ShapeScaleFrom.scaleFromTopLeft);

    await context.sync();
});

形状中的文本

几何形状可以包含文本。 形状具有 textFrameTextFrame 类型的属性。 对象 TextFrame 管理文本显示选项 (,例如边距和文本溢出) 。 TextFrame.textRange 是具有文本内容和字体设置的 TextRange 对象。

下面的代码示例创建一个名为“Wave”的几何形状,其文本为“形状文本”。 它还调整形状和文本颜色,并将文本的水平对齐方式设置为中心。

// This sample creates a light-blue wave shape and adds the purple text "Shape text" to the center.
await Excel.run(async (context) => {
    let shapes = context.workbook.worksheets.getItem("MyWorksheet").shapes;
    let wave = shapes.addGeometricShape(Excel.GeometricShapeType.wave);
    wave.left = 100;
    wave.top = 400;
    wave.height = 50;
    wave.width = 150;

    wave.name = "Wave";
    wave.fill.setSolidColor("lightblue");

    wave.textFrame.textRange.text = "Shape text";
    wave.textFrame.textRange.font.color = "purple";
    wave.textFrame.horizontalAlignment = Excel.ShapeTextHorizontalAlignment.center;

    await context.sync();
});

addTextBox方法ShapeCollection创建GeometricShape具有白色背景和黑色文本的 类型的 Rectangle 。 这与 Excel 的 “文本框” 按钮在“ 插入 ”选项卡上创建的相同。 addTextBox 采用字符串参数来设置 的文本 TextRange

下面的代码示例演示如何创建一个带有文本“Hello!”的文本框。

// This sample creates a text box with the text "Hello!" and sizes it appropriately.
await Excel.run(async (context) => {
    let shapes = context.workbook.worksheets.getItem("MyWorksheet").shapes;
    let textbox = shapes.addTextBox("Hello!");
    textbox.left = 100;
    textbox.top = 100;
    textbox.height = 20;
    textbox.width = 45;
    textbox.name = "Textbox";
    await context.sync();
});

形状组

形状可以组合在一起。 这允许用户将它们视为用于定位、调整大小和其他相关任务的单个实体。 ShapeGroup 是 的一种Shape类型,因此外接程序会将该组视为单个形状。

下面的代码示例显示了三个组合在一起的形状。 后续代码示例显示将形状组移动到右侧 50 像素。

// This sample takes three previously-created shapes ("Square", "Pentagon", and "Octagon")
// and groups them into a single ShapeGroup.
await Excel.run(async (context) => {
    let shapes = context.workbook.worksheets.getItem("MyWorksheet").shapes;
    let square = shapes.getItem("Square");
    let pentagon = shapes.getItem("Pentagon");
    let octagon = shapes.getItem("Octagon");

    let shapeGroup = shapes.addGroup([square, pentagon, octagon]);
    shapeGroup.name = "Group";
    console.log("Shapes grouped");

    await context.sync();
});

// This sample moves the previously created shape group to the right by 50 pixels.
await Excel.run(async (context) => {
    let shapes = context.workbook.worksheets.getItem("MyWorksheet").shapes;
    let shapeGroup = shapes.getItem("Group");
    shapeGroup.incrementLeft(50);
    await context.sync();
});

重要

组中的各个形状通过 ShapeGroup.shapes 属性引用,该属性的类型为 GroupShapeCollection。 分组后,不再可通过工作表的形状集合访问它们。 例如,如果工作表有三个形状,并且它们全部组合在一起,则工作表的 shapes.getCount 方法将返回计数 1。

将形状导出为图像

任何 Shape 对象都可以转换为图像。 Shape.getAsImage 返回 base64 编码的字符串。 图像的格式指定为传递给 的 getAsImagePictureFormat 枚举。

await Excel.run(async (context) => {
    let shapes = context.workbook.worksheets.getItem("MyWorksheet").shapes;
    let shape = shapes.getItem("Image");
    let stringResult = shape.getAsImage(Excel.PictureFormat.png);

    await context.sync();

    console.log(stringResult.value);
    // Instead of logging, your add-in may use the base64-encoded string to save the image as a file or insert it in HTML.
});

删除形状

使用 Shape 对象的 delete 方法从工作表中删除形状。 不需要其他元数据。

下面的代码示例从 MyWorksheet 中删除所有形状。

// This deletes all the shapes from "MyWorksheet".
await Excel.run(async (context) => {
    let sheet = context.workbook.worksheets.getItem("MyWorksheet");
    let shapes = sheet.shapes;

    // We'll load all the shapes in the collection without loading their properties.
    shapes.load("items/$none");
    await context.sync();

    shapes.items.forEach(function (shape) {
        shape.delete();
    });
    
    await context.sync();
});

另请参阅