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

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

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

מ-raster לווקטור

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

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

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

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

היכרות עם קוד SVG

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

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

שימוש ב-SVGcode

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

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

Posterization ב-SVGcode

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

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

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

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

Progressive Web App

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

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

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

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

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.

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

לעומת זאת, קוד SVG יכול לשמש גם כיעד שיתוף ולקבל קבצים מאפליקציות אחרות. כדי שזה יקרה, האפליקציה צריכה להודיע למערכת ההפעלה דרך 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.