Copy & Paste Images – Real-World PWA: The Making Of Paint.Js.Org – Part 3

In part three of the series about the making of the web-based Microsoft Paint clone paint.js.org, I want to show how you can copy drawings from the Paint clone to other applications and paste them back.

In diesem Artikel:

Copy & Paste Images – Real-World PWA: The Making Of Paint.Js.Org – Part 3
Christian Liebel ist Consultant bei Thinktecture. Sein Fokus liegt auf Webstandards und Progressive Web Apps. Er vertritt Thinktecture beim W3C.

In the previous part of this series, I showed how you can use the HTML canvas element to implement the drawing area. In order to copy and paste images, the pixel-based image data needs to be converted to and from an exchange format such as PNG. For the copy and paste part, I will demonstrate the use of the Async Clipboard API. This API is supported on Google Chrome, other Chromium-based browsers such as Opera or Edge since version 62, and on Apple Safari starting from version 13.1 (support table on caniuse.com). At the time of this writing, Mozilla Firefox only allows writing plain text to the clipboard.

Image to Blob

Before we can copy to another application, it needs to be converted to an exchange format. The most compatible format that can be used in combination with the Async Clipboard API across all platforms is the PNG format (Portable Network Graphics). The canvas API offers a toBlob() method that creates a binary-large object (BLOB) from the current image data of the canvas. This method takes a callback receiving the generated blob as a parameter. It also allows specifying the target format and image quality. Unless specified, the target format is PNG.

				
					canvas.toBlob(blob => {
  /* do something with the blob */
});
				
			

Copying Images to the Clipboard

The blob can now be written to the clipboard. The Async Clipboard API offers the writeText() and write() methods on the navigator’s clipboard object to copy data. While the first method is a convenient method to write plain text to the clipboard, the second one can be used to copy arbitrary data—as long as the browser or platform supports it. Currently, Safari only supports plain text, HTML, URI lists, and PNG data.

The write() method takes an array of clipboard items. They are created by calling the ClipboardItem() constructor that takes an object containing one or more representations of the item, with the representation’s MIME type as a key. The write() method returns a promise that resolves when copying was successful and rejects if it wasn’t. For security reasons, you may only invoke this action as a part of a user gesture (i.e., a keypress or a click). Depending on the browser, the user may need to give their permission first.

				
					canvas.toBlob(async (blob) => {
  await navigator.clipboard.write([
    new ClipboardItem({ [blob.type]: blob })
  ]);
});
				
			

That’s all. With the help of Async Clipboard API, you can now copy your drawing to another application—for example, the macOS app Preview.

Pasting Images from the Clipboard

Let’s say you edited the image in the other application and want to paste the result back to the application, or you took a screenshot and want to edit it in Paint. In this case, the read() method of the Async Clipboard API is used. Again, the API also provides a readText() method for convenience. Reading back the data is a little more complicated, as you need to iterate over the list of clipboard items and their representations and pick the ones that match. Again, you may only invoke this action as a part of a user gesture, the user may be asked to allow reading from the clipboard, and the supported formats vary between browsers.

The following sample iterates over the items in the clipboard and their types. Whenever it finds a clipboard item with the image/png MIME type, it retrieves the blob data by calling the getType() method on the clipboard item.

				
					const items = await navigator.clipboard.read();
for (const item of items) {
    try {
      for (const type of image.types) {
          if (type === 'image/png') {
              const blob = await item.getType(type);
              /* draw image data from blob */
          }
      }
    } catch (err) {
        console.error(err);
    }
}
				
			

Blob to Image

In order to draw the image to the canvas, its pixel data must first be extracted from the exchange format in the blob. This is where the Image() constructor comes into play. It creates a new instance of the HTMLImageElement (<img>). This can be used to load an image from a URL. The canvas API provides a drawImage() method that takes an HTMLImageElement as a parameter.

However, as the image was pasted from the clipboard, there’s no URL that could be used as the image source. Fortunately, with URL.createObjectUrl(), the web provides a method to create temporary URLs that point to a blob in memory. After the image was successfully loaded, the temporary URL is revoked to prevent memory leaks, and the image data can be drawn on the canvas using the drawImage() method.

				
					const image = new Image();
image.onload = () => {
  URL.revokeObjectURL(image.src);
  context.drawImage(image, 0, 0);
};
image.src = URL.createObjectURL(blob);
				
			

Et voilà! You can now paste images from other applications back to the Paint clone. In the sample application, this can either be done by pressing Ctrl+C/Ctrl+V, or by selecting the copy and paste entries in the application menu.

In the next part of this series, I want to show you how to save the drawings to the local file system and how to read them back. Furthermore, the Paint clone will be registered as a file handler for PNG files.

Kostenloser
Newsletter

Aktuelle Artikel, Screencasts, Webinare und Interviews unserer Experten für Sie

Verpassen Sie keine Inhalte zu Angular, .NET Core, Blazor, Azure und Kubernetes und melden Sie sich zu unserem kostenlosen monatlichen Dev-Newsletter an.

Diese Artikel könnten Sie interessieren
Angular
Implementing Smart and Presentational Components with Angular: Condensed Angular Experiences – Part 4

Implementing Smart and Presentational Components with Angular: Condensed Angular Experiences – Part 4

In this article, we will explore how to apply the concept of smart and presentational components with Angular. We will choose a complex-enough target to see all aspects in action, yet understandable and within the scope of this article. The goal is to teach you how to use this architecture in your way. For that, we will iterate through different development stages, starting with the target selection and implementing it in a naive way. After the first development, we will refactor that naive solution into smart and presentational components that are reusable, refactor-friendly, and testable.
23.01.2023
Angular
Master Web Component Forms Integration – with Lit and Angular

Master Web Component Forms Integration – with Lit and Angular

When a company has cross-framework teams, it is a good choice to use Web Components to build a unified and framework-independent component library. However, some pitfalls are to consider when integrating these components into web forms. Therefore, for a better understanding, we will look at two possible approaches and try to integrate them into an Angular form as an example.

Notice: All code samples are available on Github!
09.06.2022
Angular
About Smart and Presentational Components, Architecture Overview & Concepts: Condensed Angular Experiences – Part 3

About Smart and Presentational Components, Architecture Overview & Concepts: Condensed Angular Experiences – Part 3

Modern web technologies enable us to write huge business applications that are performant and easy to use. But with time comes complexity to our projects naturally. That added complexity sums up and makes further app development slow and cumbersome. This article discusses how a simple architectural concept can make every app more maintainable, faster to develop, and extendable in the long run.
08.04.2022
Project Fugu
Dark Mode Support – Real-World PWA: The Making Of Paint.Js.Org – Part 5

Dark Mode Support – Real-World PWA: The Making Of Paint.Js.Org – Part 5

In part five of the series about the making of the web-based Microsoft Paint clone paint.js.org, I want to show how to implement support for dark mode in your web applications.
20.05.2021
Project Fugu
Accessing Files & File Handler – Real-World PWA: The Making Of Paint.Js.Org – Part 4

Accessing Files & File Handler – Real-World PWA: The Making Of Paint.Js.Org – Part 4

In this fourth part of the series about the Microsoft Paint remake on paint.js.org, I want to demonstrate how you can save your drawings to your local disk, read them back later and how to add your web app as a handler for certain file extensions.
12.05.2021
Project Fugu
Canvas & Input – Real-World PWA: The Making Of Paint.Js.Org – Part 2

Canvas & Input – Real-World PWA: The Making Of Paint.Js.Org – Part 2

After introducing into the project about the web-based Microsoft Paint clone in the first part of this series and talking about the choice of Web Components and the architecture of paint.js.org, I now want to demonstrate how I implemented the drawing functionality.
15.04.2021