fix: Prevent gradual canvas misalignment (#3833)
Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
parent
0749d2c1f3
commit
9581c45522
@ -11,6 +11,14 @@ The change should be grouped under one of the below section and must contain PR
|
|||||||
Please add the latest change on the top under the correct section.
|
Please add the latest change on the top under the correct section.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
## Excalidraw Library
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- Prevent gradual misalignment of the canvas due to floating point rounding errors [#3833](https://github.com/excalidraw/excalidraw/pull/3833).
|
||||||
|
|
||||||
## 0.9.0 (2021-07-10)
|
## 0.9.0 (2021-07-10)
|
||||||
|
|
||||||
## Excalidraw API
|
## Excalidraw API
|
||||||
|
@ -102,8 +102,8 @@ const generateElementCanvas = (
|
|||||||
padding * zoom.value * 2;
|
padding * zoom.value * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.save();
|
||||||
context.translate(padding * zoom.value, padding * zoom.value);
|
context.translate(padding * zoom.value, padding * zoom.value);
|
||||||
|
|
||||||
context.scale(
|
context.scale(
|
||||||
window.devicePixelRatio * zoom.value,
|
window.devicePixelRatio * zoom.value,
|
||||||
window.devicePixelRatio * zoom.value,
|
window.devicePixelRatio * zoom.value,
|
||||||
@ -112,12 +112,7 @@ const generateElementCanvas = (
|
|||||||
const rc = rough.canvas(canvas);
|
const rc = rough.canvas(canvas);
|
||||||
|
|
||||||
drawElementOnCanvas(element, rc, context);
|
drawElementOnCanvas(element, rc, context);
|
||||||
|
context.restore();
|
||||||
context.translate(-(padding * zoom.value), -(padding * zoom.value));
|
|
||||||
context.scale(
|
|
||||||
1 / (window.devicePixelRatio * zoom.value),
|
|
||||||
1 / (window.devicePixelRatio * zoom.value),
|
|
||||||
);
|
|
||||||
return {
|
return {
|
||||||
element,
|
element,
|
||||||
canvas,
|
canvas,
|
||||||
@ -175,11 +170,9 @@ const drawElementOnCanvas = (
|
|||||||
document.body.appendChild(context.canvas);
|
document.body.appendChild(context.canvas);
|
||||||
}
|
}
|
||||||
context.canvas.setAttribute("dir", rtl ? "rtl" : "ltr");
|
context.canvas.setAttribute("dir", rtl ? "rtl" : "ltr");
|
||||||
const font = context.font;
|
context.save();
|
||||||
context.font = getFontString(element);
|
context.font = getFontString(element);
|
||||||
const fillStyle = context.fillStyle;
|
|
||||||
context.fillStyle = element.strokeColor;
|
context.fillStyle = element.strokeColor;
|
||||||
const textAlign = context.textAlign;
|
|
||||||
context.textAlign = element.textAlign as CanvasTextAlign;
|
context.textAlign = element.textAlign as CanvasTextAlign;
|
||||||
|
|
||||||
// Canvas does not support multiline text by default
|
// Canvas does not support multiline text by default
|
||||||
@ -199,9 +192,7 @@ const drawElementOnCanvas = (
|
|||||||
(index + 1) * lineHeight - verticalOffset,
|
(index + 1) * lineHeight - verticalOffset,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
context.fillStyle = fillStyle;
|
context.restore();
|
||||||
context.font = font;
|
|
||||||
context.textAlign = textAlign;
|
|
||||||
if (shouldTemporarilyAttach) {
|
if (shouldTemporarilyAttach) {
|
||||||
context.canvas.remove();
|
context.canvas.remove();
|
||||||
}
|
}
|
||||||
@ -518,6 +509,7 @@ const drawElementFromCanvas = (
|
|||||||
|
|
||||||
const cx = ((x1 + x2) / 2 + sceneState.scrollX) * window.devicePixelRatio;
|
const cx = ((x1 + x2) / 2 + sceneState.scrollX) * window.devicePixelRatio;
|
||||||
const cy = ((y1 + y2) / 2 + sceneState.scrollY) * window.devicePixelRatio;
|
const cy = ((y1 + y2) / 2 + sceneState.scrollY) * window.devicePixelRatio;
|
||||||
|
context.save();
|
||||||
context.scale(1 / window.devicePixelRatio, 1 / window.devicePixelRatio);
|
context.scale(1 / window.devicePixelRatio, 1 / window.devicePixelRatio);
|
||||||
context.translate(cx, cy);
|
context.translate(cx, cy);
|
||||||
context.rotate(element.angle);
|
context.rotate(element.angle);
|
||||||
@ -531,9 +523,7 @@ const drawElementFromCanvas = (
|
|||||||
elementWithCanvas.canvas!.width / elementWithCanvas.canvasZoom,
|
elementWithCanvas.canvas!.width / elementWithCanvas.canvasZoom,
|
||||||
elementWithCanvas.canvas!.height / elementWithCanvas.canvasZoom,
|
elementWithCanvas.canvas!.height / elementWithCanvas.canvasZoom,
|
||||||
);
|
);
|
||||||
context.rotate(-element.angle);
|
context.restore();
|
||||||
context.translate(-cx, -cy);
|
|
||||||
context.scale(window.devicePixelRatio, window.devicePixelRatio);
|
|
||||||
|
|
||||||
// Clear the nested element we appended to the DOM
|
// Clear the nested element we appended to the DOM
|
||||||
};
|
};
|
||||||
@ -548,18 +538,14 @@ export const renderElement = (
|
|||||||
const generator = rc.generator;
|
const generator = rc.generator;
|
||||||
switch (element.type) {
|
switch (element.type) {
|
||||||
case "selection": {
|
case "selection": {
|
||||||
|
context.save();
|
||||||
context.translate(
|
context.translate(
|
||||||
element.x + sceneState.scrollX,
|
element.x + sceneState.scrollX,
|
||||||
element.y + sceneState.scrollY,
|
element.y + sceneState.scrollY,
|
||||||
);
|
);
|
||||||
const fillStyle = context.fillStyle;
|
|
||||||
context.fillStyle = "rgba(0, 0, 255, 0.10)";
|
context.fillStyle = "rgba(0, 0, 255, 0.10)";
|
||||||
context.fillRect(0, 0, element.width, element.height);
|
context.fillRect(0, 0, element.width, element.height);
|
||||||
context.fillStyle = fillStyle;
|
context.restore();
|
||||||
context.translate(
|
|
||||||
-element.x - sceneState.scrollX,
|
|
||||||
-element.y - sceneState.scrollY,
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "freedraw": {
|
case "freedraw": {
|
||||||
@ -577,13 +563,12 @@ export const renderElement = (
|
|||||||
const cy = (y1 + y2) / 2 + sceneState.scrollY;
|
const cy = (y1 + y2) / 2 + sceneState.scrollY;
|
||||||
const shiftX = (x2 - x1) / 2 - (element.x - x1);
|
const shiftX = (x2 - x1) / 2 - (element.x - x1);
|
||||||
const shiftY = (y2 - y1) / 2 - (element.y - y1);
|
const shiftY = (y2 - y1) / 2 - (element.y - y1);
|
||||||
|
context.save();
|
||||||
context.translate(cx, cy);
|
context.translate(cx, cy);
|
||||||
context.rotate(element.angle);
|
context.rotate(element.angle);
|
||||||
context.translate(-shiftX, -shiftY);
|
context.translate(-shiftX, -shiftY);
|
||||||
drawElementOnCanvas(element, rc, context);
|
drawElementOnCanvas(element, rc, context);
|
||||||
context.translate(shiftX, shiftY);
|
context.restore();
|
||||||
context.rotate(-element.angle);
|
|
||||||
context.translate(-cx, -cy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -607,13 +592,12 @@ export const renderElement = (
|
|||||||
const cy = (y1 + y2) / 2 + sceneState.scrollY;
|
const cy = (y1 + y2) / 2 + sceneState.scrollY;
|
||||||
const shiftX = (x2 - x1) / 2 - (element.x - x1);
|
const shiftX = (x2 - x1) / 2 - (element.x - x1);
|
||||||
const shiftY = (y2 - y1) / 2 - (element.y - y1);
|
const shiftY = (y2 - y1) / 2 - (element.y - y1);
|
||||||
|
context.save();
|
||||||
context.translate(cx, cy);
|
context.translate(cx, cy);
|
||||||
context.rotate(element.angle);
|
context.rotate(element.angle);
|
||||||
context.translate(-shiftX, -shiftY);
|
context.translate(-shiftX, -shiftY);
|
||||||
drawElementOnCanvas(element, rc, context);
|
drawElementOnCanvas(element, rc, context);
|
||||||
context.translate(shiftX, shiftY);
|
context.restore();
|
||||||
context.rotate(-element.angle);
|
|
||||||
context.translate(-cx, -cy);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -64,14 +64,14 @@ const strokeRectWithRotation = (
|
|||||||
angle: number,
|
angle: number,
|
||||||
fill: boolean = false,
|
fill: boolean = false,
|
||||||
) => {
|
) => {
|
||||||
|
context.save();
|
||||||
context.translate(cx, cy);
|
context.translate(cx, cy);
|
||||||
context.rotate(angle);
|
context.rotate(angle);
|
||||||
if (fill) {
|
if (fill) {
|
||||||
context.fillRect(x - cx, y - cy, width, height);
|
context.fillRect(x - cx, y - cy, width, height);
|
||||||
}
|
}
|
||||||
context.strokeRect(x - cx, y - cy, width, height);
|
context.strokeRect(x - cx, y - cy, width, height);
|
||||||
context.rotate(-angle);
|
context.restore();
|
||||||
context.translate(-cx, -cy);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const strokeDiamondWithRotation = (
|
const strokeDiamondWithRotation = (
|
||||||
@ -82,6 +82,7 @@ const strokeDiamondWithRotation = (
|
|||||||
cy: number,
|
cy: number,
|
||||||
angle: number,
|
angle: number,
|
||||||
) => {
|
) => {
|
||||||
|
context.save();
|
||||||
context.translate(cx, cy);
|
context.translate(cx, cy);
|
||||||
context.rotate(angle);
|
context.rotate(angle);
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
@ -91,8 +92,7 @@ const strokeDiamondWithRotation = (
|
|||||||
context.lineTo(-width / 2, 0);
|
context.lineTo(-width / 2, 0);
|
||||||
context.closePath();
|
context.closePath();
|
||||||
context.stroke();
|
context.stroke();
|
||||||
context.rotate(-angle);
|
context.restore();
|
||||||
context.translate(-cx, -cy);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const strokeEllipseWithRotation = (
|
const strokeEllipseWithRotation = (
|
||||||
@ -128,7 +128,7 @@ const strokeGrid = (
|
|||||||
width: number,
|
width: number,
|
||||||
height: number,
|
height: number,
|
||||||
) => {
|
) => {
|
||||||
const origStrokeStyle = context.strokeStyle;
|
context.save();
|
||||||
context.strokeStyle = "rgba(0,0,0,0.1)";
|
context.strokeStyle = "rgba(0,0,0,0.1)";
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
for (let x = offsetX; x < offsetX + width + gridSize * 2; x += gridSize) {
|
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.lineTo(offsetX + width + gridSize * 2, y);
|
||||||
}
|
}
|
||||||
context.stroke();
|
context.stroke();
|
||||||
context.strokeStyle = origStrokeStyle;
|
context.restore();
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderLinearPointHandles = (
|
const renderLinearPointHandles = (
|
||||||
@ -149,9 +149,8 @@ const renderLinearPointHandles = (
|
|||||||
sceneState: SceneState,
|
sceneState: SceneState,
|
||||||
element: NonDeleted<ExcalidrawLinearElement>,
|
element: NonDeleted<ExcalidrawLinearElement>,
|
||||||
) => {
|
) => {
|
||||||
|
context.save();
|
||||||
context.translate(sceneState.scrollX, sceneState.scrollY);
|
context.translate(sceneState.scrollX, sceneState.scrollY);
|
||||||
const origStrokeStyle = context.strokeStyle;
|
|
||||||
const lineWidth = context.lineWidth;
|
|
||||||
context.lineWidth = 1 / sceneState.zoom.value;
|
context.lineWidth = 1 / sceneState.zoom.value;
|
||||||
|
|
||||||
LinearElementEditor.getPointsGlobalCoordinates(element).forEach(
|
LinearElementEditor.getPointsGlobalCoordinates(element).forEach(
|
||||||
@ -171,10 +170,7 @@ const renderLinearPointHandles = (
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
context.setLineDash([]);
|
context.restore();
|
||||||
context.lineWidth = lineWidth;
|
|
||||||
context.translate(-sceneState.scrollX, -sceneState.scrollY);
|
|
||||||
context.strokeStyle = origStrokeStyle;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const renderScene = (
|
export const renderScene = (
|
||||||
@ -207,6 +203,8 @@ export const renderScene = (
|
|||||||
|
|
||||||
const context = canvas.getContext("2d")!;
|
const context = canvas.getContext("2d")!;
|
||||||
|
|
||||||
|
context.setTransform(1, 0, 0, 1, 0, 0);
|
||||||
|
context.save();
|
||||||
context.scale(scale, scale);
|
context.scale(scale, scale);
|
||||||
|
|
||||||
// When doing calculations based on canvas width we should used normalized one
|
// When doing calculations based on canvas width we should used normalized one
|
||||||
@ -227,10 +225,10 @@ export const renderScene = (
|
|||||||
if (hasTransparence) {
|
if (hasTransparence) {
|
||||||
context.clearRect(0, 0, normalizedCanvasWidth, normalizedCanvasHeight);
|
context.clearRect(0, 0, normalizedCanvasWidth, normalizedCanvasHeight);
|
||||||
}
|
}
|
||||||
const fillStyle = context.fillStyle;
|
context.save();
|
||||||
context.fillStyle = sceneState.viewBackgroundColor;
|
context.fillStyle = sceneState.viewBackgroundColor;
|
||||||
context.fillRect(0, 0, normalizedCanvasWidth, normalizedCanvasHeight);
|
context.fillRect(0, 0, normalizedCanvasWidth, normalizedCanvasHeight);
|
||||||
context.fillStyle = fillStyle;
|
context.restore();
|
||||||
} else {
|
} else {
|
||||||
context.clearRect(0, 0, normalizedCanvasWidth, normalizedCanvasHeight);
|
context.clearRect(0, 0, normalizedCanvasWidth, normalizedCanvasHeight);
|
||||||
}
|
}
|
||||||
@ -238,6 +236,7 @@ export const renderScene = (
|
|||||||
// Apply zoom
|
// Apply zoom
|
||||||
const zoomTranslationX = sceneState.zoom.translation.x;
|
const zoomTranslationX = sceneState.zoom.translation.x;
|
||||||
const zoomTranslationY = sceneState.zoom.translation.y;
|
const zoomTranslationY = sceneState.zoom.translation.y;
|
||||||
|
context.save();
|
||||||
context.translate(zoomTranslationX, zoomTranslationY);
|
context.translate(zoomTranslationX, zoomTranslationY);
|
||||||
context.scale(sceneState.zoom.value, sceneState.zoom.value);
|
context.scale(sceneState.zoom.value, sceneState.zoom.value);
|
||||||
|
|
||||||
@ -382,6 +381,7 @@ export const renderScene = (
|
|||||||
const locallySelectedElements = getSelectedElements(elements, appState);
|
const locallySelectedElements = getSelectedElements(elements, appState);
|
||||||
|
|
||||||
// Paint resize transformHandles
|
// Paint resize transformHandles
|
||||||
|
context.save();
|
||||||
context.translate(sceneState.scrollX, sceneState.scrollY);
|
context.translate(sceneState.scrollX, sceneState.scrollY);
|
||||||
if (locallySelectedElements.length === 1) {
|
if (locallySelectedElements.length === 1) {
|
||||||
context.fillStyle = oc.white;
|
context.fillStyle = oc.white;
|
||||||
@ -427,12 +427,11 @@ export const renderScene = (
|
|||||||
);
|
);
|
||||||
renderTransformHandles(context, sceneState, transformHandles, 0);
|
renderTransformHandles(context, sceneState, transformHandles, 0);
|
||||||
}
|
}
|
||||||
context.translate(-sceneState.scrollX, -sceneState.scrollY);
|
context.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset zoom
|
// Reset zoom
|
||||||
context.scale(1 / sceneState.zoom.value, 1 / sceneState.zoom.value);
|
context.restore();
|
||||||
context.translate(-zoomTranslationX, -zoomTranslationY);
|
|
||||||
|
|
||||||
// Paint remote pointers
|
// Paint remote pointers
|
||||||
for (const clientId in sceneState.remotePointerViewportCoords) {
|
for (const clientId in sceneState.remotePointerViewportCoords) {
|
||||||
@ -457,9 +456,7 @@ export const renderScene = (
|
|||||||
|
|
||||||
const { background, stroke } = getClientColors(clientId, appState);
|
const { background, stroke } = getClientColors(clientId, appState);
|
||||||
|
|
||||||
const strokeStyle = context.strokeStyle;
|
context.save();
|
||||||
const fillStyle = context.fillStyle;
|
|
||||||
const globalAlpha = context.globalAlpha;
|
|
||||||
context.strokeStyle = stroke;
|
context.strokeStyle = stroke;
|
||||||
context.fillStyle = background;
|
context.fillStyle = background;
|
||||||
|
|
||||||
@ -545,9 +542,7 @@ export const renderScene = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.strokeStyle = strokeStyle;
|
context.restore();
|
||||||
context.fillStyle = fillStyle;
|
|
||||||
context.globalAlpha = globalAlpha;
|
|
||||||
context.closePath();
|
context.closePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,8 +556,7 @@ export const renderScene = (
|
|||||||
sceneState,
|
sceneState,
|
||||||
);
|
);
|
||||||
|
|
||||||
const fillStyle = context.fillStyle;
|
context.save();
|
||||||
const strokeStyle = context.strokeStyle;
|
|
||||||
context.fillStyle = SCROLLBAR_COLOR;
|
context.fillStyle = SCROLLBAR_COLOR;
|
||||||
context.strokeStyle = "rgba(255,255,255,0.8)";
|
context.strokeStyle = "rgba(255,255,255,0.8)";
|
||||||
[scrollBars.horizontal, scrollBars.vertical].forEach((scrollBar) => {
|
[scrollBars.horizontal, scrollBars.vertical].forEach((scrollBar) => {
|
||||||
@ -577,12 +571,10 @@ export const renderScene = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
context.fillStyle = fillStyle;
|
context.restore();
|
||||||
context.strokeStyle = strokeStyle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.scale(1 / scale, 1 / scale);
|
context.restore();
|
||||||
|
|
||||||
return { atLeastOneVisibleElement: visibleElements.length > 0, scrollBars };
|
return { atLeastOneVisibleElement: visibleElements.length > 0, scrollBars };
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -595,7 +587,7 @@ const renderTransformHandles = (
|
|||||||
Object.keys(transformHandles).forEach((key) => {
|
Object.keys(transformHandles).forEach((key) => {
|
||||||
const transformHandle = transformHandles[key as TransformHandleType];
|
const transformHandle = transformHandles[key as TransformHandleType];
|
||||||
if (transformHandle !== undefined) {
|
if (transformHandle !== undefined) {
|
||||||
const lineWidth = context.lineWidth;
|
context.save();
|
||||||
context.lineWidth = 1 / sceneState.zoom.value;
|
context.lineWidth = 1 / sceneState.zoom.value;
|
||||||
if (key === "rotation") {
|
if (key === "rotation") {
|
||||||
fillCircle(
|
fillCircle(
|
||||||
@ -617,7 +609,7 @@ const renderTransformHandles = (
|
|||||||
true, // fill before stroke
|
true, // fill before stroke
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
context.lineWidth = lineWidth;
|
context.restore();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -645,18 +637,13 @@ const renderSelectionBorder = (
|
|||||||
const elementWidth = elementX2 - elementX1;
|
const elementWidth = elementX2 - elementX1;
|
||||||
const elementHeight = elementY2 - elementY1;
|
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 dashedLinePadding = 4 / sceneState.zoom.value;
|
||||||
const dashWidth = 8 / sceneState.zoom.value;
|
const dashWidth = 8 / sceneState.zoom.value;
|
||||||
const spaceWidth = 4 / sceneState.zoom.value;
|
const spaceWidth = 4 / sceneState.zoom.value;
|
||||||
|
|
||||||
context.lineWidth = 1 / sceneState.zoom.value;
|
context.save();
|
||||||
|
|
||||||
context.translate(sceneState.scrollX, sceneState.scrollY);
|
context.translate(sceneState.scrollX, sceneState.scrollY);
|
||||||
|
context.lineWidth = 1 / sceneState.zoom.value;
|
||||||
|
|
||||||
const count = selectionColors.length;
|
const count = selectionColors.length;
|
||||||
for (let index = 0; index < count; ++index) {
|
for (let index = 0; index < count; ++index) {
|
||||||
@ -677,11 +664,7 @@ const renderSelectionBorder = (
|
|||||||
angle,
|
angle,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
context.lineDashOffset = lineDashOffset;
|
context.restore();
|
||||||
context.strokeStyle = strokeStyle;
|
|
||||||
context.lineWidth = lineWidth;
|
|
||||||
context.setLineDash(initialLineDash);
|
|
||||||
context.translate(-sceneState.scrollX, -sceneState.scrollY);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderBindingHighlight = (
|
const renderBindingHighlight = (
|
||||||
@ -689,21 +672,15 @@ const renderBindingHighlight = (
|
|||||||
sceneState: SceneState,
|
sceneState: SceneState,
|
||||||
suggestedBinding: SuggestedBinding,
|
suggestedBinding: SuggestedBinding,
|
||||||
) => {
|
) => {
|
||||||
// preserve context settings to restore later
|
|
||||||
const originalStrokeStyle = context.strokeStyle;
|
|
||||||
const originalLineWidth = context.lineWidth;
|
|
||||||
|
|
||||||
const renderHighlight = Array.isArray(suggestedBinding)
|
const renderHighlight = Array.isArray(suggestedBinding)
|
||||||
? renderBindingHighlightForSuggestedPointBinding
|
? renderBindingHighlightForSuggestedPointBinding
|
||||||
: renderBindingHighlightForBindableElement;
|
: renderBindingHighlightForBindableElement;
|
||||||
|
|
||||||
|
context.save();
|
||||||
context.translate(sceneState.scrollX, sceneState.scrollY);
|
context.translate(sceneState.scrollX, sceneState.scrollY);
|
||||||
renderHighlight(context, suggestedBinding as any);
|
renderHighlight(context, suggestedBinding as any);
|
||||||
|
|
||||||
// restore context settings
|
context.restore();
|
||||||
context.strokeStyle = originalStrokeStyle;
|
|
||||||
context.lineWidth = originalLineWidth;
|
|
||||||
context.translate(-sceneState.scrollX, -sceneState.scrollY);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderBindingHighlightForBindableElement = (
|
const renderBindingHighlightForBindableElement = (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user