Każdą dostatecznie zaawansowaną technologię nie da się odróżnić od magii. Chyba że go rozumiesz. Nazywam się Thomas Steiner i pracuję w zespole ds. kontaktów z deweloperami w Google. W trakcie naszej prezentacji na konferencję Google I/O chcę Panu/Pani opowiedzieć o niektórych nowych interfejsach API Fugu i o tym, jak usprawniają one podstawowe doświadczenia użytkowników w aplikacji Excalidraw PWA. Może Pan/Pani czerpać inspirację z tych pomysłów i zastosować je we własnych aplikacjach.
Jak udało mi się przejść do Excalidraw
Chcę zacząć od opowieści. 1 stycznia 2020 r. Christopher Chedeau, inżynier oprogramowania w firmie Facebook, zamieścił tweeta o niewielkiej aplikacji do rysowania, nad którymi pracują. Dzięki niemu możesz rysować kreskówkowe pola i strzały, ręcznie rysowana. Następnego dnia mogłem też rysować elipsy i tekst, a także zaznaczać obiekty i poruszać się ich otoczenie. 3 stycznia aplikacja otrzymała nazwę Excalidraw. w ramach projektu, zakup nazwy domeny był jednym z pierwszych działań Krzysztofa. Według Teraz możesz użyć kolorów i wyeksportować cały rysunek jako plik PNG.
15 stycznia Christopher wymyślił post na blogu, który przyciągnął na Twitterze. Post zaczynał się od kilku imponujących statystyk:
- 12 tys. unikalnych aktywnych użytkowników
- 1,5 tys.gwiazdek w GitHubie
- 26 współtwórców
W przypadku projektu, który rozpoczął się zaledwie dwa tygodnie temu, nie jest to nic złego. Jednak prawdziwą rzeczą moje zainteresowanie rosło na dalszej części posta. Christopher pisał, że próbował czegoś nowego time: przyznanie wszystkim użytkownikom, którzy otrzymali żądanie pull, bezwarunkowego dostępu do zatwierdzenia. Tego samego dnia czytając post na blogu, doszła do mnie prośba o wyciągnięcie ręki. dodano obsługę interfejsu File System Access API do Excalidraw, naprawiono przesłanej przez kogoś prośby o dodanie funkcji.
Moje żądanie pull zostało scalone dzień później i od tego momentu mam pełny dostęp do zatwierdzenia. Nie trzeba chyba mówić, Nie nadużywałam swojej mocy. Nikt inny spośród 149 współtwórców również do tej pory.
Dziś Excalidraw to w pełni funkcjonalna, progresywna aplikacja internetowa, którą można zainstalować. obsługę offline, efektowny tryb ciemny, a tak, możliwość otwierania i zapisywania plików Interfejs File System Access API.
Lipis wyjaśnia, dlaczego poświęca tyle czasu na Excalidraw
Na tym kończy się mój „Jak udało mi się dotrzeć do Excalidraw” ale zanim zajmę się czymś innym, Niezwykłe funkcje Excalidraw, miło mi przedstawić Panayiotis. Panayiotis Lipiridis, na internet (określany po prostu jako lipis) jest najbardziej efektywnym twórcą Excalidraw. Zapytałem lipisa, co motywuje go do poświęcania tak dużej ilości czasu na Excalidraw:
Tak jak wszyscy, dowiedziałam się o tym projekcie z tweeta Christophera. Moja pierwsza publikacja dodaję otwartą bibliotekę kolorów, czyli kolory, dostępny obecnie w Excalidraw. W miarę jak projekt się rozwijał i otrzymano coraz więcej próśb, moim kolejnym dużym wspieraliśmy tworzenie backendu do przechowywania rysunków, aby użytkownicy mogli je udostępniać. Ale co Zachęcam do wkładu w to, że kto korzysta z Excalidraw, szuka wymówki go powtórzyć.
W pełni zgadzam się z lipi. Każdy, kto próbował używać Excalidraw, szuka powodu, by ponownie go używać.
Excalidraw w akcji
Teraz pokażę Ci, jak w praktyce używać Excalidraw. Nie jestem wielkim artystą, ale Logo Google I/O jest wystarczająco proste, więc warto je wypróbować. Pole to litera „i”, a wiersz może być ukośnik i „o” to okrąg. Przytrzymuję wciśnięty klawisz shift, aby uzyskać idealną okrąg. Pozwól mi się ruszać przewężenie, dzięki czemu wygląda lepiej. Teraz trochę koloru dla „i” i „o”. Niebieski to dobrze. Może i zmienić styl wypełnienia? Wszystkie wymiary pełne, czy krzyżowe? Nie, czapka wygląda świetnie. Nie jest to idealne, Tak działa Excalidraw, więc zachowam go.
Klikam ikonę zapisywania i wprowadzam nazwę pliku w oknie dialogowym zapisywania. W przeglądarce Chrome, obsługuje interfejs File System Access API, nie jest to pobieranie, tylko prawdziwa operacja zapisu, wybierz lokalizację i nazwę pliku. Jeśli wprowadzam zmiany, mogę je po prostu zapisać w ten sam plik.
Chcę zmienić logo i dodać literę „i” czerwony. Jeśli teraz ponownie kliknę Zapisz, moja modyfikacja zostanie zapisana w taki sam jak poprzednio. W ramach dowodu chcę wyczyścić obszar roboczy i ponownie otworzyć plik. Jak widać, zmodyfikowane czerwono-niebieskie logo.
Praca z plikami
W przeglądarkach, które obecnie nie obsługują interfejsu File System Access API, każda operacja zapisu jest pobierania. Po wprowadzeniu zmian otrzymam wiele plików o rosnącej liczbie w która wypełnia folder Pobrane. Mimo to mogę zapisać plik.
Otwieranie plików
Na czym polega sekret? Jak otwierać i zapisywać pliki w różnych przeglądarkach, które mogą, ale nie muszą,
obsługuje interfejs File System Access API? Otwarcie pliku w Excalidraw odbywa się w funkcji o nazwie
loadFromJSON)(
), która z kolei wywołuje funkcję fileOpen()
.
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);
};
Funkcja fileOpen()
pochodząca z napisanej przeze mnie małej biblioteki o nazwie
browser-fs-access używany w
Excalidraw. Ta biblioteka zapewnia dostęp do systemu plików za pomocą
File System Access API ze starszą wersją zastępczą, więc może być używana w dowolnym
przeglądarki.
Najpierw pokażę Ci implementację, gdy interfejs API jest obsługiwany. Po wynegocjowaniu
akceptowane typy MIME i rozszerzenia plików, najważniejszym elementem jest wywoływanie interfejsu File System Access API
funkcja showOpenFilePicker()
. Ta funkcja zwraca tablicę plików lub pojedynczy plik, w zależności od
czy zaznaczyć wiele plików. Teraz wystarczy tylko dodać do niego uchwyt
tak aby można go było pobrać ponownie.
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;
};
};
Implementacja kreacji zastępczej opiera się na elemencie input
typu "file"
. Po wynegocjowaniu
Akceptowane typy i rozszerzenia MIME, następny krok to programowe kliknięcie danych wejściowych
tak by pojawiło się okno otwierania pliku. W przypadku zmiany, czyli gdy użytkownik wybierze jedną lub
wiele plików, obietnica rozwiązana.
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();
});
};
Zapisuję pliki
Czas na zapisywanie. W Excalidraw zapisywanie odbywa się w funkcji saveAsJSON()
. To najpierw
zserializuje tablicę elementów Excalidraw do formatu JSON, konwertuje plik JSON na obiekt blob, a następnie wywołuje metodę
funkcja o nazwie fileSave()
. Funkcja ta jest również dostępna przez
browser-fs-access.
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 };
};
Jeszcze raz przyjrzyjmy się implementacji w przeglądarkach obsługujących interfejs File System Access API. Na początku kilka pierwszych wierszy wygląda na trochę skomplikowanych, ale wystarczy negocjować typy MIME rozszerzeń. Jeśli mam już zapisany plik i mam już uchwyt, nie trzeba otwierać okna zapisywania wyświetlane. Jeśli jednak jest to pierwszy zapis, wyświetla się okno dialogowe pliku, a aplikacja otrzymuje uchwyt. z powrotem do wykorzystania w przyszłości. Pozostała część to zapis w pliku, który odbywa się zapisu.
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;
};
Przycisk „Zapisz jako” cecha
Jeśli zignoruję istniejący uchwyt pliku, mogę zaimplementować funkcję „zapisz jako” do utworzenia nowy plik na podstawie istniejącego pliku. Aby to pokazać, pozwól mi otworzyć istniejący plik, i nie można zastąpić istniejącego pliku, ale można utworzyć nowy plik za pomocą polecenia funkcji. Dzięki temu pierwotny plik pozostaje niezmieniony.
Implementacja w przeglądarkach, które nie obsługują interfejsu File System Access API, jest krótka,
czyli utworzenie elementu kotwicy z atrybutem download
, którego wartością jest żądana nazwa pliku i
adresu URL obiektu blob jako wartości atrybutu href
.
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();
};
Następnie element kotwicy jest klikany automatycznie. Aby zapobiec wyciekom pamięci, adres URL obiektu blob wymaga
zostaną unieważnione po użyciu. Ponieważ jest to plik do pobrania, nigdy nie wyświetla się okno zapisywania plików,
trafiają do domyślnego folderu Downloads
.
Przeciągnij i upuść
Jedną z moich ulubionych integracji systemowych na komputerach jest przeciąganie i upuszczanie. W Excalidraw po upuszczeniu
.excalidraw
. Plik zostanie otwarty od razu i będzie można rozpocząć edycję. W przeglądarkach
które obsługują interfejs File System Access API, mogę wtedy nawet od razu zapisać zmiany. Nie musisz
w oknie zapisywania pliku, ponieważ wymagany uchwyt został uzyskany w wyniku przeciągania i upuszczania pliku
.
Aby tak się stało, musisz wywołać funkcję getAsFileSystemHandle()
na
transfer danych, jeśli obsługiwany jest interfejs File System Access API. Następnie przekazuję
uchwytu do loadFromBlob()
, który być może pamiętasz z kilku powyższych akapitów. Tak wiele
Czynności, które można wykonać z plikami: otwieranie, zapisywanie, nadmierne zapisywanie, przeciąganie i upuszczanie. Mój kolega, Piotr
Wszystkie te i inne sztuczki znajdziesz w naszym artykule, dzięki któremu
Chcę nadrobić zaległości, bo to wszystko przebiegło za szybko.
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 });
});
}
Udostępnianie plików
Inną integracją systemów z systemem Android, ChromeOS i Windows jest
Interfejs API docelowego udziału w internecie. Jestem w aplikacji Files w moim folderze Downloads
. Ja
może zobaczyć dwa pliki, w tym 1 z nieopisową nazwą untitled
i sygnaturą czasową. Aby sprawdzić, co
klikam 3 kropki i udostępniam. Jedna z wyświetlonych opcji to
Excalidraw. Po dotknięciu ikony plik ponownie zawiera logo I/O.
Lipis w wycofanej wersji Electron
Z plikami, o których jeszcze nie mówiliśmy, możesz zrobić dwukrotne kliknięcie pliku. Co zwykle
Gdy klikniesz dwukrotnie plik, oznacza to, że aplikacja powiązana z jego typem MIME
zostanie otwarte. Na przykład w przypadku .docx
będzie to Microsoft Word.
Firma Excalidraw miała wcześniej wersję Electron, która
obsługuje takie powiązania, dlatego po dwukrotnym kliknięciu pliku .excalidraw
Otworzyłaby się aplikacja Excalidraw Electron. Lipis, którą już znasz, została twórczynią
oraz wycofanie Excalidraw Electron. Zapytałem go, dlaczego uważa, że można wycofać
Wersja elektroniczna:
Użytkownicy od samego początku pytali o aplikację Electron, głównie dlatego, otwierać pliki, klikając je dwukrotnie. Planowaliśmy też udostępnić aplikację w sklepach z aplikacjami. Równolegle ktoś zaproponowaliśmy utworzenie PWA, więc zrobiliśmy obie. Na szczęście dowiedzieliśmy się o Projekcie Fugu, Interfejsy API takie jak dostęp do systemu plików, dostęp do schowka, obsługa plików i inne. Jednym kliknięciem możesz Zainstaluj ją na komputerze lub komórce, bez dodatkowej wagi urządzenia Electron. To było łatwe decyzji o wycofaniu wersji Electron, skupieniu się wyłącznie na aplikacji internetowej i uczynieniu z niej progresywnych aplikacji internetowych. Teraz możemy publikować aplikacje PWA w Sklepie Play, Sklep. To imponująca liczba!
Można powiedzieć, że rozwiązanie Excalidraw do Electron nie zostało wycofane ze względu na to, że Electron jest zły, ponieważ sieć jest wystarczająco dobra. Podoba mi się!
Obsługa plików
Kiedy powiem „internet jest wystarczająco dobry”, wynika to z funkcji takich jak nowa wersja Pliku Obsługa.
To jest zwykła instalacja systemu macOS Big Sur. Teraz zobacz, co się dzieje, gdy kliknę prawym przyciskiem myszy Plik Excalidraw. Mogę otworzyć go za pomocą zainstalowanej aplikacji PWA Excalidraw. Oczywiście. dwukrotne klikanie też zadziała, ale w screencaście będzie to mniej dramatyczne.
Jak to działa? Pierwszym krokiem jest określenie typów plików, które obsługuje moja aplikacja.
oraz system operacyjny. Robię to w nowym polu o nazwie file_handlers
w manifeście aplikacji internetowej. To
jest tablicą obiektów z działaniem i właściwością accept
. Czynność określa adres URL
ścieżka, na której system operacyjny uruchamia Twoją aplikację, a obiektem „Akceptuję” są pary klucz-wartość MIME.
typów plików i powiązanych z nimi rozszerzeń.
{
"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"]
}
}
]
}
Następnym krokiem jest obsługa pliku po uruchomieniu aplikacji. Dzieje się tak w launchQueue
w którym trzeba określić konsumenta, dzwoniąc pod numer setConsumer()
. Parametr argumentu
jest funkcją asynchroniczną, która odbiera funkcję launchParams
. Ten obiekt typu launchParams
zawiera pole o nazwie pliki, w którym znajduje się tablica uchwytów plików do pracy. Interesuje mnie tylko
z nowego uchwytu pliku, powstaje obiekt blob, który przesyłam poprzedniemu znajomemu.
loadFromBlob()
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 });
});
});
}
Jeśli przebiegnie zbyt szybko, możesz dowiedzieć się więcej o interfejsie File Handling API w artykule moj artykuł. Możesz włączyć obsługę plików, konfigurując eksperymentalną platformę internetową. flaga funkcji. Ma pojawić się w Chrome jeszcze w tym roku.
Integracja ze schowkiem
Inną ciekawą funkcją Excalidraw jest integracja ze schowek. Mogę skopiować cały rysunek lub do schowka, dodać znak wodny i wkleić go do innej aplikacji. Przy okazji: jest to internetowa wersja aplikacji Windows 95 Paint.
Sposób działania jest zaskakująco prosty. Potrzebny mi tylko obiekt canvas w postaci bloba, który następnie piszę
do schowka, przekazując do funkcji blob tablicę jednoelementową z obiektem ClipboardItem
navigator.clipboard.write()
. Więcej informacji o możliwościach schowka
API, przeczytaj artykuły Jacka i mój artykuł.
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);
}
});
};
Współpraca z innymi
Udostępnianie adresu URL sesji
Czy wiesz, że Excalidraw też ma tryb współpracy? Różne osoby mogą wspólnie pracować nad ten sam dokument. Żeby rozpocząć nową sesję, kliknę przycisk współpracy na żywo i rozpocznę . Mogę łatwo udostępnić URL sesji współpracownikom dzięki Web Share API zintegrowany przez Excalidraw.
Współpraca na żywo
Stworzyłem lokalnie sesję współpracy nad logo Google I/O na Pixelbooku. mój telefon Pixel 3a i iPad Pro. Jak widać, zmiany wprowadzone na jednym urządzeniu są odzwierciedlane na na wszystkich innych urządzeniach.
Widzę nawet ruchy wszystkich kursorów. Kursor na Pixelbooku porusza się stabilnie, ponieważ za pomocą trackpada, ale kursor telefonu Pixel 3a i kursor na tablecie iPad Pro skaczą się dookoła. sterować tymi urządzeniami, dotykając palcem.
Wyświetlanie stanów współpracowników
Aby usprawnić współpracę w czasie rzeczywistym, wprowadziliśmy nawet system wykrywania bezczynności. Kursor na iPadzie Pro pokazuje zieloną kropkę, gdy go używam. Po przełączeniu na w innej karcie lub w innej aplikacji przeglądarki. Gdy jestem w aplikacji Excalidraw, ale nic nie robię, kursor pokazuje mnie jako nieaktywnego, co symbolizuje trzy zZZ.
Regularni czytelnicy naszych publikacji mogą sądzić, że wykrywanie bezczynności jest realizowane przez Idle Detection API, czyli propozycję na wczesnym etapie, nad którą pracowaliśmy w kontekście Projektu Fugu. Uwaga spojler: nie. Chociaż mieliśmy wdrożenie oparte na tym interfejsie API, Ostatecznie zdecydowaliśmy się zastosować bardziej tradycyjne podejście, oparte na pomiarze poruszanie się wskaźnikiem i widoczność strony.
Przesłaliśmy opinię na temat tego, dlaczego interfejs Idle Detection API który nie był możliwy do rozwiązania. Wszystkie interfejsy API Project Fugu są opracowywane w otwartej wersji, każdy może dołączyć do nas i usłyszeć swój głos.
Lipis o tym, co powstrzymuje Excalidraw
W związku z tym zadałam ostatnie pytanie. Jego pytanie dotyczyło tego, czego brakuje w internecie. która powstrzymuje Excalidraw:
Interfejs File System Access API jest świetny, ale wiesz co? Obecnie większość plików, na których mi zależy znajdziesz na moim koncie usługi Dropbox lub na Dysku Google, a nie na moim dysku twardym. Chcę, aby interfejs File System Access API nie uwzględnić warstwę abstrakcji w przypadku zdalnych dostawców systemów plików, takich jak Dropbox czy Google. i na które programiści mogą kodować. Dzięki temu użytkownicy mogą mieć pewność, że ich pliki są bezpieczne z zaufanym dostawcą chmury.
W pełni się z nimi zgadzam, ja też mieszkam w chmurze. Mamy nadzieję, że uda się to wdrożyć wkrótce.
Tryb aplikacji z kartami
Niesamowite! Widzimy, że w Excalidraw jest wiele świetnych integracji API. System plików, obsługa plików, schowek, udostępnianie w internecie oraz Cel udziału w internecie. Tu jednak jeszcze jedno. Do tej pory mogłem to robić tylko edytowania jednego dokumentu naraz. Już nie. Po raz pierwszy ciesz się wczesną wersją aplikacji trybu aplikacji z kartami w Excalidraw. Tak to wygląda.
Mam otwarty plik w zainstalowanej aplikacji PWA Excalidraw, która działa w trybie samodzielnym. Teraz Otwieram nową kartę w osobnym oknie. To nie jest zwykła karta przeglądarki, tylko karta PWA. W tym nowa karta Mogę otworzyć dodatkowy plik i pracować na nich niezależnie w tym samym oknie aplikacji.
Tryb aplikacji z kartami jest na wczesnym etapie i nie wszystko jest gotowe. Jeśli zapoznaj się z aktualnym stanem tej funkcji tutaj: moj artykuł.
Zakończenie
Aby być na bieżąco z tą i innymi funkcjami, obejrzyj Tag śledzenia interfejsu Fugu API. Cieszymy się, że możemy rozwijać sieć pozwala robić więcej na platformie. Stale ulepszamy Excalidraw. Tu wszystkich które stworzycie. Zacznij tworzyć od excalidraw.com.
Nie mogę się doczekać, aż niektóre z przedstawionych dzisiaj interfejsów API pojawią się w Twoich aplikacjach. Mam na imię Tom. możecie znaleźć mnie pod adresem @tomayac na Twitterze i w internecie. Dziękuję za uwagę i życzę miłej lekcji z konferencji Google I/O.