Compare commits
8 Commits
master
...
zsviczian-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
44390cb146 | ||
![]() |
9da3e47877 | ||
![]() |
9f6edd8eaa | ||
![]() |
0d60253de7 | ||
![]() |
4027a5b245 | ||
![]() |
49f2c88978 | ||
![]() |
156b8b422b | ||
![]() |
e2982a2968 |
@ -1038,6 +1038,10 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
this.state.activeEmbeddable?.element === el &&
|
this.state.activeEmbeddable?.element === el &&
|
||||||
this.state.activeEmbeddable?.state === "hover";
|
this.state.activeEmbeddable?.state === "hover";
|
||||||
|
|
||||||
|
// Modify the scale based on el.scale property
|
||||||
|
const [xScale, yScale] = el.scale ?? [1, 1];
|
||||||
|
const scaledTransform = `scale(${scale * xScale}, ${scale * yScale})`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={el.id}
|
key={el.id}
|
||||||
@ -1048,14 +1052,13 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
transform: isVisible
|
transform: isVisible
|
||||||
? `translate(${x - this.state.offsetLeft}px, ${
|
? `translate(${x - this.state.offsetLeft}px, ${
|
||||||
y - this.state.offsetTop
|
y - this.state.offsetTop
|
||||||
}px) scale(${scale})`
|
}px) ${scaledTransform}`
|
||||||
: "none",
|
: "none",
|
||||||
display: isVisible ? "block" : "none",
|
display: isVisible ? "block" : "none",
|
||||||
opacity: el.opacity / 100,
|
opacity: el.opacity / 100,
|
||||||
["--embeddable-radius" as string]: `${getCornerRadius(
|
["--embeddable-radius" as string]: `${
|
||||||
Math.min(el.width, el.height),
|
getCornerRadius(Math.min(el.width, el.height), el) / xScale
|
||||||
el,
|
}px`,
|
||||||
)}px`,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -1077,8 +1080,8 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
}}*/
|
}}*/
|
||||||
className="excalidraw__embeddable-container__inner"
|
className="excalidraw__embeddable-container__inner"
|
||||||
style={{
|
style={{
|
||||||
width: isVisible ? `${el.width}px` : 0,
|
width: isVisible ? `${el.width / xScale}px` : 0,
|
||||||
height: isVisible ? `${el.height}px` : 0,
|
height: isVisible ? `${el.height / yScale}px` : 0,
|
||||||
transform: isVisible ? `rotate(${el.angle}rad)` : "none",
|
transform: isVisible ? `rotate(${el.angle}rad)` : "none",
|
||||||
pointerEvents: isActive
|
pointerEvents: isActive
|
||||||
? POINTER_EVENTS.enabled
|
? POINTER_EVENTS.enabled
|
||||||
@ -1093,7 +1096,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
<div
|
<div
|
||||||
className="excalidraw__embeddable__outer"
|
className="excalidraw__embeddable__outer"
|
||||||
style={{
|
style={{
|
||||||
padding: `${el.strokeWidth}px`,
|
padding: `${el.strokeWidth / el.scale[0]}px`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(isEmbeddableElement(el)
|
{(isEmbeddableElement(el)
|
||||||
|
@ -294,11 +294,15 @@ const restoreElement = (
|
|||||||
case "ellipse":
|
case "ellipse":
|
||||||
case "rectangle":
|
case "rectangle":
|
||||||
case "diamond":
|
case "diamond":
|
||||||
case "iframe":
|
|
||||||
return restoreElementWithProperties(element, {});
|
return restoreElementWithProperties(element, {});
|
||||||
|
case "iframe":
|
||||||
|
return restoreElementWithProperties(element, {
|
||||||
|
scale: element.scale ?? [1, 1],
|
||||||
|
});
|
||||||
case "embeddable":
|
case "embeddable":
|
||||||
return restoreElementWithProperties(element, {
|
return restoreElementWithProperties(element, {
|
||||||
validated: null,
|
validated: null,
|
||||||
|
scale: element.scale ?? [1, 1],
|
||||||
});
|
});
|
||||||
case "magicframe":
|
case "magicframe":
|
||||||
case "frame":
|
case "frame":
|
||||||
|
@ -21,7 +21,7 @@ import {
|
|||||||
const embeddedLinkCache = new Map<string, IframeData>();
|
const embeddedLinkCache = new Map<string, IframeData>();
|
||||||
|
|
||||||
const RE_YOUTUBE =
|
const RE_YOUTUBE =
|
||||||
/^(?:http(?:s)?:\/\/)?(?:www\.)?youtu(?:be\.com|\.be)\/(embed\/|watch\?v=|shorts\/|playlist\?list=|embed\/videoseries\?list=)?([a-zA-Z0-9_-]+)(?:\?t=|&t=|\?start=|&start=)?([a-zA-Z0-9_-]+)?[^\s]*$/;
|
/^(?:http(?:s)?:\/\/)?(?:www\.)?youtu(?:be\.com|\.be)\/(embed\/|watch\?v=|shorts\/|playlist\?list=|embed\/videoseries\?list=)?([a-zA-Z0-9_-]+)(?:\?t=|.*&t=|\?start=|.*&start=)?([a-zA-Z0-9_-]+)?[^\s]*$/;
|
||||||
|
|
||||||
const RE_VIMEO =
|
const RE_VIMEO =
|
||||||
/^(?:http(?:s)?:\/\/)?(?:(?:w){3}.)?(?:player\.)?vimeo\.com\/(?:video\/)?([^?\s]+)(?:\?.*)?$/;
|
/^(?:http(?:s)?:\/\/)?(?:(?:w){3}.)?(?:player\.)?vimeo\.com\/(?:video\/)?([^?\s]+)(?:\?.*)?$/;
|
||||||
|
@ -142,6 +142,7 @@ export const newEmbeddableElement = (
|
|||||||
return {
|
return {
|
||||||
..._newElementBase<ExcalidrawEmbeddableElement>("embeddable", opts),
|
..._newElementBase<ExcalidrawEmbeddableElement>("embeddable", opts),
|
||||||
validated: opts.validated,
|
validated: opts.validated,
|
||||||
|
scale: [1, 1],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -152,6 +153,7 @@ export const newIframeElement = (
|
|||||||
): NonDeleted<ExcalidrawIframeElement> => {
|
): NonDeleted<ExcalidrawIframeElement> => {
|
||||||
return {
|
return {
|
||||||
..._newElementBase<ExcalidrawIframeElement>("iframe", opts),
|
..._newElementBase<ExcalidrawIframeElement>("iframe", opts),
|
||||||
|
scale: [1, 1],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
isArrowElement,
|
isArrowElement,
|
||||||
isBoundToContainer,
|
isBoundToContainer,
|
||||||
|
isIframeLikeElement,
|
||||||
isFrameLikeElement,
|
isFrameLikeElement,
|
||||||
isFreeDrawElement,
|
isFreeDrawElement,
|
||||||
isImageElement,
|
isImageElement,
|
||||||
@ -586,15 +587,31 @@ export const resizeSingleElement = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
if ("scale" in element && "scale" in stateAtResizeStart) {
|
if ("scale" in element && "scale" in stateAtResizeStart) {
|
||||||
mutateElement(element, {
|
if (isIframeLikeElement(element)) {
|
||||||
scale: [
|
if (shouldMaintainAspectRatio) {
|
||||||
// defaulting because scaleX/Y can be 0/-0
|
const scale: [number, number] = [
|
||||||
(Math.sign(newBoundsX2 - stateAtResizeStart.x) ||
|
Math.abs(
|
||||||
stateAtResizeStart.scale[0]) * stateAtResizeStart.scale[0],
|
eleNewWidth /
|
||||||
(Math.sign(newBoundsY2 - stateAtResizeStart.y) ||
|
(stateAtResizeStart.width / stateAtResizeStart.scale[0]),
|
||||||
stateAtResizeStart.scale[1]) * stateAtResizeStart.scale[1],
|
),
|
||||||
],
|
Math.abs(
|
||||||
});
|
eleNewHeight /
|
||||||
|
(stateAtResizeStart.height / stateAtResizeStart.scale[1]),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
mutateElement(element, { scale });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mutateElement(element, {
|
||||||
|
scale: [
|
||||||
|
// defaulting because scaleX/Y can be 0/-0
|
||||||
|
(Math.sign(newBoundsX2 - stateAtResizeStart.x) ||
|
||||||
|
stateAtResizeStart.scale[0]) * stateAtResizeStart.scale[0],
|
||||||
|
(Math.sign(newBoundsY2 - stateAtResizeStart.y) ||
|
||||||
|
stateAtResizeStart.scale[1]) * stateAtResizeStart.scale[1],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -96,6 +96,7 @@ export type ExcalidrawEmbeddableElement = _ExcalidrawElementBase &
|
|||||||
* may not have access to host-app supplied url validator during restore.
|
* may not have access to host-app supplied url validator during restore.
|
||||||
*/
|
*/
|
||||||
validated: boolean | null;
|
validated: boolean | null;
|
||||||
|
scale: [number, number];
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type ExcalidrawIframeElement = _ExcalidrawElementBase &
|
export type ExcalidrawIframeElement = _ExcalidrawElementBase &
|
||||||
@ -103,6 +104,7 @@ export type ExcalidrawIframeElement = _ExcalidrawElementBase &
|
|||||||
type: "iframe";
|
type: "iframe";
|
||||||
// TODO move later to AI-specific frame
|
// TODO move later to AI-specific frame
|
||||||
customData?: { generationData?: MagicCacheData };
|
customData?: { generationData?: MagicCacheData };
|
||||||
|
scale: [number, number];
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type ExcalidrawIframeLikeElement =
|
export type ExcalidrawIframeLikeElement =
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
isInitializedImageElement,
|
isInitializedImageElement,
|
||||||
isArrowElement,
|
isArrowElement,
|
||||||
hasBoundTextElement,
|
hasBoundTextElement,
|
||||||
|
isIframeLikeElement,
|
||||||
isMagicFrameElement,
|
isMagicFrameElement,
|
||||||
} from "../element/typeChecks";
|
} from "../element/typeChecks";
|
||||||
import { getElementAbsoluteCoords } from "../element/bounds";
|
import { getElementAbsoluteCoords } from "../element/bounds";
|
||||||
@ -520,7 +521,8 @@ const drawElementFromCanvas = (
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
"scale" in elementWithCanvas.element &&
|
"scale" in elementWithCanvas.element &&
|
||||||
!isPendingImageElement(element, renderConfig)
|
!isPendingImageElement(element, renderConfig) &&
|
||||||
|
!isIframeLikeElement(element)
|
||||||
) {
|
) {
|
||||||
context.scale(
|
context.scale(
|
||||||
elementWithCanvas.element.scale[0],
|
elementWithCanvas.element.scale[0],
|
||||||
@ -996,6 +998,8 @@ export const renderElementToSvg = (
|
|||||||
renderConfig,
|
renderConfig,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const scaleX = element.scale?.[0] || 1;
|
||||||
|
const scaleY = element.scale?.[1] || 1;
|
||||||
// render embeddable element + iframe
|
// render embeddable element + iframe
|
||||||
const embeddableNode = roughSVGDrawWithPrecision(
|
const embeddableNode = roughSVGDrawWithPrecision(
|
||||||
rsvg,
|
rsvg,
|
||||||
@ -1007,7 +1011,7 @@ export const renderElementToSvg = (
|
|||||||
"transform",
|
"transform",
|
||||||
`translate(${offsetX || 0} ${
|
`translate(${offsetX || 0} ${
|
||||||
offsetY || 0
|
offsetY || 0
|
||||||
}) rotate(${degree} ${cx} ${cy})`,
|
}) rotate(${degree} ${cx} ${cy}) scale(${scaleX}, ${scaleY})`,
|
||||||
);
|
);
|
||||||
while (embeddableNode.firstChild) {
|
while (embeddableNode.firstChild) {
|
||||||
embeddableNode.removeChild(embeddableNode.firstChild);
|
embeddableNode.removeChild(embeddableNode.firstChild);
|
||||||
@ -1038,8 +1042,8 @@ export const renderElementToSvg = (
|
|||||||
SVG_NS,
|
SVG_NS,
|
||||||
"foreignObject",
|
"foreignObject",
|
||||||
);
|
);
|
||||||
foreignObject.style.width = `${element.width}px`;
|
foreignObject.style.width = `${element.width / scaleX}px`;
|
||||||
foreignObject.style.height = `${element.height}px`;
|
foreignObject.style.height = `${element.height / scaleY}px`;
|
||||||
foreignObject.style.border = "none";
|
foreignObject.style.border = "none";
|
||||||
const div = foreignObject.ownerDocument!.createElementNS(SVG_NS, "div");
|
const div = foreignObject.ownerDocument!.createElementNS(SVG_NS, "div");
|
||||||
div.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
div.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
||||||
|
1
src/tests/fixtures/elementFixture.ts
vendored
1
src/tests/fixtures/elementFixture.ts
vendored
@ -35,6 +35,7 @@ export const embeddableFixture: ExcalidrawElement = {
|
|||||||
...elementBase,
|
...elementBase,
|
||||||
type: "embeddable",
|
type: "embeddable",
|
||||||
validated: null,
|
validated: null,
|
||||||
|
scale: [1, 1],
|
||||||
};
|
};
|
||||||
export const ellipseFixture: ExcalidrawElement = {
|
export const ellipseFixture: ExcalidrawElement = {
|
||||||
...elementBase,
|
...elementBase,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user