Wklejanie obrazów

Harry Teodoulou
Harry Theodoulou

Nowoczesny sposób

Korzystanie z interfejsu Async Clipboard API

Aby automatycznie odczytywać obrazy ze schowka użytkownika, czyli po kliknięciu przycisku, możesz użyć metody read() interfejsu Async Clipboard API. Jeśli uprawnienia do odczytu ze schowka nie zostały jeszcze przyznane, wywołanie metody navigator.clipboard.read() zażąda ich przy pierwszym wywołaniu metody.

const pasteButton = document.querySelector('#paste-button');

pasteButton.addEventListener('click', async () => {
  try {
    const clipboardItems = await navigator.clipboard.read();
    for (const clipboardItem of clipboardItems) {
      const imageTypes = clipboardItem.types.find(type => type.startsWith('image/'))
      for (const imageType of imageTypes) {
        const blob = await clipboardItem.getType(imageType);
        // Do something with the image blob.
      }
    }
  } catch (err) {
    console.error(err.name, err.message);
  }
});

Obsługa przeglądarek

  • 86
  • 79
  • 13.1

Źródło

Klasyczny sposób

Posłuchaj zdarzenia paste

Klasycznym sposobem wklejania obrazów jest użycie (synchronicznego) interfejsu Clipboard API, który zapewnia dostęp do pola clipboardData w wydarzeniu Dokument: paste. Ta metoda ma jednak pewne ograniczenia, np. dlatego, że jest synchroniczna, więc wklejenie dużej ilości danych może zablokować stronę.

document.addEventListener('paste', async (e) => {
  e.preventDefault();

  for (const clipboardItem of e.clipboardData.files) {
    if (clipboardItem.type.startsWith('image/')) {
      // Do something with the image file.
    }
  }
});

Obsługa przeglądarek

  • 66
  • 79
  • 63
  • 13.1

Źródło

Stopniowe ulepszanie

W przypadku przeglądarek, które nie obsługują interfejsu Async Clipboard API, nie można automatycznie uzyskać dostępu do schowka użytkownika (na przykład przez kliknięcie przycisku). Aby uzyskiwać dostęp do schowka użytkownika w przypadku zdarzenia paste, możesz użyć interfejsu Async Clipboard API i skorzystać z (synchronicznego) interfejsu Clipboard API.

Obiekty ClipboardItem pochodzące z navigator.clipboard.read mają pole types, które jest tablicą, a obiekty File z event.clipboardData.files mają pole type, które jest ciągiem znaków. Możesz warunkowo sprawdzać poszczególne pola type i types pod kątem obrazów w schowku:

document.addEventListener('paste', async (e) => {
  e.preventDefault();
  const clipboardItems = typeof navigator?.clipboard?.read === 'function' ? await navigator.clipboard.read() : e.clipboardData.files;

  for (const clipboardItem of clipboardItems) {
    let blob;
    if (clipboardItem.type?.startsWith('image/')) {
      // For files from `e.clipboardData.files`.
      blob = clipboardItem
      // Do something with the blob.
    } else {
      // For files from `navigator.clipboard.read()`.
      const imageTypes = clipboardItem.types?.filter(type => type.startsWith('image/'))
      for (const imageType of imageTypes) {
        blob = await clipboardItem.getType(imageType);
        // Do something with the blob.
      }
    }
  }
});

Więcej informacji

  • Clipboard API w MDN
  • Odblokowywanie dostępu do schowka na stronie web.dev

Prezentacja

HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link
      rel="icon"
      href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🎉</text></svg>"
    />
    <title>How to paste images</title>
  </head>
  <body>
    <h1>How to paste images</h1>
    <p>Hit <kbd>⌘</kbd> + <kbd>v</kbd> (for macOS) or <kbd>ctrl</kbd> + <kbd>v</kbd>
      (for other operating systems) to paste images anywhere in this page.
    </p>
  </body>
</html>

CSS


        :root {
  color-scheme: dark light;
}

html {
  box-sizing: border-box;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  margin: 1rem;
  font-family: system-ui, sans-serif;
}

button {
  display: block;
}
        

JS


        document.addEventListener('paste', async (e) => {
  e.preventDefault();
  const clipboardItems = typeof navigator?.clipboard?.read === 'function' ? await navigator.clipboard.read() : e.clipboardData.files;

  for (const clipboardItem of clipboardItems) {
    let blob;
    if (clipboardItem.type?.startsWith('image/')) {
      // For files from `e.clipboardData.files`.
      blob = clipboardItem
      // Do something with the blob.
      appendImage(blob);
    } else {
      // For files from `navigator.clipboard.read()`.
      const imageTypes = clipboardItem.types?.filter(type => type.startsWith('image/'))
      for (const imageType of imageTypes) {
        blob = await clipboardItem.getType(imageType);
        // Do something with the blob.
        appendImage(blob);
      }
    }
  }
});

const appendImage = (blob) => {
  const img = document.createElement('img');
  img.src = URL.createObjectURL(blob);
  document.body.append(img);
};