I have an html element which renders at the bottom left corner of an object when it’s clicked and I’m trying to make it stick to the object when the canvas is panned or zoomed. The code I have works great when user pans the canvas but it gets wacky when zoomed in or out.
Looking at the below gif, you’ll see the red box:
- renders fine when canvas is moved
- gets dislocated when zoomed in or out
- corrects back to proper location when moved/panned again
Here’s part of the code:
JavaScript:
function getNodeTemplate() {
return new go.Node(go.Panel.Auto, {
isShadowed: true,
shadowVisible: true,
shadowOffset: new go.Point(0, 2),
shadowColor: 'rgba(0, 0, 0, 0.4)',
shadowBlur: 1,
selectionObjectName: 'box',
})
.bind(
new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(
go.Point.stringify
)
)
.add(
new go.Shape('RoundedRectangle', {
name: 'box',
desiredSize: new go.Size(28, 28),
fill: 'gold',
})
)
.add(new go.TextBlock('').bind(new go.Binding('text', 'name')));
}
function getLinkTemplate() {
return new go.Link()
.add(new go.Shape({ strokeWidth: 2, stroke: 'black' }))
.add(new go.Shape({ toArrow: 'Standard', alignment: go.Spot.Center }));
}
function getModel() {
const nodes = [
{ key: 'A', name: 'A', loc: '0 0' },
{ key: 'B', name: 'B', loc: '116 0' },
{ key: 'C', name: 'C', loc: '160 100' },
];
const links = [ { from: 'A', to: 'B' }, { from: 'B', to: 'C' } ];
return new go.GraphLinksModel(nodes, links);
}
function getPopoverLocation(
d: go.Diagram,
obj: go.GraphObject
): go.Point {
const point = obj.getDocumentPoint(go.Spot.BottomLeft);
point.y = point.y + 8;
return d.transformDocToView(point);
}
let clickedObject: go.GraphObject | null = null;
const popover: HTMLDivElement | null = document.querySelector('#popover');
popover?.addEventListener('click', () => {
popover.style.display = 'none';
});
const d = go.GraphObject.make(go.Diagram, 'box', {
ObjectSingleClicked: (e) => {
clickedObject = e?.subject?.part;
if (clickedObject) {
const loc = getPopoverLocation(e.diagram, clickedObject);
if (popover) {
popover.style.display = 'revert';
popover.style.left = `${loc.x}px`;
popover.style.top = `${loc.y}px`;
}
}
},
});
d.nodeTemplate = getNodeTemplate();
d.linkTemplate = getLinkTemplate();
d.model = getModel();
d?.addDiagramListener('ViewportBoundsChanged', (e: go.DiagramEvent) => {
if (clickedObject) {
const loc = getPopoverLocation(e.diagram, clickedObject);
if (popover) {
popover.style.left = `${loc.x}px`;
popover.style.top = `${loc.y}px`;
}
}
});
HTML:
<body>
<div class="canvas">
<div id="box"></div>
<div id="popover" role="dialog" class="popover">dialog stuff</div>
</div>
<script type="module" src="/src/main.ts"></script>
</body>
Styles:
body {
background-color: whitesmoke;
padding: 0px;
margin: 0px;
}
.canvas {
width: 100vw;
height: 100vh;
}
#box {
width: 100%;
height: 100%;
background-color: #dae4e4;
}
.popover {
left: 0;
position: absolute;
display: none;
top: 0;
z-index: 10;
border: 1px solid red;
border-radius: 5px;
padding: 4px;
font-family: monospace;
font-size: 12px;
background-color: rgb(255, 190, 190);
box-shadow: 2px 2px 0px 0px rgb(248, 97, 97);
}
Not sure if this is a bug or I’m doing something wrong.