rodzic
85533fa111
commit
3090d9fd89
|
@ -9,33 +9,78 @@ function getSvgMarkup(el: SVGSVGElement): string {
|
||||||
].join("\n");
|
].join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ImageExporter = (svgEl: SVGSVGElement) => Promise<string>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiates a download on the user's browser which downloads the given
|
* Initiates a download on the user's browser which downloads the given
|
||||||
* SVG element under the given filename.
|
* SVG element under the given filename, using the given export algorithm.
|
||||||
*/
|
*/
|
||||||
export function exportSvg(
|
async function exportImage(
|
||||||
filename: string,
|
svgRef: React.RefObject<SVGSVGElement>,
|
||||||
svgRef: React.RefObject<SVGSVGElement>
|
basename: string,
|
||||||
|
ext: string,
|
||||||
|
exporter: ImageExporter
|
||||||
) {
|
) {
|
||||||
const svgEl = svgRef.current;
|
const svgEl = svgRef.current;
|
||||||
if (!svgEl) {
|
if (!svgEl) {
|
||||||
alert("Oops, an error occurred! Please try again later.");
|
alert("Oops, an error occurred! Please try again later.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const dataURL = `data:image/svg+xml;utf8,${encodeURIComponent(
|
const url = await exporter(svgEl);
|
||||||
getSvgMarkup(svgEl)
|
|
||||||
)}`;
|
|
||||||
const anchor = document.createElement("a");
|
const anchor = document.createElement("a");
|
||||||
anchor.href = dataURL;
|
anchor.href = url;
|
||||||
anchor.download = filename;
|
anchor.download = `${basename}.${ext}`;
|
||||||
document.body.append(anchor);
|
document.body.append(anchor);
|
||||||
anchor.click();
|
anchor.click();
|
||||||
document.body.removeChild(anchor);
|
document.body.removeChild(anchor);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ExportSvgButton: React.FC<{
|
function getCanvasContext2D(
|
||||||
|
canvas: HTMLCanvasElement
|
||||||
|
): CanvasRenderingContext2D {
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
if (!ctx) throw new Error(`Unable to get 2D context for canvas!`);
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports the given SVG as an SVG in a data URL.
|
||||||
|
*/
|
||||||
|
const exportSvg: ImageExporter = async (svgEl) =>
|
||||||
|
`data:image/svg+xml;utf8,${encodeURIComponent(getSvgMarkup(svgEl))}`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports the given SVG as a PNG in a data URL.
|
||||||
|
*/
|
||||||
|
const exportPng: ImageExporter = async (svgEl) => {
|
||||||
|
const dataURL = await exportSvg(svgEl);
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
const img = document.createElement("img");
|
||||||
|
|
||||||
|
img.onload = () => {
|
||||||
|
canvas.width = img.width;
|
||||||
|
canvas.height = img.height;
|
||||||
|
const ctx = getCanvasContext2D(canvas);
|
||||||
|
ctx.drawImage(img, 0, 0);
|
||||||
|
resolve(canvas.toDataURL());
|
||||||
|
};
|
||||||
|
img.onerror = reject;
|
||||||
|
img.src = dataURL;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ExportWidget: React.FC<{
|
||||||
svgRef: React.RefObject<SVGSVGElement>;
|
svgRef: React.RefObject<SVGSVGElement>;
|
||||||
filename: string;
|
basename: string;
|
||||||
}> = ({ svgRef, filename }) => (
|
}> = ({ svgRef, basename }) => (
|
||||||
<button onClick={() => exportSvg(filename, svgRef)}>Export SVG</button>
|
<>
|
||||||
|
<button onClick={() => exportImage(svgRef, basename, "svg", exportSvg)}>
|
||||||
|
Export SVG
|
||||||
|
</button>{" "}
|
||||||
|
<button onClick={() => exportImage(svgRef, basename, "png", exportPng)}>
|
||||||
|
Export PNG
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { SymbolContextWidget } from "../symbol-context-widget";
|
||||||
import { range } from "../util";
|
import { range } from "../util";
|
||||||
|
|
||||||
import { AutoSizingSvg } from "../auto-sizing-svg";
|
import { AutoSizingSvg } from "../auto-sizing-svg";
|
||||||
import { ExportSvgButton } from "../export-svg";
|
import { ExportWidget } from "../export-svg";
|
||||||
import {
|
import {
|
||||||
CreatureContext,
|
CreatureContext,
|
||||||
CreatureContextType,
|
CreatureContextType,
|
||||||
|
@ -167,8 +167,8 @@ const MAX_COMPLEXITY_LEVEL = COMPLEXITY_LEVEL_GENERATORS.length - 1;
|
||||||
|
|
||||||
const INITIAL_COMPLEXITY_LEVEL = 2;
|
const INITIAL_COMPLEXITY_LEVEL = 2;
|
||||||
|
|
||||||
function getDownloadFilename(randomSeed: number) {
|
function getDownloadBasename(randomSeed: number) {
|
||||||
return `mystic-symbolic-creature-${randomSeed}.svg`;
|
return `mystic-symbolic-creature-${randomSeed}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CreaturePage: React.FC<{}> = () => {
|
export const CreaturePage: React.FC<{}> = () => {
|
||||||
|
@ -221,8 +221,8 @@ export const CreaturePage: React.FC<{}> = () => {
|
||||||
<u>R</u>andomize!
|
<u>R</u>andomize!
|
||||||
</button>{" "}
|
</button>{" "}
|
||||||
<button onClick={() => window.location.reload()}>Reset</button>{" "}
|
<button onClick={() => window.location.reload()}>Reset</button>{" "}
|
||||||
<ExportSvgButton
|
<ExportWidget
|
||||||
filename={getDownloadFilename(randomSeed)}
|
basename={getDownloadBasename(randomSeed)}
|
||||||
svgRef={svgRef}
|
svgRef={svgRef}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { AutoSizingSvg } from "../auto-sizing-svg";
|
||||||
import { getBoundingBoxCenter } from "../bounding-box";
|
import { getBoundingBoxCenter } from "../bounding-box";
|
||||||
import { ColorWidget } from "../color-widget";
|
import { ColorWidget } from "../color-widget";
|
||||||
import { DEFAULT_BG_COLOR } from "../colors";
|
import { DEFAULT_BG_COLOR } from "../colors";
|
||||||
import { ExportSvgButton } from "../export-svg";
|
import { ExportWidget } from "../export-svg";
|
||||||
import { HoverDebugHelper } from "../hover-debug-helper";
|
import { HoverDebugHelper } from "../hover-debug-helper";
|
||||||
import { NumericSlider } from "../numeric-slider";
|
import { NumericSlider } from "../numeric-slider";
|
||||||
import {
|
import {
|
||||||
|
@ -307,7 +307,7 @@ export const MandalaPage: React.FC<{}> = () => {
|
||||||
<button accessKey="r" onClick={randomize}>
|
<button accessKey="r" onClick={randomize}>
|
||||||
<u>R</u>andomize!
|
<u>R</u>andomize!
|
||||||
</button>{" "}
|
</button>{" "}
|
||||||
<ExportSvgButton filename="mandala.svg" svgRef={svgRef} />
|
<ExportWidget basename="mandala" svgRef={svgRef} />
|
||||||
</div>
|
</div>
|
||||||
<HoverDebugHelper>
|
<HoverDebugHelper>
|
||||||
<AutoSizingSvg padding={20} ref={svgRef} bgColor={bgColor}>
|
<AutoSizingSvg padding={20} ref={svgRef} bgColor={bgColor}>
|
||||||
|
|
Ładowanie…
Reference in New Issue