Jede hinreichend fortgeschrittene Technologie ist von Magie nicht mehr zu unterscheiden. Es sei denn, Sie verstehen es. Mein Name ist Thomas Steiner. Ich arbeite bei Google im Bereich Developer Relations. In diesem Beitrag zu meinem Google I/O-Vortrag stelle ich einige der neuen Fugu-APIs vor und zeige, wie sie die wichtigsten Nutzeraktionen in der Excalidraw-PWA verbessern. Sie können sich von diesen Ideen inspirieren lassen und sie auf Ihre eigenen Apps anwenden.
Wie ich zu Excalidraw gekommen bin
Ich möchte mit einer Geschichte beginnen. Am 1. Januar 2020 twitterte Christopher Chedeau, ein Softwareentwickler bei Facebook, über eine kleine Zeichen-App, an der er zu arbeiten begonnen hatte. Mit diesem Tool konnten Sie Kästen und Pfeile zeichnen, die cartoonartig und handgezeichnet wirkten. Am nächsten Tag konnten Sie auch Ellipsen und Text zeichnen sowie Objekte auswählen und verschieben. Am 3. Januar erhielt die App den Namen Excalidraw und wie bei jedem guten Nebenprojekt war der Kauf des Domainnamens eine der ersten Handlungen von Christopher. Sie konnten bereits Farben verwenden und die gesamte Zeichnung als PNG exportieren.
Am 15. Januar veröffentlichte Christopher einen Blogbeitrag, der auf Twitter viel Aufmerksamkeit erregte, auch bei mir. Der Beitrag begann mit einigen beeindruckenden Statistiken:
- 12.000 einzelne aktive Nutzer
- 1.500 Sterne auf GitHub
- 26 Beitragende
Für ein Projekt, das erst vor zwei Wochen gestartet wurde, ist das gar nicht schlecht. Was mein Interesse aber wirklich geweckt hat, war weiter unten im Beitrag. Christopher schrieb, dass er dieses Mal etwas Neues ausprobiert hat: Er hat allen, die einen Pull-Request eingereicht haben, uneingeschränkten Commit-Zugriff gewährt. Am selben Tag, an dem ich den Blogpost gelesen hatte, habe ich einen Pull-Request erstellt, mit dem die File System Access API in Excalidraw unterstützt wird. Damit wurde eine Funktionsanfrage behoben, die jemand gestellt hatte.
Meine Pull-Anfrage wurde einen Tag später zusammengeführt und von da an hatte ich vollen Commit-Zugriff. Ich habe meine Macht natürlich nicht missbraucht. Das gilt auch für alle anderen der bisher 149 Beitragenden.
Heute ist Excalidraw eine vollwertige installierbare Progressive Web-App mit Offlineunterstützung, einem beeindruckenden Dark Mode und der Möglichkeit, Dateien zu öffnen und zu speichern, dank der File System Access API.
Lipis erklärt, warum er so viel Zeit in Excalidraw investiert
Das war die Geschichte, wie ich zu Excalidraw gekommen bin. Bevor ich auf einige der tollen Funktionen von Excalidraw eingehe, möchte ich Panayiotis vorstellen. Panayiotis Lipiridis, im Internet einfach als lipis bekannt, ist der produktivste Mitwirkende bei Excalidraw. Ich habe lipis gefragt, was ihn motiviert, so viel Zeit in Excalidraw zu investieren:
Wie alle anderen habe ich durch Christophers Tweet von diesem Projekt erfahren. Mein erster Beitrag war das Hinzufügen der Open Color-Bibliothek, deren Farben bis heute Teil von Excalidraw sind. Als das Projekt wuchs und wir viele Anfragen erhielten, war mein nächster großer Beitrag die Entwicklung eines Backends zum Speichern von Zeichnungen, damit Nutzer sie teilen konnten. Was mich aber wirklich dazu motiviert, etwas beizutragen, ist, dass jeder, der Excalidraw ausprobiert hat, nach Ausreden sucht, es wieder zu verwenden.
Ich stimme lipis voll und ganz zu. Wer Excalidraw ausprobiert hat, sucht nach Gründen, es wieder zu verwenden.
Excalidraw in Aktion
Ich möchte Ihnen jetzt zeigen, wie Sie Excalidraw in der Praxis verwenden können. Ich bin kein großer Künstler, aber das Google I/O-Logo ist einfach genug. Ich versuche es mal. Ein Kästchen ist das „i“, eine Linie kann der Schrägstrich sein und das „o“ ist ein Kreis. Ich halte die Umschalttaste gedrückt, damit ich einen perfekten Kreis erhalte. Ich verschiebe den Schrägstrich ein wenig, damit es besser aussieht. Jetzt noch etwas Farbe für das „i“ und das „o“. Blau ist gut. Vielleicht ein anderer Füllstil? Voll oder schraffiert? Nein, die Schraffur sieht gut aus. Es ist nicht perfekt, aber das ist die Idee von Excalidraw. Ich speichere es trotzdem.
Ich klicke auf das Symbol zum Speichern und gebe im Dialogfeld zum Speichern von Dateien einen Dateinamen ein. In Chrome, einem Browser, der die File System Access API unterstützt, ist dies kein Download, sondern ein echter Speichervorgang. Ich kann den Speicherort und den Namen der Datei auswählen und Änderungen einfach in derselben Datei speichern.
Ich ändere das Logo und mache das „i“ rot. Wenn ich jetzt noch einmal auf „Speichern“ klicke, wird meine Änderung in derselben Datei wie zuvor gespeichert. Als Beweis lösche ich den Inhalt der Arbeitsfläche und öffne die Datei noch einmal. Wie Sie sehen, ist das modifizierte rot-blaue Logo wieder da.
Mit Dateien arbeiten
In Browsern, die die File System Access API derzeit nicht unterstützen, ist jeder Speichervorgang ein Download. Wenn ich also Änderungen vornehme, erhalte ich mehrere Dateien mit einer fortlaufenden Nummer im Dateinamen, die meinen Downloadordner füllen. Trotz dieses Nachteils kann ich die Datei aber trotzdem speichern.
Dateien öffnen
Was ist also das Geheimnis? Wie kann das Öffnen und Speichern in verschiedenen Browsern funktionieren, die die File System Access API möglicherweise unterstützen oder nicht? Das Öffnen einer Datei in Excalidraw erfolgt in einer Funktion namens loadFromJSON)(
, die wiederum eine Funktion namens fileOpen()
aufruft.
export const loadFromJSON = async (localAppState: AppState) => {
const blob = await fileOpen({
description: 'Excalidraw files',
extensions: ['.json', '.excalidraw', '.png', '.svg'],
mimeTypes: ['application/json', 'image/png', 'image/svg+xml'],
});
return loadFromBlob(blob, localAppState);
};
Die Funktion fileOpen()
stammt aus einer kleinen Bibliothek, die ich geschrieben habe und die browser-fs-access heißt. Wir verwenden sie in Excalidraw. Diese Bibliothek bietet Dateisystemzugriff über die File System Access API mit einem Legacy-Fallback, sodass sie in jedem Browser verwendet werden kann.
Zuerst zeige ich Ihnen die Implementierung für den Fall, dass die API unterstützt wird. Nachdem die akzeptierten MIME-Typen und Dateiendungen ausgehandelt wurden, wird die Funktion showOpenFilePicker()
der File System Access API aufgerufen. Diese Funktion gibt ein Array von Dateien oder eine einzelne Datei zurück, je nachdem, ob mehrere Dateien ausgewählt sind. Jetzt muss nur noch das Dateihandle in das Dateiobjekt eingefügt werden, damit es wieder abgerufen werden kann.
export default async (options = {}) => {
const accept = {};
// Not shown: deal with extensions and MIME types.
const handleOrHandles = await window.showOpenFilePicker({
types: [
{
description: options.description || '',
accept: accept,
},
],
multiple: options.multiple || false,
});
const files = await Promise.all(handleOrHandles.map(getFileWithHandle));
if (options.multiple) return files;
return files[0];
const getFileWithHandle = async (handle) => {
const file = await handle.getFile();
file.handle = handle;
return file;
};
};
Die Fallback-Implementierung basiert auf einem input
-Element vom Typ "file"
. Nach der Aushandlung der zu akzeptierenden MIME-Typen und Erweiterungen besteht der nächste Schritt darin, programmatisch auf das Eingabeelement zu klicken, damit das Dialogfeld zum Öffnen von Dateien angezeigt wird. Das Promise wird aufgelöst, wenn der Nutzer eine oder mehrere Dateien ausgewählt hat.
export default async (options = {}) => {
return new Promise((resolve) => {
const input = document.createElement('input');
input.type = 'file';
const accept = [
...(options.mimeTypes ? options.mimeTypes : []),
options.extensions ? options.extensions : [],
].join();
input.multiple = options.multiple || false;
input.accept = accept || '*/*';
input.addEventListener('change', () => {
resolve(input.multiple ? Array.from(input.files) : input.files[0]);
});
input.click();
});
};
Dateien speichern
Jetzt zum Sparen. In Excalidraw erfolgt das Speichern in einer Funktion namens saveAsJSON()
. Zuerst wird das Excalidraw-Elementarray in JSON serialisiert, dann wird das JSON in einen Blob konvertiert und schließlich wird eine Funktion namens fileSave()
aufgerufen. Diese Funktion wird ebenfalls von der Bibliothek browser-fs-access bereitgestellt.
export const saveAsJSON = async (
elements: readonly ExcalidrawElement[],
appState: AppState,
) => {
const serialized = serializeAsJSON(elements, appState);
const blob = new Blob([serialized], {
type: 'application/vnd.excalidraw+json',
});
const fileHandle = await fileSave(
blob,
{
fileName: appState.name,
description: 'Excalidraw file',
extensions: ['.excalidraw'],
},
appState.fileHandle,
);
return { fileHandle };
};
Sehen wir uns zuerst die Implementierung für Browser mit Unterstützung der File System Access API an. Die ersten Zeilen sind etwas kompliziert, aber sie dienen nur dazu, die MIME-Typen und Dateinamenerweiterungen zu vereinbaren. Wenn ich bereits gespeichert habe und bereits ein Dateihandle habe, muss kein Speicherdialogfeld angezeigt werden. Wenn es sich jedoch um das erste Speichern handelt, wird ein Dateidialogfeld angezeigt und die App erhält einen Dateihandle zur zukünftigen Verwendung zurück. Der Rest besteht dann nur noch darin, in die Datei zu schreiben, was über einen schreibbaren Stream erfolgt.
export default async (blob, options = {}, handle = null) => {
options.fileName = options.fileName || 'Untitled';
const accept = {};
// Not shown: deal with extensions and MIME types.
handle =
handle ||
(await window.showSaveFilePicker({
suggestedName: options.fileName,
types: [
{
description: options.description || '',
accept: accept,
},
],
}));
const writable = await handle.createWritable();
await writable.write(blob);
await writable.close();
return handle;
};
Die Funktion „Speichern unter“
Wenn ich mich entscheide, ein bereits vorhandenes Dateihandle zu ignorieren, kann ich eine „Speichern unter“-Funktion implementieren, um eine neue Datei auf Grundlage einer vorhandenen Datei zu erstellen. Um das zu zeigen, öffne ich eine vorhandene Datei, nehme einige Änderungen vor und überschreibe dann nicht die vorhandene Datei, sondern erstelle mit der Funktion „Speichern unter“ eine neue Datei. Die Originaldatei bleibt dabei erhalten.
Die Implementierung für Browser, die die File System Access API nicht unterstützen, ist kurz, da sie nur ein Anker-Element mit einem download
-Attribut erstellt, dessen Wert der gewünschte Dateiname ist, und eine Blob-URL als href
-Attributwert.
export default async (blob, options = {}) => {
const a = document.createElement('a');
a.download = options.fileName || 'Untitled';
a.href = URL.createObjectURL(blob);
a.addEventListener('click', () => {
setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);
});
a.click();
};
Das Anker-Element wird dann programmatisch angeklickt. Um Speicherlecks zu vermeiden, muss die Blob-URL nach der Verwendung widerrufen werden. Da es sich nur um einen Download handelt, wird nie ein Dialogfeld zum Speichern von Dateien angezeigt und alle Dateien werden im Standardordner Downloads
gespeichert.
Drag-and-Drop
Eine meiner Lieblingssystemintegrationen auf dem Desktop ist Drag-and-drop. Wenn ich in Excalidraw eine .excalidraw
-Datei in die Anwendung ziehe, wird sie sofort geöffnet und ich kann mit der Bearbeitung beginnen. In Browsern, die die File System Access API unterstützen, kann ich meine Änderungen dann sogar sofort speichern. Da der erforderliche Dateihandle durch das Ziehen und Ablegen abgerufen wurde, ist kein Dialogfeld zum Speichern der Datei erforderlich.
Das Geheimnis besteht darin, getAsFileSystemHandle()
für das Element data transfer aufzurufen, wenn die File System Access API unterstützt wird. Ich übergebe diesen Dateihandle dann an loadFromBlob()
, das Sie vielleicht noch von einigen Absätzen weiter oben kennen. Mit Dateien können Sie vieles tun: öffnen, speichern, überschreiben, ziehen und ablegen. Mein Kollege Pete und ich haben alle diese Tricks und mehr in unserem Artikel dokumentiert. Dort können Sie alles nachlesen, falls es Ihnen zu schnell ging.
const file = event.dataTransfer?.files[0];
if (file?.type === 'application/json' || file?.name.endsWith('.excalidraw')) {
this.setState({ isLoading: true });
// Provided by browser-fs-access.
if (supported) {
try {
const item = event.dataTransfer.items[0];
file as any.handle = await item as any
.getAsFileSystemHandle();
} catch (error) {
console.warn(error.name, error.message);
}
}
loadFromBlob(file, this.state).then(({ elements, appState }) =>
// Load from blob
).catch((error) => {
this.setState({ isLoading: false, errorMessage: error.message });
});
}
Dateien teilen
Eine weitere Systemintegration, die derzeit unter Android, ChromeOS und Windows verfügbar ist, erfolgt über die Web Share Target API. Hier bin ich in der Dateimanager-App im Ordner Downloads
. Ich sehe zwei Dateien, eine davon mit dem nichtssagenden Namen untitled
und einem Zeitstempel. Um zu sehen, was es enthält, klicke ich auf die drei Punkte, dann auf „Teilen“ und eine der angezeigten Optionen ist Excalidraw. Wenn ich auf das Symbol tippe, sehe ich, dass die Datei nur das I/O-Logo enthält.
Lipis in der verworfenen Electron-Version
Eine Sache, die Sie mit Dateien tun können, die ich noch nicht erwähnt habe, ist, darauf zu doppelklicken. Wenn Sie normalerweise auf eine Datei doppelklicken, wird die App geöffnet, die dem MIME-Typ der Datei zugeordnet ist. Für .docx
wäre das beispielsweise Microsoft Word.
Früher gab es eine Electron-Version von Excalidraw, die solche Dateitypzuordnungen unterstützte. Wenn Sie also auf eine .excalidraw
-Datei doppelklickten, wurde die Excalidraw Electron-App geöffnet. Lipis, den Sie bereits kennengelernt haben, war sowohl der Entwickler als auch derjenige, der Excalidraw Electron eingestellt hat. Ich fragte ihn, warum er der Meinung war, dass die Electron-Version eingestellt werden könnte:
Seit Beginn der Entwicklung von Google Drive für den Computer haben sich Nutzer eine Electron-App gewünscht, vor allem, weil sie Dateien durch Doppelklicken öffnen wollten. Wir wollten die App auch in App-Stores anbieten. Gleichzeitig schlug jemand vor, stattdessen eine PWA zu erstellen, also haben wir einfach beides gemacht. Glücklicherweise wurden wir mit Project Fugu-APIs wie Dateisystemzugriff, Zwischenablagezugriff und Dateiverarbeitung vertraut gemacht. Mit nur einem Klick können Sie die App auf Ihrem Computer oder Mobilgerät installieren, ohne die zusätzliche Größe von Electron. Es war eine einfache Entscheidung, die Electron-Version einzustellen, sich nur auf die Web-App zu konzentrieren und sie zur bestmöglichen PWA zu machen. Außerdem können wir PWAs jetzt im Play Store und im Microsoft Store veröffentlichen. Das ist ein enormer Anstieg.
Man könnte sagen, dass Excalidraw für Electron nicht eingestellt wurde, weil Electron schlecht ist, sondern weil das Web gut genug geworden ist. Das gefällt mir!
Dateiverwaltung
Wenn ich sage, dass das Web gut genug geworden ist, liegt das an Funktionen wie der bevorstehenden Dateiverarbeitung.
Dies ist eine reguläre Installation von macOS Big Sur. Sehen wir uns nun an, was passiert, wenn ich mit der rechten Maustaste auf eine Excalidraw-Datei klicke. Ich kann sie mit Excalidraw, der installierten PWA, öffnen. Natürlich funktioniert auch ein Doppelklick, aber in einem Screencast ist das weniger eindrucksvoll.
Wie funktioniert das? Im ersten Schritt müssen Sie dem Betriebssystem die Dateitypen mitteilen, die Ihre Anwendung verarbeiten kann. Ich verwende dazu ein neues Feld namens file_handlers
im Web-App-Manifest. Sein Wert ist ein Array von Objekten mit einer Aktion und einer accept
-Property. Die Aktion bestimmt den URL-Pfad, unter dem das Betriebssystem Ihre App startet. Das Accept-Objekt besteht aus Schlüssel/Wert-Paaren von MIME-Typen und den zugehörigen Dateiendungen.
{
"name": "Excalidraw",
"description": "Excalidraw is a whiteboard tool...",
"start_url": "/",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff",
"file_handlers": [
{
"action": "/",
"accept": {
"application/vnd.excalidraw+json": [".excalidraw"]
}
}
]
}
Im nächsten Schritt wird die Datei beim Starten der Anwendung verarbeitet. Das passiert in der launchQueue
-Schnittstelle, in der ich einen Consumer festlegen muss, indem ich setConsumer()
aufrufe. Der Parameter für diese Funktion ist eine asynchrone Funktion, die die launchParams
empfängt. Dieses launchParams
-Objekt hat ein Feld namens „files“, das ein Array von Dateihandles zurückgibt, mit denen ich arbeiten kann. Ich kümmere mich nur um die erste und erhalte aus diesem Dateihandle ein Blob, das ich dann an unseren alten Freund loadFromBlob()
übergebe.
if ('launchQueue' in window && 'LaunchParams' in window) {
window as any.launchQueue
.setConsumer(async (launchParams: { files: any[] }) => {
if (!launchParams.files.length) return;
const fileHandle = launchParams.files[0];
const blob: Blob = await fileHandle.getFile();
blob.handle = fileHandle;
loadFromBlob(blob, this.state).then(({ elements, appState }) =>
// Initialize app state.
).catch((error) => {
this.setState({ isLoading: false, errorMessage: error.message });
});
});
}
Falls das zu schnell ging, können Sie in meinem Artikel mehr über die File Handling API lesen. Sie können die Dateiverarbeitung aktivieren, indem Sie das Flag für experimentelle Webplattformfunktionen festlegen. Die Funktion soll im Laufe des Jahres in Chrome eingeführt werden.
Zwischenablage-Integration
Ein weiteres cooles Feature von Excalidraw ist die Zwischenablage-Integration. Ich kann die gesamte Zeichnung oder nur Teile davon in die Zwischenablage kopieren, bei Bedarf ein Wasserzeichen hinzufügen und sie dann in eine andere App einfügen. Das ist übrigens eine Webversion der Windows 95-Paint-App.
Das funktioniert überraschend einfach. Ich benötige nur das Canvas als Blob, das ich dann in die Zwischenablage schreibe, indem ich ein Ein-Element-Array mit einem ClipboardItem
mit dem Blob an die Funktion navigator.clipboard.write()
übergebe. Weitere Informationen dazu, was Sie mit der Clipboard API tun können, finden Sie in diesem Artikel von Jason und mir.
export const copyCanvasToClipboardAsPng = async (canvas: HTMLCanvasElement) => {
const blob = await canvasToBlob(canvas);
await navigator.clipboard.write([
new window.ClipboardItem({
'image/png': blob,
}),
]);
};
export const canvasToBlob = async (canvas: HTMLCanvasElement): Promise<Blob> => {
return new Promise((resolve, reject) => {
try {
canvas.toBlob((blob) => {
if (!blob) {
return reject(new CanvasError(t('canvasError.canvasTooBig'), 'CANVAS_POSSIBLY_TOO_BIG'));
}
resolve(blob);
});
} catch (error) {
reject(error);
}
});
};
Zusammenarbeit mit Anderen
Sitzungs-URL teilen
Wussten Sie, dass es in Excalidraw auch einen Modus für die Zusammenarbeit gibt? Mehrere Personen können gemeinsam an demselben Dokument arbeiten. Um eine neue Sitzung zu starten, klicke ich auf die Schaltfläche für die gemeinsame Bearbeitung in Echtzeit und starte dann eine Sitzung. Dank der Web Share API, die in Excalidraw integriert ist, kann ich die Sitzungs-URL ganz einfach mit meinen Mitbearbeitern teilen.
Zusammenarbeit in Echtzeit
Ich habe eine lokale Zusammenarbeitssitzung simuliert, indem ich auf meinem Pixelbook, meinem Pixel 3a und meinem iPad Pro am Google I/O-Logo gearbeitet habe. Sie sehen, dass Änderungen, die ich auf einem Gerät vornehme, auf allen anderen Geräten übernommen werden.
Ich kann sogar sehen, wie sich alle Cursors bewegen. Der Cursor des Pixelbook bewegt sich gleichmäßig, da er über ein Touchpad gesteuert wird. Der Cursor des Pixel 3a und des iPad Pro springt jedoch, da ich diese Geräte durch Tippen mit dem Finger steuere.
Status von Mitbearbeitern sehen
Um die Echtzeit-Zusammenarbeit zu verbessern, gibt es sogar ein System zur Erkennung von Inaktivität. Der Cursor des iPad Pro hat einen grünen Punkt, wenn ich ihn verwende. Der Punkt wird schwarz, wenn ich zu einem anderen Browser-Tab oder einer anderen App wechsle. Wenn ich in der Excalidraw App bin, aber nichts tue, wird der Cursor als inaktiv angezeigt, symbolisiert durch die drei ZZZs.
Leser unserer Publikationen könnten annehmen, dass die Inaktivitätserkennung über die Idle Detection API erfolgt, einen Vorschlag in der frühen Phase, an dem im Rahmen von Project Fugu gearbeitet wurde. Achtung, Spoiler: Das ist nicht der Fall. Wir hatten zwar eine Implementierung auf Grundlage dieser API in Excalidraw, haben uns aber letztendlich für einen traditionelleren Ansatz entschieden, der auf der Messung von Zeigerbewegungen und der Seitenansicht basiert.
Wir haben Feedback dazu gegeben, warum die Idle Detection API den Anwendungsfall, den wir hatten, nicht gelöst hat. Alle Project Fugu-APIs werden öffentlich entwickelt, sodass jeder mitreden und seine Meinung äußern kann.
Lipis darüber, was Excalidraw zurückhält
Apropos: Ich habe lipis eine letzte Frage dazu gestellt, was seiner Meinung nach auf der Webplattform fehlt, was Excalidraw zurückhält:
Die File System Access API ist großartig, aber wissen Sie was? Die meisten Dateien, die mir wichtig sind, befinden sich heutzutage in meiner Dropbox oder Google Drive, nicht auf meiner Festplatte. Ich wünschte, die File System Access API würde eine Abstraktionsschicht für Anbieter von Remote-Dateisystemen wie Dropbox oder Google enthalten, mit der Entwickler arbeiten könnten. Nutzer konnten sich dann entspannen und darauf vertrauen, dass ihre Dateien beim Cloud-Anbieter ihrer Wahl sicher sind.
Ich stimme lipis voll und ganz zu. Ich lebe auch in der Cloud. Hoffentlich wird das bald umgesetzt.
Tabbed-App-Modus
Wow! Wir haben viele wirklich tolle API-Integrationen in Excalidraw gesehen. Dateisystem, Dateiverarbeitung, Zwischenablage, Web Share und Web Share Target. Aber es gibt noch etwas. Bisher konnte ich immer nur ein Dokument gleichzeitig bearbeiten. Das ist kein Problem mehr. Wir freuen uns, Ihnen eine erste Version des Tabbed Application Mode in Excalidraw präsentieren zu können. So sieht es aus.
Ich habe eine vorhandene Datei in der installierten Excalidraw-PWA geöffnet, die im Standalone-Modus ausgeführt wird. Ich öffne jetzt einen neuen Tab im eigenständigen Fenster. Dies ist kein normaler Browser-Tab, sondern ein PWA-Tab. Auf diesem neuen Tab kann ich dann eine zweite Datei öffnen und unabhängig vom selben App-Fenster daran arbeiten.
Der Tabbed-Anwendungsmodus befindet sich noch in der Anfangsphase und es ist noch nicht alles in Stein gemeißelt. Wenn Sie daran interessiert sind, sollten Sie sich meinen Artikel zum aktuellen Status dieser Funktion ansehen.
Abschluss
Wenn Sie über diese und andere Funktionen auf dem Laufenden bleiben möchten, sollten Sie sich unseren Fugu API-Tracker ansehen. Wir freuen uns darauf, das Web weiterzuentwickeln und Ihnen mehr Möglichkeiten auf der Plattform zu bieten. Wir freuen uns auf ein immer besseres Excalidraw und auf all die tollen Anwendungen, die Sie entwickeln werden. Beginnen Sie mit dem Erstellen unter excalidraw.com.
Ich bin gespannt, welche der APIs, die ich heute vorgestellt habe, in Ihren Apps zum Einsatz kommen. Ich heiße Tom und bin auf Twitter und im Internet unter @tomayac zu finden. Vielen Dank fürs Zusehen und viel Spaß auf dem Rest der Google I/O.