Compare commits

...

8 Commits

Author SHA1 Message Date
zsviczian
44390cb146 suport iFrame 2023-11-25 05:43:25 +00:00
zsviczian
9da3e47877
Merge branch 'master' into zsviczian-embeddable-scaling 2023-11-25 06:15:24 +01:00
zsviczian
9f6edd8eaa lint 2023-11-17 18:50:26 +00:00
zsviczian
0d60253de7 fix border sizing 2023-11-17 18:47:01 +00:00
zsviczian
4027a5b245 don't flip video when negative scaling 2023-11-17 06:43:23 +00:00
zsviczian
49f2c88978 adjusted renderElement 2023-11-16 22:15:53 +00:00
zsviczian
156b8b422b fix - restore 2023-11-16 20:10:22 +00:00
zsviczian
e2982a2968 scale embeddable and fix youtube start bug 2023-11-16 18:27:28 +00:00
8 changed files with 56 additions and 23 deletions

View File

@ -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)

View File

@ -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":

View File

@ -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]+)(?:\?.*)?$/;

View File

@ -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],
}; };
}; };

View File

@ -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 (

View File

@ -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 =

View File

@ -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");

View File

@ -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,