mirror of
https://github.com/stan-smith/FossFLOW.git
synced 2026-04-23 08:31:16 -04:00
feat: transparent background for exporting as png (#180) @F4tal1t thank you for contributing as always!
Allows for exporting diagrams with a transparent background.
This commit is contained in:
6
packages/fossflow-lib/dist/index.js
vendored
6
packages/fossflow-lib/dist/index.js
vendored
File diff suppressed because one or more lines are too long
@@ -14,4 +14,4 @@ export declare const transformToCompactFormat: (model: Model) => {
|
||||
export declare const transformFromCompactFormat: (compactModel: any) => Model;
|
||||
export declare const exportAsJSON: (model: Model) => void;
|
||||
export declare const exportAsCompactJSON: (model: Model) => void;
|
||||
export declare const exportAsImage: (el: HTMLDivElement, size?: Size, scale?: number) => Promise<string>;
|
||||
export declare const exportAsImage: (el: HTMLDivElement, size?: Size, scale?: number, bgcolor?: string) => Promise<string>;
|
||||
|
||||
@@ -94,6 +94,12 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
|
||||
});
|
||||
}, [uiStateActions]);
|
||||
|
||||
const [transparentBackground, setTransparentBackground] = useState(false);
|
||||
|
||||
const [backgroundColor, setBackgroundColor] = useState<string>(
|
||||
customVars.customPalette.diagramBg
|
||||
);
|
||||
|
||||
const exportImage = useCallback(() => {
|
||||
if (!containerRef.current || isExporting.current) {
|
||||
return;
|
||||
@@ -107,7 +113,7 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
|
||||
height: bounds.height
|
||||
};
|
||||
|
||||
exportAsImage(containerRef.current as HTMLDivElement, containerSize, exportScale)
|
||||
exportAsImage(containerRef.current as HTMLDivElement, containerSize, exportScale, transparentBackground ? 'transparent' : backgroundColor)
|
||||
.then((data) => {
|
||||
setImageData(data);
|
||||
isExporting.current = false;
|
||||
@@ -117,7 +123,7 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
|
||||
setExportError(true);
|
||||
isExporting.current = false;
|
||||
});
|
||||
}, [bounds, exportScale]);
|
||||
}, [bounds, exportScale, transparentBackground, backgroundColor]);
|
||||
|
||||
// Crop the image based on selected area
|
||||
const cropImage = useCallback((cropArea: CropArea, sourceImage: string) => {
|
||||
@@ -248,6 +254,17 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
|
||||
// Clear canvas
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Draw checkerboard if transparent background
|
||||
if (transparentBackground) {
|
||||
const squareSize = 10;
|
||||
for (let y = 0; y < canvas.height; y += squareSize) {
|
||||
for (let x = 0; x < canvas.width; x += squareSize) {
|
||||
ctx.fillStyle = (x / squareSize + y / squareSize) % 2 === 0 ? '#f0f0f0' : 'transparent';
|
||||
ctx.fillRect(x, y, squareSize, squareSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the image scaled to fit canvas
|
||||
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||
|
||||
@@ -306,7 +323,7 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
|
||||
};
|
||||
|
||||
img.src = imageData;
|
||||
}, [imageData, isInCropMode, cropArea]);
|
||||
}, [imageData, isInCropMode, cropArea, transparentBackground]);
|
||||
|
||||
const [showGrid, setShowGrid] = useState(false);
|
||||
const handleShowGridChange = (checked: boolean) => {
|
||||
@@ -318,9 +335,15 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
|
||||
setExpandLabels(checked);
|
||||
};
|
||||
|
||||
const [backgroundColor, setBackgroundColor] = useState<string>(
|
||||
customVars.customPalette.diagramBg
|
||||
);
|
||||
const handleTransparentBackgroundChange = (checked: boolean) => {
|
||||
setTransparentBackground(checked);
|
||||
if (checked) {
|
||||
setBackgroundColor('transparent');
|
||||
} else {
|
||||
setBackgroundColor(customVars.customPalette.diagramBg);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBackgroundColorChange = (color: string) => {
|
||||
setBackgroundColor(color);
|
||||
};
|
||||
@@ -365,7 +388,7 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
|
||||
}, 200);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [showGrid, backgroundColor, expandLabels, exportImage, cropToContent, exportScale]);
|
||||
}, [showGrid, backgroundColor, expandLabels, exportImage, cropToContent, exportScale, transparentBackground]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!imageData) {
|
||||
@@ -491,7 +514,10 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
|
||||
sx={{
|
||||
maxWidth: '100%',
|
||||
maxHeight: '300px',
|
||||
objectFit: 'contain'
|
||||
objectFit: 'contain',
|
||||
backgroundImage: transparentBackground ? 'linear-gradient(45deg, #f0f0f0 25%, transparent 25%), linear-gradient(-45deg, #f0f0f0 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #f0f0f0 75%), linear-gradient(-45deg, transparent 75%, #f0f0f0 75%)' : undefined,
|
||||
backgroundSize: transparentBackground ? '20px 20px' : undefined,
|
||||
backgroundPosition: transparentBackground ? '0 0, 0 10px, 10px -10px, -10px 0px' : undefined
|
||||
}}
|
||||
src={displayImage}
|
||||
alt="preview"
|
||||
@@ -547,6 +573,20 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
|
||||
<ColorPicker
|
||||
value={backgroundColor}
|
||||
onChange={handleBackgroundColorChange}
|
||||
disabled={transparentBackground}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<FormControlLabel
|
||||
label="Transparent background"
|
||||
control={
|
||||
<Checkbox
|
||||
size="small"
|
||||
checked={transparentBackground}
|
||||
onChange={(event) => {
|
||||
handleTransparentBackgroundChange(event.target.checked);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -53,9 +53,7 @@ export const Renderer = ({ showGrid, backgroundColor }: RendererProps) => {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
zIndex: 0,
|
||||
bgcolor: (theme) => {
|
||||
return backgroundColor ?? theme.customVars.customPalette.diagramBg;
|
||||
}
|
||||
bgcolor: (theme) => backgroundColor === 'transparent' ? 'transparent' : (backgroundColor ?? theme.customVars.customPalette.diagramBg)
|
||||
}}
|
||||
>
|
||||
<SceneLayer>
|
||||
|
||||
@@ -178,7 +178,8 @@ export const exportAsCompactJSON = (model: Model) => {
|
||||
export const exportAsImage = async (
|
||||
el: HTMLDivElement,
|
||||
size?: Size,
|
||||
scale: number = 1
|
||||
scale: number = 1,
|
||||
bgcolor: string = '#ffffff'
|
||||
) => {
|
||||
// Calculate scaled dimensions
|
||||
const width = size ? size.width * scale : el.clientWidth * scale;
|
||||
@@ -189,7 +190,7 @@ export const exportAsImage = async (
|
||||
width,
|
||||
height,
|
||||
cacheBust: true,
|
||||
bgcolor: '#ffffff',
|
||||
bgcolor,
|
||||
quality: 1.0,
|
||||
// Apply CSS transform for high-quality scaling
|
||||
style: scale !== 1 ? {
|
||||
@@ -208,7 +209,7 @@ export const exportAsImage = async (
|
||||
width,
|
||||
height,
|
||||
cacheBust: true,
|
||||
bgcolor: '#ffffff'
|
||||
bgcolor
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user