fix: Prevent gradual canvas misalignment (#3833)

Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
Hargobind S. Khalsa 2021-07-14 04:29:22 -07:00 committed by GitHub
parent 0749d2c1f3
commit 9581c45522
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 48 additions and 79 deletions

View file

@ -64,14 +64,14 @@ const strokeRectWithRotation = (
angle: number,
fill: boolean = false,
) => {
context.save();
context.translate(cx, cy);
context.rotate(angle);
if (fill) {
context.fillRect(x - cx, y - cy, width, height);
}
context.strokeRect(x - cx, y - cy, width, height);
context.rotate(-angle);
context.translate(-cx, -cy);
context.restore();
};
const strokeDiamondWithRotation = (
@ -82,6 +82,7 @@ const strokeDiamondWithRotation = (
cy: number,
angle: number,
) => {
context.save();
context.translate(cx, cy);
context.rotate(angle);
context.beginPath();
@ -91,8 +92,7 @@ const strokeDiamondWithRotation = (
context.lineTo(-width / 2, 0);
context.closePath();
context.stroke();
context.rotate(-angle);
context.translate(-cx, -cy);
context.restore();
};
const strokeEllipseWithRotation = (
@ -128,7 +128,7 @@ const strokeGrid = (
width: number,
height: number,
) => {
const origStrokeStyle = context.strokeStyle;
context.save();
context.strokeStyle = "rgba(0,0,0,0.1)";
context.beginPath();
for (let x = offsetX; x < offsetX + width + gridSize * 2; x += gridSize) {
@ -140,7 +140,7 @@ const strokeGrid = (
context.lineTo(offsetX + width + gridSize * 2, y);
}
context.stroke();
context.strokeStyle = origStrokeStyle;
context.restore();
};
const renderLinearPointHandles = (
@ -149,9 +149,8 @@ const renderLinearPointHandles = (
sceneState: SceneState,
element: NonDeleted<ExcalidrawLinearElement>,
) => {
context.save();
context.translate(sceneState.scrollX, sceneState.scrollY);
const origStrokeStyle = context.strokeStyle;
const lineWidth = context.lineWidth;
context.lineWidth = 1 / sceneState.zoom.value;
LinearElementEditor.getPointsGlobalCoordinates(element).forEach(
@ -171,10 +170,7 @@ const renderLinearPointHandles = (
);
},
);
context.setLineDash([]);
context.lineWidth = lineWidth;
context.translate(-sceneState.scrollX, -sceneState.scrollY);
context.strokeStyle = origStrokeStyle;
context.restore();
};
export const renderScene = (
@ -207,6 +203,8 @@ export const renderScene = (
const context = canvas.getContext("2d")!;
context.setTransform(1, 0, 0, 1, 0, 0);
context.save();
context.scale(scale, scale);
// When doing calculations based on canvas width we should used normalized one
@ -227,10 +225,10 @@ export const renderScene = (
if (hasTransparence) {
context.clearRect(0, 0, normalizedCanvasWidth, normalizedCanvasHeight);
}
const fillStyle = context.fillStyle;
context.save();
context.fillStyle = sceneState.viewBackgroundColor;
context.fillRect(0, 0, normalizedCanvasWidth, normalizedCanvasHeight);
context.fillStyle = fillStyle;
context.restore();
} else {
context.clearRect(0, 0, normalizedCanvasWidth, normalizedCanvasHeight);
}
@ -238,6 +236,7 @@ export const renderScene = (
// Apply zoom
const zoomTranslationX = sceneState.zoom.translation.x;
const zoomTranslationY = sceneState.zoom.translation.y;
context.save();
context.translate(zoomTranslationX, zoomTranslationY);
context.scale(sceneState.zoom.value, sceneState.zoom.value);
@ -382,6 +381,7 @@ export const renderScene = (
const locallySelectedElements = getSelectedElements(elements, appState);
// Paint resize transformHandles
context.save();
context.translate(sceneState.scrollX, sceneState.scrollY);
if (locallySelectedElements.length === 1) {
context.fillStyle = oc.white;
@ -427,12 +427,11 @@ export const renderScene = (
);
renderTransformHandles(context, sceneState, transformHandles, 0);
}
context.translate(-sceneState.scrollX, -sceneState.scrollY);
context.restore();
}
// Reset zoom
context.scale(1 / sceneState.zoom.value, 1 / sceneState.zoom.value);
context.translate(-zoomTranslationX, -zoomTranslationY);
context.restore();
// Paint remote pointers
for (const clientId in sceneState.remotePointerViewportCoords) {
@ -457,9 +456,7 @@ export const renderScene = (
const { background, stroke } = getClientColors(clientId, appState);
const strokeStyle = context.strokeStyle;
const fillStyle = context.fillStyle;
const globalAlpha = context.globalAlpha;
context.save();
context.strokeStyle = stroke;
context.fillStyle = background;
@ -545,9 +542,7 @@ export const renderScene = (
);
}
context.strokeStyle = strokeStyle;
context.fillStyle = fillStyle;
context.globalAlpha = globalAlpha;
context.restore();
context.closePath();
}
@ -561,8 +556,7 @@ export const renderScene = (
sceneState,
);
const fillStyle = context.fillStyle;
const strokeStyle = context.strokeStyle;
context.save();
context.fillStyle = SCROLLBAR_COLOR;
context.strokeStyle = "rgba(255,255,255,0.8)";
[scrollBars.horizontal, scrollBars.vertical].forEach((scrollBar) => {
@ -577,12 +571,10 @@ export const renderScene = (
);
}
});
context.fillStyle = fillStyle;
context.strokeStyle = strokeStyle;
context.restore();
}
context.scale(1 / scale, 1 / scale);
context.restore();
return { atLeastOneVisibleElement: visibleElements.length > 0, scrollBars };
};
@ -595,7 +587,7 @@ const renderTransformHandles = (
Object.keys(transformHandles).forEach((key) => {
const transformHandle = transformHandles[key as TransformHandleType];
if (transformHandle !== undefined) {
const lineWidth = context.lineWidth;
context.save();
context.lineWidth = 1 / sceneState.zoom.value;
if (key === "rotation") {
fillCircle(
@ -617,7 +609,7 @@ const renderTransformHandles = (
true, // fill before stroke
);
}
context.lineWidth = lineWidth;
context.restore();
}
});
};
@ -645,18 +637,13 @@ const renderSelectionBorder = (
const elementWidth = elementX2 - elementX1;
const elementHeight = elementY2 - elementY1;
const initialLineDash = context.getLineDash();
const lineWidth = context.lineWidth;
const lineDashOffset = context.lineDashOffset;
const strokeStyle = context.strokeStyle;
const dashedLinePadding = 4 / sceneState.zoom.value;
const dashWidth = 8 / sceneState.zoom.value;
const spaceWidth = 4 / sceneState.zoom.value;
context.lineWidth = 1 / sceneState.zoom.value;
context.save();
context.translate(sceneState.scrollX, sceneState.scrollY);
context.lineWidth = 1 / sceneState.zoom.value;
const count = selectionColors.length;
for (let index = 0; index < count; ++index) {
@ -677,11 +664,7 @@ const renderSelectionBorder = (
angle,
);
}
context.lineDashOffset = lineDashOffset;
context.strokeStyle = strokeStyle;
context.lineWidth = lineWidth;
context.setLineDash(initialLineDash);
context.translate(-sceneState.scrollX, -sceneState.scrollY);
context.restore();
};
const renderBindingHighlight = (
@ -689,21 +672,15 @@ const renderBindingHighlight = (
sceneState: SceneState,
suggestedBinding: SuggestedBinding,
) => {
// preserve context settings to restore later
const originalStrokeStyle = context.strokeStyle;
const originalLineWidth = context.lineWidth;
const renderHighlight = Array.isArray(suggestedBinding)
? renderBindingHighlightForSuggestedPointBinding
: renderBindingHighlightForBindableElement;
context.save();
context.translate(sceneState.scrollX, sceneState.scrollY);
renderHighlight(context, suggestedBinding as any);
// restore context settings
context.strokeStyle = originalStrokeStyle;
context.lineWidth = originalLineWidth;
context.translate(-sceneState.scrollX, -sceneState.scrollY);
context.restore();
};
const renderBindingHighlightForBindableElement = (