Shape Alignment

SpreadJS makes it easy to fine-tune multiple shape's positions using shape alignment options, distribution options or snapping them to other shapes or grids.

Description
app.vue
index.html
Copy to CodeMine

You can align shapes using the following code:

    var commandManager = spread.commandManager();
    commandManager.execute({
        cmd: 'moveShapesByHAlign',
        sheetName: sheet.name(),
        shapeNames: ['rect1','line1','heart1','group1'],
        alignment: GC.Spread.Sheets.Shapes.HorizontalAlign.left
    });
    commandManager.execute({
        cmd: 'moveShapesByVAlign',
        sheetName: sheet.name(),
        shapeNames: ['rect1','line1','heart1','group1'],
        alignment: GC.Spread.Sheets.Shapes.VerticalAlign.bottom
    });
    commandManager.execute({
            cmd: 'moveShapesByHDistribute',
            sheetName: sheet.name(),
            shapeNames: ['rect1','line1','heart1','group1']
        });
    });
    commandManager.execute({
            cmd: 'moveShapesByVDistribute',
            sheetName: sheet.name(),
            shapeNames: ['rect1','line1','heart1','group1']
        });
    });

You can change snapMode to align shapes easily

    var sheet = spread.getActiveSheet();
    sheet.shapes.snapMode(GC.Spread.Sheets.Shapes.SnapMode.grid);

Now when you drag or resize a shape, the shape will align to grid lines automatically.

You can also align to other shapes:

    var sheet = spread.getActiveSheet();
    sheet.shapes.snapMode(GC.Spread.Sheets.Shapes.SnapMode.grid | GC.Spread.Sheets.Shapes.SnapMode.shape);
You can align shapes using the following code: You can change snapMode to align shapes easily Now when you drag or resize a shape, the shape will align to grid lines automatically. You can also align to other shapes:
<template> <div class="sample-tutorial"> <gc-spread-sheets class="sample-spreadsheets" @workbookInitialized="initSpread"> </gc-spread-sheets> <div class="options-container"> <div class="option-row"> Select two or more shapes then change the different alignment options below to see the effects. alignment to see the effect. </div> <div class="option-row"> *for the vertical and horizontal distribution, all three shapes must be selected. </div> <div class="option-row"> <label>- Horizontal Alignment</label> <hr> <select id="moveShapesByHAlign" :disabled="!canAlign" @change="alignDd($event,'moveShapesByHAlign')"> <option value="left">left</option> <option value="center">center</option> <option value="right">right</option> </select> <br /> </div> <div class="option-row"> <label>Horizontal Distribute Selected Shapes</label> <input type="button" :disabled="!canDistribute" value="Horizontal" @click="align('moveShapesByHDistribute')" /> <br /> </div> <div class="option-row"> <label>- Vertical Alignment</label> <hr> <select id="moveShapesByVAlign" :disabled="!canAlign" @change="alignDd($event,'moveShapesByVAlign')"> <option value="top">top</option> <option value="middle">middle</option> <option value="bottom">bottom</option> </select> <br /> </div> <div class="option-row"> <label>Vertical Distribute Selected Shapes</label> <input type="button" :disabled="!canDistribute" value="Vertical" @click="align('moveShapesByVDistribute')" /> <br /> </div> <div class="option-row"> <label>Change the SnapMode options to align, drag, or resize shapes to the nearest edge based on gridlines or other shapes in the worksheet. </label> <select id="changeSnapMode" @change="changeSnapMode($event)"> <option value="0" selected>none</option> <option value="1">grid</option> <option value="2">shape</option> <option value="3">grid and shape</option> </select> </div> </div> </div> </template> <script setup> import GC from "@mescius/spread-sheets"; import "@mescius/spread-sheets-shapes"; import { ref } from 'vue'; const spreadRef = ref(null); const canAlign = ref(false); const canDistribute = ref(false); function initSpread(spread) { spreadRef.value = spread; let sheet = spread.getActiveSheet(); var rect1 = sheet.shapes.add( "rect1", GC.Spread.Sheets.Shapes.AutoShapeType.rectangle, 100, 50, 230, 100 ); var rect2 = sheet.shapes.add( "rect2", GC.Spread.Sheets.Shapes.AutoShapeType.rectangle, 200, 200, 150, 100 ); var rect3 = sheet.shapes.add( "rect3", GC.Spread.Sheets.Shapes.AutoShapeType.rectangle, 500, 300, 200, 100 ); spread.bind( GC.Spread.Sheets.Events.ShapeSelectionChanged, function () { let activeSheet = spread.getActiveSheet(); let shapes = activeSheet.shapes.all(), activeShapes = []; for (let i = 0; i < shapes.length; i++) { let shape = shapes[i]; if (shape.isSelected()) { activeShapes.push(shape); } } if (activeShapes.length > 1) { canAlign.value = true; } else { canAlign.value = false; } if (activeShapes.length > 2) { canDistribute.value = true; } else { canDistribute.value = false; } } ); } function align(cmd, alignment) { const spread = spreadRef.value; let activeSheet = spread.getActiveSheet(); let activeShapes = activeSheet.shapes.all().filter(function (sp) { return sp.isSelected(); }); var commandManager = spread.commandManager(); commandManager.execute({ cmd: cmd, sheetName: activeSheet.name(), shapeNames: activeShapes.map(function (s) { return s.name(); }), alignment: getAlignmentEnum(alignment), }); } function alignDd(e, cmd) { const spread = spreadRef.value; let activeSheet = spread.getActiveSheet(); let activeShapes = activeSheet.shapes.all().filter(function (sp) { return sp.isSelected(); }); var commandManager = spread.commandManager(); commandManager.execute({ cmd: cmd, sheetName: activeSheet.name(), shapeNames: activeShapes.map(function (s) { return s.name(); }), alignment: getAlignmentEnum(e.target.value), }); } function getAlignmentEnum(alignment) { switch (alignment) { case "left": return GC.Spread.Sheets.Shapes.HorizontalAlign.left; case "center": return GC.Spread.Sheets.Shapes.HorizontalAlign.center; case "right": return GC.Spread.Sheets.Shapes.HorizontalAlign.right; case "top": return GC.Spread.Sheets.Shapes.VerticalAlign.top; case "middle": return GC.Spread.Sheets.Shapes.VerticalAlign.middle; case "bottom": return GC.Spread.Sheets.Shapes.VerticalAlign.bottom; } } function changeSnapMode(e) { let sheet = spreadRef.value.getActiveSheet(); switch (e.target.value) { case '0': sheet.shapes.snapMode(GC.Spread.Sheets.Shapes.SnapMode.none); return; case '1': sheet.shapes.snapMode(GC.Spread.Sheets.Shapes.SnapMode.grid); return; case '2': sheet.shapes.snapMode(GC.Spread.Sheets.Shapes.SnapMode.shape); return; case '3': sheet.shapes.snapMode(GC.Spread.Sheets.Shapes.SnapMode.grid | GC.Spread.Sheets.Shapes.SnapMode.shape); return; default: return; } } </script> <style scoped> #app { height: 100%; } .sample-tutorial { position: relative; height: 100%; overflow: hidden; } .sample-spreadsheets { width: calc(100% - 350px); height: 100%; overflow: hidden; float: left; } .options-container { float: right; width: 350px; padding: 12px; height: 100%; box-sizing: border-box; background: #fbfbfb; overflow: auto; } .options-container { float: right; width: 350px; padding: 12px; height: 100%; box-sizing: border-box; background: #fbfbfb; overflow: auto; } .option-row { font-size: 14px; padding: 5px; margin-top: 5px; } .sample-options { z-index: 1000; } label { display: block; margin-bottom: 6px; } p { padding: 2px 10px; background-color: #F4F8EB; } input { padding: 4px 6px; width: 160px; } input[type="button"] { margin-top: 6px; display: block; } body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; } </style>
<!DOCTYPE html> <html lang="en" style="height:100%;font-size:14px;"> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <title>SpreadJS VUE</title> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/en/vue3/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <script src="$DEMOROOT$/en/vue3/node_modules/systemjs/dist/system.src.js"></script> <script src="./systemjs.config.js"></script> <script src="./compiler.js" type="module"></script> <script> var System = SystemJS; System.import("./src/app.js"); System.import('$DEMOROOT$/en/lib/vue3/license.js'); </script> </head> <body> <div id="app"></div> </body> </html>
(function (global) { SystemJS.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, packageConfigPaths: [ './node_modules/*/package.json', "./node_modules/@mescius/*/package.json", "./node_modules/@babel/*/package.json", "./node_modules/@vue/*/package.json" ], map: { '@mescius/spread-sheets': 'npm:@mescius/spread-sheets/index.js', '@mescius/spread-sheets-vue': 'npm:@mescius/spread-sheets-vue/index.js', '@mescius/spread-sheets-shapes': 'npm:@mescius/spread-sheets-shapes/index.js', 'vue': "npm:vue/dist/vue.esm-browser.js", 'tiny-emitter': 'npm:tiny-emitter/index.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', "systemjs-babel-build": "npm:systemjs-plugin-babel/systemjs-babel-browser.js", }, meta: { '*.css': { loader: 'systemjs-plugin-css' }, '*.vue': { loader: "../plugin-vue/index.js" } } }); })(this);