support export canvas to clipboard (#232)

pull/291/head
David Luzar 5 years ago committed by GitHub
parent 1541428ab1
commit deee57314d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,17 +1,35 @@
import React from "react"; import React from "react";
import { EditableText } from "../EditableText"; import { EditableText } from "../EditableText";
import { Panel } from "../Panel"; import { Panel } from "../Panel";
import { ExportType } from "../../scene/types";
import "./panelExport.scss";
interface PanelExportProps { interface PanelExportProps {
projectName: string; projectName: string;
onProjectNameChange: (name: string) => void; onProjectNameChange: (name: string) => void;
onExportAsPNG: React.MouseEventHandler; onExportCanvas: (type: ExportType) => void;
exportBackground: boolean; exportBackground: boolean;
onExportBackgroundChange: (val: boolean) => void; onExportBackgroundChange: (val: boolean) => void;
onSaveScene: React.MouseEventHandler; onSaveScene: React.MouseEventHandler;
onLoadScene: React.MouseEventHandler; onLoadScene: React.MouseEventHandler;
} }
// fa-clipboard
const ClipboardIcon = () => (
<svg viewBox="0 0 384 512">
<path
fill="currentColor"
d="M384 112v352c0 26.51-21.49 48-48 48H48c-26.51 0-48-21.49-48-48V112c0-26.51 21.49-48 48-48h80c0-35.29 28.71-64 64-64s64 28.71 64 64h80c26.51 0 48 21.49 48 48zM192 40c-13.255 0-24 10.745-24 24s10.745 24 24 24 24-10.745 24-24-10.745-24-24-24m96 114v-20a6 6 0 0 0-6-6H102a6 6 0 0 0-6 6v20a6 6 0 0 0 6 6h180a6 6 0 0 0 6-6z"
></path>
</svg>
);
const probablySupportsClipboard =
"toBlob" in HTMLCanvasElement.prototype &&
"write" in navigator.clipboard &&
"ClipboardItem" in window;
export const PanelExport: React.FC<PanelExportProps> = ({ export const PanelExport: React.FC<PanelExportProps> = ({
projectName, projectName,
exportBackground, exportBackground,
@ -19,7 +37,7 @@ export const PanelExport: React.FC<PanelExportProps> = ({
onExportBackgroundChange, onExportBackgroundChange,
onSaveScene, onSaveScene,
onLoadScene, onLoadScene,
onExportAsPNG onExportCanvas
}) => { }) => {
return ( return (
<Panel title="Export"> <Panel title="Export">
@ -32,7 +50,23 @@ export const PanelExport: React.FC<PanelExportProps> = ({
/> />
)} )}
<h5>Image</h5> <h5>Image</h5>
<button onClick={onExportAsPNG}>Export to png</button> <div className="panelExport-imageButtons">
<button
className="panelExport-exportToPngButton"
onClick={() => onExportCanvas("png")}
>
Export to PNG
</button>
{probablySupportsClipboard && (
<button
className="panelExport-exportToClipboardButton"
onClick={() => onExportCanvas("clipboard")}
title="Copy to clipboard (experimental)"
>
<ClipboardIcon />
</button>
)}
</div>
<label> <label>
<input <input
type="checkbox" type="checkbox"

@ -0,0 +1,16 @@
.panelExport-imageButtons {
display: flex;
}
.panelExport-exportToPngButton {
flex: 1 1 auto;
}
.panelExport-exportToClipboardButton {
margin-left: 10px;
padding: 0 15px;
svg {
width: 15px;
}
}

7
src/global.d.ts vendored

@ -0,0 +1,7 @@
interface Window {
ClipboardItem: any;
}
interface Clipboard extends EventTarget {
write(data: any[]): Promise<void>;
}

@ -23,7 +23,7 @@ import {
getSelectedAttribute, getSelectedAttribute,
loadFromJSON, loadFromJSON,
saveAsJSON, saveAsJSON,
exportAsPNG, exportCanvas,
restoreFromLocalStorage, restoreFromLocalStorage,
saveToLocalStorage, saveToLocalStorage,
hasBackground, hasBackground,
@ -37,6 +37,7 @@ import {
import { renderScene } from "./renderer"; import { renderScene } from "./renderer";
import { AppState } from "./types"; import { AppState } from "./types";
import { ExcalidrawElement, ExcalidrawTextElement } from "./element/types"; import { ExcalidrawElement, ExcalidrawTextElement } from "./element/types";
import { ExportType } from "./scene/types";
import { getDateTime, isInputLike, measureText } from "./utils"; import { getDateTime, isInputLike, measureText } from "./utils";
import { KEYS, META_KEY, isArrowKey } from "./keys"; import { KEYS, META_KEY, isArrowKey } from "./keys";
@ -170,9 +171,9 @@ export class App extends React.Component<{}, AppState> {
if (event.key === KEYS.ESCAPE) { if (event.key === KEYS.ESCAPE) {
elements = clearSelection(elements); elements = clearSelection(elements);
this.forceUpdate(); this.forceUpdate();
this.setState({ elementType: 'selection' }); this.setState({ elementType: "selection" });
if (window.document.activeElement instanceof HTMLElement) { if (window.document.activeElement instanceof HTMLElement) {
window.document.activeElement.blur() window.document.activeElement.blur();
} }
event.preventDefault(); event.preventDefault();
return; return;
@ -614,8 +615,8 @@ export class App extends React.Component<{}, AppState> {
<PanelExport <PanelExport
projectName={this.state.name} projectName={this.state.name}
onProjectNameChange={this.updateProjectName} onProjectNameChange={this.updateProjectName}
onExportAsPNG={() => onExportCanvas={(type: ExportType) =>
exportAsPNG(elements, this.canvas!, this.state) exportCanvas(type, elements, this.canvas!, this.state)
} }
exportBackground={this.state.exportBackground} exportBackground={this.state.exportBackground}
onExportBackgroundChange={val => onExportBackgroundChange={val =>

@ -6,6 +6,7 @@ import { getElementAbsoluteCoords } from "../element";
import { renderScene } from "../renderer"; import { renderScene } from "../renderer";
import { AppState } from "../types"; import { AppState } from "../types";
import { ExportType } from "./types";
import nanoid from "nanoid"; import nanoid from "nanoid";
const LOCAL_STORAGE_KEY = "excalidraw"; const LOCAL_STORAGE_KEY = "excalidraw";
@ -76,7 +77,8 @@ export function loadFromJSON() {
}); });
} }
export function exportAsPNG( export function exportCanvas(
type: ExportType,
elements: readonly ExcalidrawElement[], elements: readonly ExcalidrawElement[],
canvas: HTMLCanvasElement, canvas: HTMLCanvasElement,
{ {
@ -136,7 +138,23 @@ export function exportAsPNG(
} }
); );
saveFile(`${name}.png`, tempCanvas.toDataURL("image/png")); if (type === "png") {
saveFile(`${name}.png`, tempCanvas.toDataURL("image/png"));
} else if (type === "clipboard") {
try {
tempCanvas.toBlob(async function(blob) {
try {
await navigator.clipboard.write([
new window.ClipboardItem({ "image/png": blob })
]);
} catch (err) {
window.alert("Couldn't copy to clipboard. Try using Chrome browser.");
}
});
} catch (err) {
window.alert("Couldn't copy to clipboard. Try using Chrome browser.");
}
}
// clean up the DOM // clean up the DOM
if (tempCanvas !== canvas) tempCanvas.remove(); if (tempCanvas !== canvas) tempCanvas.remove();

@ -8,7 +8,7 @@ export {
getSelectedAttribute getSelectedAttribute
} from "./selection"; } from "./selection";
export { export {
exportAsPNG, exportCanvas,
loadFromJSON, loadFromJSON,
saveAsJSON, saveAsJSON,
restoreFromLocalStorage, restoreFromLocalStorage,

@ -15,3 +15,5 @@ export type SceneScroll = {
export interface Scene { export interface Scene {
elements: ExcalidrawTextElement[]; elements: ExcalidrawTextElement[];
} }
export type ExportType = "png" | "clipboard";

Loading…
Cancel
Save