Add an 'Export PNG' button (#63)

This fixes #62.
pull/66/head
Atul Varma 2021-03-29 07:22:09 -04:00 zatwierdzone przez GitHub
rodzic 85533fa111
commit 3090d9fd89
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
3 zmienionych plików z 65 dodań i 20 usunięć

Wyświetl plik

@ -9,33 +9,78 @@ function getSvgMarkup(el: SVGSVGElement): string {
].join("\n");
}
type ImageExporter = (svgEl: SVGSVGElement) => Promise<string>;
/**
* 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(
filename: string,
svgRef: React.RefObject<SVGSVGElement>
async function exportImage(
svgRef: React.RefObject<SVGSVGElement>,
basename: string,
ext: string,
exporter: ImageExporter
) {
const svgEl = svgRef.current;
if (!svgEl) {
alert("Oops, an error occurred! Please try again later.");
return;
}
const dataURL = `data:image/svg+xml;utf8,${encodeURIComponent(
getSvgMarkup(svgEl)
)}`;
const url = await exporter(svgEl);
const anchor = document.createElement("a");
anchor.href = dataURL;
anchor.download = filename;
anchor.href = url;
anchor.download = `${basename}.${ext}`;
document.body.append(anchor);
anchor.click();
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>;
filename: string;
}> = ({ svgRef, filename }) => (
<button onClick={() => exportSvg(filename, svgRef)}>Export SVG</button>
basename: string;
}> = ({ svgRef, basename }) => (
<>
<button onClick={() => exportImage(svgRef, basename, "svg", exportSvg)}>
Export SVG
</button>{" "}
<button onClick={() => exportImage(svgRef, basename, "png", exportPng)}>
Export PNG
</button>
</>
);

Wyświetl plik

@ -11,7 +11,7 @@ import { SymbolContextWidget } from "../symbol-context-widget";
import { range } from "../util";
import { AutoSizingSvg } from "../auto-sizing-svg";
import { ExportSvgButton } from "../export-svg";
import { ExportWidget } from "../export-svg";
import {
CreatureContext,
CreatureContextType,
@ -167,8 +167,8 @@ const MAX_COMPLEXITY_LEVEL = COMPLEXITY_LEVEL_GENERATORS.length - 1;
const INITIAL_COMPLEXITY_LEVEL = 2;
function getDownloadFilename(randomSeed: number) {
return `mystic-symbolic-creature-${randomSeed}.svg`;
function getDownloadBasename(randomSeed: number) {
return `mystic-symbolic-creature-${randomSeed}`;
}
export const CreaturePage: React.FC<{}> = () => {
@ -221,8 +221,8 @@ export const CreaturePage: React.FC<{}> = () => {
<u>R</u>andomize!
</button>{" "}
<button onClick={() => window.location.reload()}>Reset</button>{" "}
<ExportSvgButton
filename={getDownloadFilename(randomSeed)}
<ExportWidget
basename={getDownloadBasename(randomSeed)}
svgRef={svgRef}
/>
</div>

Wyświetl plik

@ -3,7 +3,7 @@ import { AutoSizingSvg } from "../auto-sizing-svg";
import { getBoundingBoxCenter } from "../bounding-box";
import { ColorWidget } from "../color-widget";
import { DEFAULT_BG_COLOR } from "../colors";
import { ExportSvgButton } from "../export-svg";
import { ExportWidget } from "../export-svg";
import { HoverDebugHelper } from "../hover-debug-helper";
import { NumericSlider } from "../numeric-slider";
import {
@ -307,7 +307,7 @@ export const MandalaPage: React.FC<{}> = () => {
<button accessKey="r" onClick={randomize}>
<u>R</u>andomize!
</button>{" "}
<ExportSvgButton filename="mandala.svg" svgRef={svgRef} />
<ExportWidget basename="mandala" svgRef={svgRef} />
</div>
<HoverDebugHelper>
<AutoSizingSvg padding={20} ref={svgRef} bgColor={bgColor}>