Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't update dynamiccssrules on panning, add chromium percentage missing redraw workaround #140

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/components/network-area-diagram-viewer/dynamic-css-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,34 @@ export const DEFAULT_DYNAMIC_CSS_RULES: CSS_RULE[] = [
threshold: 20000,
thresholdStatus: THRESHOLD_STATUS.BELOW,
},
// TODO using svg at the begining in selectors to increase specificity
// because the svg already contains the same selectors. Is there a better way ?
{
cssSelector: 'svg .nad-disconnected .nad-edge-path',
belowThresholdCssDeclaration: { 'stroke-dasharray': '10, 10' },
aboveThresholdCssDeclaration: { 'stroke-dasharray': '0.5%, 0.5%' },
threshold: 2500,
thresholdStatus: THRESHOLD_STATUS.ABOVE,
},
{
cssSelector: 'svg .nad-branch-edges .nad-edge-path, svg .nad-3wt-edges .nad-edge-path',
belowThresholdCssDeclaration: { 'stroke-width': '3' },
aboveThresholdCssDeclaration: { 'stroke-width': '0.25%' },
threshold: 1000,
thresholdStatus: THRESHOLD_STATUS.ABOVE,
},
{
cssSelector: 'svg .nad-branch-edges .nad-winding, svg .nad-3wt-nodes .nad-winding',
belowThresholdCssDeclaration: { 'stroke-width': '3' },
aboveThresholdCssDeclaration: { 'stroke-width': '0.25%' },
threshold: 1000,
thresholdStatus: THRESHOLD_STATUS.ABOVE,
},
{
cssSelector: 'svg .nad-vl-nodes circle.nad-unknown-busnode',
belowThresholdCssDeclaration: { 'stroke-width': '3' },
aboveThresholdCssDeclaration: { 'stroke-width': '0.25%' },
threshold: 1000,
thresholdStatus: THRESHOLD_STATUS.ABOVE,
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ export type OnToggleNadHoverCallbackType = (
equipmentType: string
) => void;

// update css rules when zoom changes by this amount. This allows to not
// update when only translating (when translating, round errors lead to
// epsilon changes in the float values), or not too often a bit when smooth
// scrolling (the update may be entirely missed when smooth scrolling if
// you don't go over the threshold but that's ok, the user doesn't see rule
// threshold values so he will continue to zoom in or out to trigger the
// rule update. Using a debounce that ensure the last update is done
// eventually may be even worse as it could introduce flicker after the
// delay after the last zoom change. We need a value that gives good
// performance but doesn't change the user experience
const dynamicCssRulesUpdateThreshold = 0.01;

export class NetworkAreaDiagramViewer {
container: HTMLElement;
svgContent: string;
Expand All @@ -73,6 +85,7 @@ export class NetworkAreaDiagramViewer {
onSelectNodeCallback: OnSelectNodeCallbackType | null;
dynamicCssRules: CSS_RULE[];
onToggleHoverCallback: OnToggleNadHoverCallbackType | null;
previousMaxDisplayedSize: number;

constructor(
container: HTMLElement,
Expand Down Expand Up @@ -113,6 +126,7 @@ export class NetworkAreaDiagramViewer {
this.onMoveTextNodeCallback = onMoveTextNodeCallback;
this.onSelectNodeCallback = onSelectNodeCallback;
this.onToggleHoverCallback = onToggleHoverCallback;
this.previousMaxDisplayedSize = 0;
}

public setWidth(width: number): void {
Expand Down Expand Up @@ -171,6 +185,14 @@ export class NetworkAreaDiagramViewer {
this.svgDraw?.viewbox(viewBox);
}

public setPreviousMaxDisplayedSize(previousMaxDisplayedSize: number): void {
this.previousMaxDisplayedSize = previousMaxDisplayedSize;
}

public getPreviousMaxDisplayedSize(): number {
return this.previousMaxDisplayedSize;
}

public getDynamicCssRules() {
return this.dynamicCssRules;
}
Expand Down Expand Up @@ -1371,6 +1393,15 @@ export class NetworkAreaDiagramViewer {

public checkAndUpdateLevelOfDetail(svg: SVGSVGElement) {
const maxDisplayedSize = this.getCurrentlyMaxDisplayedSize();
const previousMaxDisplayedSize = this.getPreviousMaxDisplayedSize();
// in case of bad or unset values NaN or Infinity, this condition is skipped and the function behaves as if zoom changed
if (
Math.abs(previousMaxDisplayedSize - maxDisplayedSize) / previousMaxDisplayedSize <
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

divide by zero

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in case of bad or unset values NaN or Infinity, this condition is skipped and the function behaves as if zoom changed

dynamicCssRulesUpdateThreshold
) {
return;
}
this.setPreviousMaxDisplayedSize(maxDisplayedSize);
// We will check each dynamic css rule to see if we crossed a zoom threshold. If this is the case, we
// update the rule's threshold status and trigger the CSS change in the SVG.
this.getDynamicCssRules().forEach((rule) => {
Expand All @@ -1388,5 +1419,33 @@ export class NetworkAreaDiagramViewer {
this.updateSvgCssDisplayValue(svg, rule.cssSelector, rule.aboveThresholdCssDeclaration);
}
});
//Workaround chromium (tested on edge and google-chrome 131) doesn't
//redraw things with percentages on viewbox changes but it should, so
//we force it. This is not strictly related to the enableLevelOfDetail
//and dynamic css feature, but it turns out that we use percentages in
//css only in the case where enableLevelOfDetail=true, so we can do the
//workaround here at each viewbox change until we have other needs or
//until we remove the workaround entirely. Firefox does correctly
//redraw, but we force for everyone to have the same behavior
//everywhere and detect problems more easily. We can't use
//innerHtml+='' on the <style> tags because values set with
//setProperty(key, value) in updateSvgCssDisplayValue are not reflected
//in the html text so the innerHTML trick has the effect of resetting
//them. So instead of doing it on the svg, we do it on all its children
//that are not style elements. This won't work if there are deeply
//nested style elements that need dynamic css rules but in practice
//only the root style element has dynamic rules so it's ok.
//TODO Remove this when chromium fixes their bug.
//TODO If this workaround causes problems, we can find a better way to
//force a redraw that doesnt change the elements in the dom.
const innerSvg = svg.querySelector('svg');
if (innerSvg) {
for (const child of innerSvg.children) {
// annoying, sometimes lowercase (html), sometimes uppercase (xml in xhtml or svg))
if (child.nodeName.toUpperCase() != 'STYLE') {
child.innerHTML += '';
}
}
}
}
}
Loading