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

SVGcode היא אפליקציית אינטרנט פרוגרסיבית שמאפשרת להמיר תמונות רסטר כמו JPG,‏ PNG,‏ GIF,‏ WebP,‏ AVIF וכו' לגרפיקה וקטורית בפורמט SVG. היא משתמשת ב-File System Access API, ב-Async Clipboard API, ב-File Handling API ובהתאמה אישית של שכבת-העל של פקדי החלון.

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

מ-raster לווקטור

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

הגדלת התמונה הרסטרית גורמת לה להיראות מפורטת מדי.

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

הגדלת התמונה ללא אובדן איכות.

חדש: SVGcode

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

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

שימוש ב-SVGcode

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

המרת תמונה שהושלכה ל-SVG.

פוסטוריזציה בקוד SVG

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

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

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

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

Progressive Web App

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

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

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

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

File System Access API

כדי לפתוח קובצי תמונות קלט ולשמור את קובצי ה-SVG שנוצרים, אני משתמש ב-File System Access API. כך אפשר לשמור על הפניה לקבצים שנפתחו בעבר ולהמשיך מהמקום שבו הפסקתם, גם אחרי טעינה מחדש של האפליקציה. בכל פעם שתמונה נשמרת, היא עוברת אופטימיזציה באמצעות הספרייה svgo. התהליך עשוי להימשך כמה רגעים, בהתאם למורכבות של קובץ ה-SVG. כדי להציג את תיבת הדו-שיח לשמירת הקובץ, צריך לבצע תנועת משתמש. לכן חשוב לקבל את ה-handle של הקובץ לפני ביצוע האופטימיזציה של קובץ ה-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);
}

גרירה ושחרור

כדי לפתוח תמונה של קלט, אפשר להשתמש בתכונה 'פתיחת קובץ', או, כפי שראיתם למעלה, פשוט לגרור ולשחרר קובץ תמונה באפליקציה. התכונה 'פתיחת קובץ' פשוטה למדי, אבל מעניינת יותר היא האפשרות של גרירה ושחרור. היתרון הגדול של השיטה הזו הוא שאפשר לקבל מאחיזת מערכת הקבצים של פריט העברת הנתונים באמצעות השיטה 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

SVGcode משולב גם באופן מלא בלוח של מערכת ההפעלה באמצעות Async Clipboard API. אפשר להדביק תמונות מהתוכנה לניהול קבצים של מערכת ההפעלה באפליקציה בלחיצה על הלחצן להדבקת תמונה או על מקש ה-Command או ה-Control ו-V במקלדת.

הדבקת תמונה מסיר הקבצים בקוד SVG.

לאחרונה נוספה ל-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'));
});

מידע נוסף זמין במאמר Async Clipboard או בקובץ 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;
    }
  }
});

מידע נוסף זמין במאמר איך מאפשרים לאפליקציות אינטרנט מותקנות להיות מנהלות קבצים, וניתן לעיין בקוד המקור ב-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 לא קיים בפועל, אבל הוא מטופל רק במטפל 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 מופץ ברישיון GPL. ושיהיה לכם ווקטוריזציה נעימה! אני מקווה ש-SVGcode יעזור לכם, ושתמצאו השראה ליצירת האפליקציה הבאה שלכם בחלק מהתכונות שלו.

תודות

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