SVGcode: PWA להמרת תמונות רשת לגרפיקה וקטורית מסוג SVG

SVGcode הוא אפליקציית אינטרנט מסוג Progressive Web App שמאפשרת להמיר תמונות מסוג רסטר כמו JPG , PNG , GIF , WebP , AVIF וכו' לגרפיקה וקטורית בפורמט SVG. היא משתמשת ב-File System Access API , Async Clipboard API , File Treatment API ובהתאמה אישית של window Controls Overlay.

(אם אתם מעדיפים לצפות במקום לקרוא את המאמר, המאמר הזה זמין גם כסרטון.)

מרסטר לווקטור

האם אי פעם שיניתם את גודל התמונה והתוצאה הייתה מפוקסלת ולא הייתה משביעת רצון? אם כן, כנראה שאתם מתכננים להשתמש בפורמט של תמונה מסוג רסטר כמו WebP, PNG או JPG.

אפשר להגדיל תמונה בפורמט רסטר כדי להיראות מפוקסלים.

לעומת זאת, גרפיקה וקטורית היא תמונות שמוגדרות באמצעות נקודות במערכת קואורדינטות. הנקודות האלה מחוברות באמצעות קווים ועקומות כדי ליצור פוליגונים וצורות אחרות. לגרפיקה וקטורית יש יתרון על פני גרפיקה בפורמט רסטר, כי ניתן לשנות אותה כלפי מעלה או כלפי מטה לכל רזולוציה בלי תצוגת פיקסלים.

אפשר להרחיב תמונה וקטורית בלי לגרוע מהאיכות.

חדש: SVGcode

פיתחתי PWA בשם SVGcode, שיכולה לעזור להמיר תמונות רסטר לווקטורים. קרדיט כשמגיע הקרדיט: לא המצאתי את זה. עם SVGcode, אני פשוט עומדת על הכתפיים של כלי שורת הפקודה בשם Potrace של Peter Selinger שהמרתי ל-Web Assembly, כדי שאפשר יהיה להשתמש בו באפליקציית אינטרנט.

צילום מסך של האפליקציה SVGcode.
האפליקציה SVGcode.

שימוש ב-SVGcode

קודם כול, אני רוצה להראות לך איך להשתמש באפליקציה. אתחיל בתמונת הטיזר של Chrome Dev Summit שהורדתי מערוץ ChromiumDev Twitter. זוהי תמונה מסוג רסטר בפורמט PNG, שלאחר מכן גוררים לאפליקציית SVGcode. כשמשחררים את הקובץ, האפליקציה מבצעת הערכה של צבע התמונה לפי צבע, עד שמופיעה גרסה של הקלט בפורמט וקטורי. עכשיו אני יכול להגדיל את התמונה, וכמו שאפשר לראות, הקצוות נשארים חדים. אבל אם תתמקדו בלוגו של Chrome, תוכלו לראות שהמעקב לא היה מושלם, ובמיוחד קווי המתאר של הלוגו נראים קצת מנוקדים. אפשר לשפר את התוצאה על ידי ביטול סקירת המעקב על ידי חסימת כתמים של עד חמישה פיקסלים, למשל.

המרת תמונה שנוספה ל-SVG.

פוסטריזציה ב-SVGcode

שלב חשוב בווקטוריזציה, במיוחד בתמונות מצולמות, הוא איחוד תמונת הקלט כדי לצמצם את מספר הצבעים. קוד SVG מאפשר לי לעשות זאת לכל ערוץ צבע, ולראות את ה-SVG שהתקבל בזמן ביצוע השינויים. כשנהיה מרוצה מהתוצאה, אוכל לשמור את ה-SVG לדיסק הקשיח שלי ולהשתמש בו בכל מקום.

פוסטרים של תמונה כדי לצמצם את מספר הצבעים.

ממשקי API שנעשה בהם שימוש ב-SVGcode

עכשיו, אחרי שגיליתם למה האפליקציה יכולה לבצע, אראה לכם כמה מממשקי ה-API שעוזרים לעשות את הקסם.

אפליקציית אינטרנט מסוג Progressive Web App

SVGcode הוא אפליקציה מסוג Progressive Web App ניתנת להתקנה, ולכן אפשר להפעיל אותה במצב אופליין. האפליקציה מבוססת על תבנית ה-JS של Vanilla עבור Vite.js ומשתמשת ב-PWA של הפלאגין של Vite, שיוצרת קובץ שירות (service worker) שמשתמש ב-Workbox.js מתחת למכסה. Workbox היא קבוצה של ספריות שיכולות להפעיל קובץ שירות (service worker) שמוכן לייצור עבור Progressive Web Apps. ייתכן שהדפוס הזה לא בהכרח יפעל בכל האפליקציות, אבל בתרחיש לדוגמה של SVGcode זה מעולה.

שכבת-על של פקדי החלונות

כדי להגדיל את השטח הזמין במסך, קוד SVGcode משתמש בהתאמה אישית של שכבת-על של פקדי חלונות על ידי הזזת התפריט הראשי כלפי מעלה לאזור של סרגל הכותרת. ניתן לראות שהיא תופעל בסוף תהליך ההתקנה.

התקנת קוד SVG והפעלת התאמה אישית של שכבת-על בפקדי החלונות.

File System Access API

כדי לפתוח קובצי תמונה של קלט ולשמור את קובצי ה-SVG שמתקבלים, נעשה שימוש ב-File System Access API. כך אני יכולה לשמור הפניה לקבצים שנפתחו בעבר ולהמשיך מהנקודה שבה הפסקתי, גם אחרי הטעינה מחדש של האפליקציה. בכל פעם ששומרים תמונה, מתבצעת אופטימיזציה שלה דרך ספריית svgo. הפעולה הזו עשויה להימשך זמן מה, בהתאם למורכבות של קובץ ה-SVG. כדי להציג את תיבת הדו-שיח לשמירת קובץ, צריך לבצע תנועת משתמש. לכן חשוב להשיג את נקודת האחיזה של הקובץ לפני האופטימיזציה ל-SVG, ולכן תנועות המשתמשים לא יבוטלו עד שקובץ ה-SVG שעבר אופטימיזציה יהיה מוכן.

try {
  let svg = svgOutput.innerHTML;
  let handle = null;
  // To not consume the user gesture obtain the handle before preparing the
  // blob, which may take longer.
  if (supported) {
    handle = await showSaveFilePicker({
      types: [{description: 'SVG file', accept: {'image/svg+xml': ['.svg']}}],
    });
  }
  showToast(i18n.t('optimizingSVG'), Infinity);
  svg = await optimizeSVG(svg);
  showToast(i18n.t('savedSVG'));
  const blob = new Blob([svg], {type: 'image/svg+xml'});
  await fileSave(blob, {description: 'SVG file'}, handle);
} catch (err) {
  console.error(err.name, err.message);
  showToast(err.message);
}

גרירה ושחרור

כדי לפתוח קובץ אימג' אפשר להשתמש בפיצ'ר 'פתיחת קובץ', או, כמו שראינו למעלה, פשוט לגרור ולשחרר קובץ תמונה באפליקציה. הפיצ'ר לפתיחת קובץ הוא די פשוט, ומעניין יותר הוא הקטע 'גרירה ושחרור'. מה שמעניין במיוחד הוא האפשרות לקבל כינוי למערכת קבצים מהפריט של העברת הנתונים, באמצעות method getAsFileSystemHandle(). כפי שצוין קודם, יש לי גישה לכינוי הזה, כך שהוא יהיה מוכן כשהאפליקציה תיטען מחדש.

document.addEventListener('drop', async (event) => {
  event.preventDefault();
  dropContainer.classList.remove('dropenter');
  const item = event.dataTransfer.items[0];
  if (item.kind === 'file') {
    inputImage.addEventListener(
      'load',
      () => {
        URL.revokeObjectURL(blobURL);
      },
      {once: true},
    );
    const handle = await item.getAsFileSystemHandle();
    if (handle.kind !== 'file') {
      return;
    }
    const file = await handle.getFile();
    const blobURL = URL.createObjectURL(file);
    inputImage.src = blobURL;
    await set(FILE_HANDLE, handle);
  }
});

לפרטים נוספים, קראו את המאמר על File System Access API תוכלו לקרוא את קוד המקור של SVGcode בsrc/js/filesystem.js.

Async Clipboard API

קוד SVG גם משולב במלואו עם הלוח של מערכת ההפעלה באמצעות Async Clipboard API. כדי להדביק תמונות מהסייר הקבצים של מערכת ההפעלה באפליקציה, על ידי לחיצה על הלחצן של 'הדבקת התמונה' או על ידי הקשה על command או על Control + v במקלדת.

הדבקת תמונה מסייר הקבצים ל-SVGcode.

ל-Async Clipboard API יש לאחרונה יכולת לטפל גם בתמונות SVG, כך שאפשר גם להעתיק תמונת SVG ולהדביק אותה באפליקציה אחרת לצורך עיבוד נוסף.

העתקת תמונה מ-SVGcode ל-SVGOMG.
copyButton.addEventListener('click', async () => {
  let svg = svgOutput.innerHTML;
  showToast(i18n.t('optimizingSVG'), Infinity);
  svg = await optimizeSVG(svg);
  const textBlob = new Blob([svg], {type: 'text/plain'});
  const svgBlob = new Blob([svg], {type: 'image/svg+xml'});
  navigator.clipboard.write([
    new ClipboardItem({
      [svgBlob.type]: svgBlob,
      [textBlob.type]: textBlob,
    }),
  ]);
  showToast(i18n.t('copiedSVG'));
});

למידע נוסף, אפשר לקרוא את המאמר על הלוח האסינכרוני, או לעיין בקובץ src/js/clipboard.js.

טיפול בקבצים

אחת התכונות האהובות עליי ב-SVGcode היא עד כמה הוא משתלב עם מערכת ההפעלה. כ-PWA מותקן, הוא יכול להפוך ל-handler של קבצים, או אפילו ל-handler של הקבצים שמוגדר כברירת מחדל, לקובצי תמונות. כלומר, כשאני ב-Finder במכונת ה-macOS, אני יכול ללחוץ לחיצה ימנית על תמונה ולפתוח אותה באמצעות SVGcode. התכונה הזו נקראת 'טיפול בקבצים' והיא פועלת על סמך המאפיין file_handlers במניפסט של אפליקציית האינטרנט ובתור ההפעלה, שמאפשר לאפליקציה להשתמש בקובץ שהועבר.

פתיחת קובץ משולחן העבודה עם אפליקציית SVGcode מותקנת.
window.launchQueue.setConsumer(async (launchParams) => {
  if (!launchParams.files.length) {
    return;
  }
  for (const handle of launchParams.files) {
    const file = await handle.getFile();
    if (file.type.startsWith('image/')) {
      const blobURL = URL.createObjectURL(file);
      inputImage.addEventListener(
        'load',
        () => {
          URL.revokeObjectURL(blobURL);
        },
        {once: true},
      );
      inputImage.src = blobURL;
      await set(FILE_HANDLE, handle);
      return;
    }
  }
});

תוכלו לקרוא מידע נוסף במאמר איך לאפשר לאפליקציות אינטרנט מותקנות להיות רכיבי handler של קבצים, ולראות את קוד המקור ב-src/js/filehandling.js.

שיתוף אינטרנט (קבצים)

דוגמה נוספת להשתלבות עם מערכת ההפעלה היא תכונת השיתוף של האפליקציה. בהנחה שאני רוצה לערוך קובץ SVG שנוצר עם SVGcode, אחת הדרכים להתמודד עם זה היא לשמור את הקובץ, להפעיל את אפליקציית העריכה בפורמט SVG ואז לפתוח משם את קובץ ה-SVG. עם זאת, תהליך חלק יותר הוא שימוש ב-Web Share API, שמאפשר לשתף קבצים באופן ישיר. כך שאם אפליקציית העריכה בפורמט SVG היא יעד שיתוף, היא יכולה לקבל את הקובץ ישירות ללא סטייה.

shareSVGButton.addEventListener('click', async () => {
  let svg = svgOutput.innerHTML;
  svg = await optimizeSVG(svg);
  const suggestedFileName =
    getSuggestedFileName(await get(FILE_HANDLE)) || 'Untitled.svg';
  const file = new File([svg], suggestedFileName, { type: 'image/svg+xml' });
  const data = {
    files: [file],
  };
  if (navigator.canShare(data)) {
    try {
      await navigator.share(data);
    } catch (err) {
      if (err.name !== 'AbortError') {
        console.error(err.name, err.message);
      }
    }
  }
});
שיתוף תמונת SVG ב-Gmail.

יעד שיתוף אינטרנט (קבצים)

להיפך, SVGcode יכול לשמש גם כיעד שיתוף ולקבל קבצים מאפליקציות אחרות. כדי שהתהליך הזה יעבוד, האפליקציה צריכה לעדכן את מערכת ההפעלה באמצעות Web Share Target API לגבי סוגי הנתונים שהיא יכולה לקבל. הפעולה מתבצעת דרך שדה ייעודי במניפסט של אפליקציית האינטרנט.

{
  "share_target": {
    "action": "https://svgco.de/share-target/",
    "method": "POST",
    "enctype": "multipart/form-data",
    "params": {
      "files": [
        {
          "name": "image",
          "accept": ["image/jpeg", "image/png", "image/webp", "image/gif"]
        }
      ]
    }
  }
}

המסלול action לא קיים בפועל, אבל מטופל אך ורק ב-handler של fetch של ה-Service Worker, שמעביר את הקבצים שהתקבלו לעיבוד בפועל באפליקציה.

self.addEventListener('fetch', (fetchEvent) => {
  if (
    fetchEvent.request.url.endsWith('/share-target/') &&
    fetchEvent.request.method === 'POST'
  ) {
    return fetchEvent.respondWith(
      (async () => {
        const formData = await fetchEvent.request.formData();
        const image = formData.get('image');
        const keys = await caches.keys();
        const mediaCache = await caches.open(
          keys.filter((key) => key.startsWith('media'))[0],
        );
        await mediaCache.put('shared-image', new Response(image));
        return Response.redirect('./?share-target', 303);
      })(),
    );
  }
});
שיתוף צילום מסך ל-SVGcode.

סיכום

בסדר, הציג בפניכם סיור קצר בכמה מהתכונות המתקדמות של האפליקציה ב-SVGcode. אני מקווה שהאפליקציה הזו תוכל להיות כלי חיוני לצורכי עיבוד התמונות שלך, לצד אפליקציות מדהימות אחרות כמו Squoosh או SVGOMG.

קוד SVG זמין בכתובת svgco.de. מה עשיתי שם? אפשר לבדוק את קוד המקור שלו ב-GitHub. שימו לב: מכיוון ש-Potrace היא עם רישיון GPL, כך גם SVGcode. וזהו, וקטוריות מהנה! אני מקווה ש-SVGcode יהיה שימושי, ושחלק מהתכונות שלו יוכלו לתת השראה לאפליקציה הבאה שלכם.

אישורים

המאמר הזה נכתב על ידי Joe Medley.