Creating a text near the center of a shape should put it in the center (#270)
* Snap to element center * Fixed typo * Added comment * Reduced threshold to 30 * Skip snapping if alt key is pressed * Fixed creating text with shape tool
This commit is contained in:
parent
068dca604f
commit
1739540f00
@ -26,7 +26,8 @@ import {
|
|||||||
hasBackground,
|
hasBackground,
|
||||||
hasStroke,
|
hasStroke,
|
||||||
getElementAtPosition,
|
getElementAtPosition,
|
||||||
createScene
|
createScene,
|
||||||
|
getElementContainingPosition
|
||||||
} from "./scene";
|
} from "./scene";
|
||||||
|
|
||||||
import { renderScene } from "./renderer";
|
import { renderScene } from "./renderer";
|
||||||
@ -110,6 +111,7 @@ function addTextElement(
|
|||||||
|
|
||||||
const ELEMENT_SHIFT_TRANSLATE_AMOUNT = 5;
|
const ELEMENT_SHIFT_TRANSLATE_AMOUNT = 5;
|
||||||
const ELEMENT_TRANSLATE_AMOUNT = 1;
|
const ELEMENT_TRANSLATE_AMOUNT = 1;
|
||||||
|
const TEXT_TO_CENTER_SNAP_THRESHOLD = 30;
|
||||||
|
|
||||||
let lastCanvasWidth = -1;
|
let lastCanvasWidth = -1;
|
||||||
let lastCanvasHeight = -1;
|
let lastCanvasHeight = -1;
|
||||||
@ -702,10 +704,25 @@ class App extends React.Component<{}, AppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isTextElement(element)) {
|
if (isTextElement(element)) {
|
||||||
|
let textX = e.clientX;
|
||||||
|
let textY = e.clientY;
|
||||||
|
if (!e.altKey) {
|
||||||
|
const snappedToCenterPosition = this.getTextWysiwygSnappedToCenterPosition(
|
||||||
|
x,
|
||||||
|
y
|
||||||
|
);
|
||||||
|
if (snappedToCenterPosition) {
|
||||||
|
element.x = snappedToCenterPosition.elementCenterX;
|
||||||
|
element.y = snappedToCenterPosition.elementCenterY;
|
||||||
|
textX = snappedToCenterPosition.wysiwygX;
|
||||||
|
textY = snappedToCenterPosition.wysiwygY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
textWysiwyg({
|
textWysiwyg({
|
||||||
initText: "",
|
initText: "",
|
||||||
x: e.clientX,
|
x: textX,
|
||||||
y: e.clientY,
|
y: textY,
|
||||||
strokeColor: this.state.currentItemStrokeColor,
|
strokeColor: this.state.currentItemStrokeColor,
|
||||||
font: this.state.currentItemFont,
|
font: this.state.currentItemFont,
|
||||||
onSubmit: text => {
|
onSubmit: text => {
|
||||||
@ -932,12 +949,6 @@ class App extends React.Component<{}, AppState> {
|
|||||||
e.clientX - CANVAS_WINDOW_OFFSET_LEFT - this.state.scrollX;
|
e.clientX - CANVAS_WINDOW_OFFSET_LEFT - this.state.scrollX;
|
||||||
const y = e.clientY - CANVAS_WINDOW_OFFSET_TOP - this.state.scrollY;
|
const y = e.clientY - CANVAS_WINDOW_OFFSET_TOP - this.state.scrollY;
|
||||||
const elementAtPosition = getElementAtPosition(elements, x, y);
|
const elementAtPosition = getElementAtPosition(elements, x, y);
|
||||||
if (elementAtPosition && !isTextElement(elementAtPosition)) {
|
|
||||||
return;
|
|
||||||
} else if (elementAtPosition) {
|
|
||||||
elements.splice(elements.indexOf(elementAtPosition), 1);
|
|
||||||
this.forceUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
const element = newElement(
|
const element = newElement(
|
||||||
"text",
|
"text",
|
||||||
@ -954,7 +965,11 @@ class App extends React.Component<{}, AppState> {
|
|||||||
let initText = "";
|
let initText = "";
|
||||||
let textX = e.clientX;
|
let textX = e.clientX;
|
||||||
let textY = e.clientY;
|
let textY = e.clientY;
|
||||||
if (elementAtPosition) {
|
|
||||||
|
if (elementAtPosition && isTextElement(elementAtPosition)) {
|
||||||
|
elements.splice(elements.indexOf(elementAtPosition), 1);
|
||||||
|
this.forceUpdate();
|
||||||
|
|
||||||
Object.assign(element, elementAtPosition);
|
Object.assign(element, elementAtPosition);
|
||||||
// x and y will change after calling addTextElement function
|
// x and y will change after calling addTextElement function
|
||||||
element.x = elementAtPosition.x + elementAtPosition.width / 2;
|
element.x = elementAtPosition.x + elementAtPosition.width / 2;
|
||||||
@ -970,6 +985,18 @@ class App extends React.Component<{}, AppState> {
|
|||||||
elementAtPosition.y +
|
elementAtPosition.y +
|
||||||
CANVAS_WINDOW_OFFSET_TOP +
|
CANVAS_WINDOW_OFFSET_TOP +
|
||||||
elementAtPosition.height / 2;
|
elementAtPosition.height / 2;
|
||||||
|
} else if (!e.altKey) {
|
||||||
|
const snappedToCenterPosition = this.getTextWysiwygSnappedToCenterPosition(
|
||||||
|
x,
|
||||||
|
y
|
||||||
|
);
|
||||||
|
|
||||||
|
if (snappedToCenterPosition) {
|
||||||
|
element.x = snappedToCenterPosition.elementCenterX;
|
||||||
|
element.y = snappedToCenterPosition.elementCenterY;
|
||||||
|
textX = snappedToCenterPosition.wysiwygX;
|
||||||
|
textY = snappedToCenterPosition.wysiwygY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
textWysiwyg({
|
textWysiwyg({
|
||||||
@ -1036,6 +1063,35 @@ class App extends React.Component<{}, AppState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private getTextWysiwygSnappedToCenterPosition(x: number, y: number) {
|
||||||
|
const elementClickedInside = getElementContainingPosition(elements, x, y);
|
||||||
|
if (elementClickedInside) {
|
||||||
|
const elementCenterX =
|
||||||
|
elementClickedInside.x + elementClickedInside.width / 2;
|
||||||
|
const elementCenterY =
|
||||||
|
elementClickedInside.y + elementClickedInside.height / 2;
|
||||||
|
const distanceToCenter = Math.hypot(
|
||||||
|
x - elementCenterX,
|
||||||
|
y - elementCenterY
|
||||||
|
);
|
||||||
|
const isSnappedToCenter =
|
||||||
|
distanceToCenter < TEXT_TO_CENTER_SNAP_THRESHOLD;
|
||||||
|
if (isSnappedToCenter) {
|
||||||
|
const wysiwygX =
|
||||||
|
this.state.scrollX +
|
||||||
|
elementClickedInside.x +
|
||||||
|
CANVAS_WINDOW_OFFSET_LEFT +
|
||||||
|
elementClickedInside.width / 2;
|
||||||
|
const wysiwygY =
|
||||||
|
this.state.scrollY +
|
||||||
|
elementClickedInside.y +
|
||||||
|
CANVAS_WINDOW_OFFSET_TOP +
|
||||||
|
elementClickedInside.height / 2;
|
||||||
|
return { wysiwygX, wysiwygY, elementCenterX, elementCenterY };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
renderScene(elements, rc, canvas, {
|
renderScene(elements, rc, canvas, {
|
||||||
scrollX: this.state.scrollX,
|
scrollX: this.state.scrollX,
|
||||||
|
@ -138,7 +138,6 @@ export function renderElement(
|
|||||||
context.fillStyle = fillStyle;
|
context.fillStyle = fillStyle;
|
||||||
context.font = font;
|
context.font = font;
|
||||||
context.globalAlpha = 1;
|
context.globalAlpha = 1;
|
||||||
console.log(element);
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Unimplemented type " + element.type);
|
throw new Error("Unimplemented type " + element.type);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { ExcalidrawElement } from "../element/types";
|
import { ExcalidrawElement } from "../element/types";
|
||||||
import { hitTest } from "../element/collision";
|
import { hitTest } from "../element/collision";
|
||||||
|
import { getElementAbsoluteCoords } from "../element";
|
||||||
|
|
||||||
export const hasBackground = (elements: ExcalidrawElement[]) =>
|
export const hasBackground = (elements: ExcalidrawElement[]) =>
|
||||||
elements.some(
|
elements.some(
|
||||||
@ -36,3 +37,20 @@ export function getElementAtPosition(
|
|||||||
|
|
||||||
return hitElement;
|
return hitElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getElementContainingPosition(
|
||||||
|
elements: ExcalidrawElement[],
|
||||||
|
x: number,
|
||||||
|
y: number
|
||||||
|
) {
|
||||||
|
let hitElement = null;
|
||||||
|
// We need to to hit testing from front (end of the array) to back (beginning of the array)
|
||||||
|
for (let i = elements.length - 1; i >= 0; --i) {
|
||||||
|
const [x1, y1, x2, y2] = getElementAbsoluteCoords(elements[i]);
|
||||||
|
if (x1 < x && x < x2 && y1 < y && y < y2) {
|
||||||
|
hitElement = elements[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hitElement;
|
||||||
|
}
|
||||||
|
@ -14,5 +14,10 @@ export {
|
|||||||
restoreFromLocalStorage,
|
restoreFromLocalStorage,
|
||||||
saveToLocalStorage
|
saveToLocalStorage
|
||||||
} from "./data";
|
} from "./data";
|
||||||
export { hasBackground, hasStroke, getElementAtPosition } from "./comparisons";
|
export {
|
||||||
|
hasBackground,
|
||||||
|
hasStroke,
|
||||||
|
getElementAtPosition,
|
||||||
|
getElementContainingPosition
|
||||||
|
} from "./comparisons";
|
||||||
export { createScene } from "./createScene";
|
export { createScene } from "./createScene";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user