Icon position is slightly getting changed during animation

I want to show a loader icon inside a node. I have written the below code for animation. But I see that the icon is slightly moving after every rotation. If there’s a text beside it, it is moving the text as well.

var myAnimation = new go.Animation();
myAnimation.easing = go.Animation.EaseLinear;
diagram.nodes.each((node) => {
myAnimation.duration = 4000;
if (node.findObject('TESTLOADINGICON')) {
   myAnimation.add(
	node.findObject('TESTLOADINGICON'),
	'angle',
	0,
	360
   );
})
})

VID-20240722-WA0012_1_1
VID-20240722-WA0014_1

Yes, as the angle changes, the width and height of the rectangular area changes.

Is the icon/Picture in a “Horizontal” Panel?

Yes it is in a horizontal panel. I have also specified height and width, but even it has enough space, it is slightly changing position. How can i avoid it?

I suppose you could surround the Picture with a “Table” Panel that has a fixed width and height big enough to hold the maximum width and height of the Picture at all angles.

Thanks, this worked. But it’s always holding fixed width and height. I want height and width go away when the picture inside Table panel is not available.

What do you mean when you say a Picture is not available?

You could bind the width/height or visibility of the table panel based on whether the picture is “available.”

We have a loadingState property on node, when the property value is inProgress, we will be showing loading animation icon in the Picture shape. If the loadingState is null, then Picture visible property value is false.

As the Picture is wrapped inside Table Panel. Even though Picture visiblity is false, it’s occupying the space of table panel height and width.

I did try binding the visibility of the table panel based on whether picture is available or not. But as the hierarchy is Picture inside Table. Table visibility binding is invoked first and then Picture visibility.

Table visibility binding on “loadingState” node property:
var pictureObj = part.findObject(“LoadingIcon”);
if(pictureObj.isVisibleObject())
return true;
else
return false;
Picture visibility binding on “loadingState” node property:
if(loadingState == “inProgress”)
return true;
else
return false;

  1. Initially loadingState is null.
    → First Table visibility is triggered and pictureObj.isVisibileObject is false it returned false;
    → Then Picture visibility is triggered and the value is null, it also returned false.
  2. loadingState is set to “inProgress”
    → First Table visibility is triggered and pictureObj.isVisibileObject is still false, it returned false;
    → Then Picture visibility is triggered as the value is inProgress, it returned true. But the Table visibility is false, the source svg is not rendered in Picture shape.

You should probably just use the same visible binding as the Picture.

Here’s what I tried:

<!-- Copyright 1998-2024 by Northwoods Software Corporation. -->
<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta charset="UTF-8" />
    <script src="https://unpkg.com/gojs@latest/release/go-debug.js"></script>

    <script id="code">
      const init = () => {
        diagram = new go.Diagram('myDiagramDiv');

        diagram.nodeTemplate =
          new go.Node('Auto', { minSize: new go.Size(100, NaN) })
            .add(
              new go.Shape('RoundedRectangle', { fill: 'lightblue' }),
              new go.Panel('Horizontal', { margin: 5 })
                .add(
                  new go.Panel('Table', { desiredSize: new go.Size(15, 15), margin: new go.Margin(0, 8, 0, 0) })
                    .bind('visible', 'loading', (l) => l === 'inProgress')
                    .add(
                      new go.Shape('LineV', { name: 'LOADER', desiredSize: new go.Size(15, 15) })
                        .bind('visible', 'loading', (l) => l === 'inProgress')
                    ),
                  new go.TextBlock('Hello')
                )
            );

        diagram.model = new go.GraphLinksModel([
          { key: 'A', loading: null },
          { key: 'B', loading: null },
        ]);
      };

      const load = () => {
        // set loading data properties
        diagram.model.commit((m) => {
          for (var nd of m.nodeDataArray) {
            m.set(nd, 'loading', 'inProgress');
          }
        }, 'load');

        // begin continuous animation
        const anim = new go.Animation();
        anim.easing = go.Animation.EaseLinear;
        anim.duration = 2000;
        anim.runCount = Infinity;
        diagram.nodes.each((n) => {
          if (n.findObject('LOADER')) {
            anim.add(n.findObject('LOADER'), 'angle', 0, 360);
          }
        });
        anim.start();

        // finish "loading" after 4s
        setTimeout(() => {
          diagram.model.commit((m) => {
            for (var nd of m.nodeDataArray) {
              m.set(nd, 'loading', null);
            }
          }, 'load');
          anim.stop();
        }, 4000);
      };
    </script>
  </head>
  <body onload="init()">
    <div id="sample">
      <div
        id="myDiagramDiv"
        style="border: solid 1px black; width: 600px; height: 600px"></div>
      <button onclick="load()">Load</button>
    </div>
  </body>
</html>