React.js watermark added to the image

LuKmanLuKman
8 min read

React.js is a popular JavaScript library that enables developers to create powerful and dynamic user interfaces. One of the features of React.js is the ability to manipulate images by adding watermarks to them. In this blog post, we will explore how to add a watermark to an image using React.js.

Watermarking an image is a popular way to protect your intellectual property, especially if the image is being shared on social media or other online platforms. A watermark is a semi-transparent image or text that is overlaid on top of another image to indicate ownership or origin. In this blog post, we will be adding a text watermark to an image using React.js.

To add a watermark to an image in React.js, we will use the HTML canvas element. The canvas element is used to draw graphics on a web page using JavaScript. We will create a canvas element and draw the original image and the watermark text on it. We will then convert the canvas to an image and display it to the user.

Prerequisites

Before we get started, ensure that you have the following prerequisites:

  • Basic knowledge of HTML, CSS, and JavaScript

  • Node.js and npm installed on your system

  • A text editor like Visual Studio Code

Getting started

Let's start by creating a new React.js project. Open your terminal and type the following command to create a new React.js project.

npx create-react-app react.js-watermark-added-to-the-image

This command creates a new React.js project with the name react.js-watermark-added-to-the-image. Navigate to the project directory by running the following command:

cd react.js-watermark-added-to-the-image

Open the project directory in your text editor. Next,

Creating a custom hook

Create a new file named useWatermarkAddedToTheImage.tsx in the src/hooks directory of your project. This file will contain the code for the watermark added logic. Here's the code for the custom hook:

import { useState } from 'react';
import { toast } from 'react-toastify';

export interface IFileProcessing {
  fileName: string;
  imgWatermarkFile: any;
  textWatermarkFile: any;
  originalFile: any;
}

const useWatermarkAddedToTheImage = () => {
  const [processedFile, setProcessedFile] = useState<IFileProcessing | null>();
  const [isProcessing, setIsProcessing] = useState<boolean>(false);

  const startFileProcessing = async (imgFile: any) => {
    try {
      if (!imgFile?.name) throw new Error('Please select a valid image file.');

      setIsProcessing(true);

      let item: IFileProcessing = {
        fileName: imgFile?.name,
        imgWatermarkFile: null,
        textWatermarkFile: null,
        originalFile: imgFile,
      };

      const imgWatermarkFile = await imgWatermark(imgFile, '/logo192.png');
      const textWatermarkFile = await textWatermark(imgFile, 'React.js');

      setProcessedFile({ ...item, imgWatermarkFile, textWatermarkFile });
    } catch (error) {
      toast.error(`${error}`);
    } finally {
      setIsProcessing(false);
    }
  };

  return [(imgFile: any) => startFileProcessing(imgFile), processedFile, isProcessing] as const;
};

const imgWatermark = async (imgOriginal: File, watermarkImagePath: any, quality = 0.75) => {
  return new Promise((resolve, reject) => {
    let canvas: any = document.createElement('canvas');
    canvas.imageSmoothingQuality = 'medium'; // [low, medium, high] Reference:https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingQuality

    let context: any = canvas.getContext('2d');

    let img = document.createElement('img');
    img.src = URL.createObjectURL(imgOriginal);

    img.onload = async () => {
      canvas.width = img.width;
      canvas.height = img.height;

      // initializing the canvas with the original image
      context.drawImage(img, 0, 0, canvas.width, canvas.height);

      // loading the watermark image and transforming it into a pattern
      const result = await fetch(watermarkImagePath);
      const blob = await result.blob();
      const image = await createImageBitmap(blob);
      const pattern = context.createPattern(image, 'no-repeat');

      // translating the watermark text to the top left corner
      context.translate(25, 25);
      context.rect(0, 0, canvas.width, canvas.height);
      context.fillStyle = pattern;

      context.fill();

      return context.canvas.toBlob((blob: any) => resolve(blob), 'image/jpeg', quality);
    };

    img.onerror = () => reject(`${imgOriginal.name} is invalid image format.`);
  });
};

const textWatermark = async (imgOriginal: File, watermarkText: string, quality = 0.75) => {
  return new Promise((resolve, reject) => {
    let canvas: any = document.createElement('canvas');
    canvas.imageSmoothingQuality = 'medium'; // [low, medium, high] Reference:https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingQuality

    let context: any = canvas.getContext('2d');

    let img = document.createElement('img');
    img.src = URL.createObjectURL(imgOriginal);

    img.onload = async () => {
      canvas.width = img.width;
      canvas.height = img.height;

      // initializing the canvas with the original image
      context.drawImage(img, 0, 0, canvas.width, canvas.height);

      // adding a blue watermark text in the top left corner
      context.fillStyle = 'white';
      context.textBaseline = 'middle';
      context.font = 'bold 100px serif';
      context.fillText(watermarkText, 100, 100);

      return context.canvas.toBlob((blob: any) => resolve(blob), 'image/jpeg', quality);
    };

    img.onerror = () => reject(`${imgOriginal.name} is invalid image format.`);
  });
};

export default useWatermarkAddedToTheImage;

The code defines a custom hook named useWatermarkAddedToTheImage that returns a tuple of three values:

  • A function named startFileProcessing that accepts an image file as an argument and triggers the watermarking process on that file.

  • A state variable named processedFile which is initially set to null, but gets updated with the processed image files (with two different types of watermarks).

  • A state variable named isProcessing which is initially set to false, but gets updated to true during the watermarking process.

The hook internally uses two async functions named imgWatermark and textWatermark to add image and text watermarks to the given image file. The hook utilizes the useState and toast hooks from react and react-toastify packages, respectively.

const imgWatermark = async (imgOriginal: File, watermarkImagePath: any, quality = 0.75) => {
  return new Promise((resolve, reject) => {
    let canvas: any = document.createElement('canvas');
    canvas.imageSmoothingQuality = 'medium'; // [low, medium, high] Reference:https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingQuality

    let context: any = canvas.getContext('2d');

    let img = document.createElement('img');
    img.src = URL.createObjectURL(imgOriginal);

    img.onload = async () => {
      canvas.width = img.width;
      canvas.height = img.height;

      // initializing the canvas with the original image
      context.drawImage(img, 0, 0, canvas.width, canvas.height);

      // loading the watermark image and transforming it into a pattern
      const result = await fetch(watermarkImagePath);
      const blob = await result.blob();
      const image = await createImageBitmap(blob);
      const pattern = context.createPattern(image, 'no-repeat');

      // translating the watermark text to the top left corner
      context.translate(25, 25);
      context.rect(0, 0, canvas.width, canvas.height);
      context.fillStyle = pattern;

      context.fill();

      return context.canvas.toBlob((blob: any) => resolve(blob), 'image/jpeg', quality);
    };

    img.onerror = () => reject(`${imgOriginal.name} is invalid image format.`);
  });
};

The above code defines a function called imgWatermark that applies a watermark image onto another image. The function takes three parameters: imgOriginal is the original image that will have the watermark applied, watermarkImagePath is the path to the watermark image file, and quality is an optional parameter that determines the quality of the resulting image.

The function creates a new canvas element, loads the original image onto it, and then loads the watermark image, transforms it into a pattern, and applies it to the canvas. Finally, it converts the canvas into a blob and returns it as a Promise.

Note that this function appears to be designed to be run in a browser environment, as it references several browser-specific objects and methods, such as document.createElement, URL.createObjectURL, and createImageBitmap.

const textWatermark = async (imgOriginal: File, watermarkText: string, quality = 0.75) => {
  return new Promise((resolve, reject) => {
    let canvas: any = document.createElement('canvas');
    canvas.imageSmoothingQuality = 'medium'; // [low, medium, high] Reference:https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingQuality

    let context: any = canvas.getContext('2d');

    let img = document.createElement('img');
    img.src = URL.createObjectURL(imgOriginal);

    img.onload = async () => {
      canvas.width = img.width;
      canvas.height = img.height;

      // initializing the canvas with the original image
      context.drawImage(img, 0, 0, canvas.width, canvas.height);

      // adding a blue watermark text in the top left corner
      context.fillStyle = 'white';
      context.textBaseline = 'middle';
      context.font = 'bold 100px serif';
      context.fillText(watermarkText, 100, 100);

      return context.canvas.toBlob((blob: any) => resolve(blob), 'image/jpeg', quality);
    };

    img.onerror = () => reject(`${imgOriginal.name} is invalid image format.`);
  });
};

The above code defines a function called textWatermark that applies a text watermark onto an image. The function takes three parameters: imgOriginal is the original image that will have the watermark applied, watermarkText is the text to be used as the watermark, and quality is an optional parameter that determines the quality of the resulting image.

The function creates a new canvas element, loads the original image onto it, and then adds the specified watermark text onto the canvas. The text is positioned at coordinates (100, 100) with a font size of 100 pixels, using a bold serif font with white fill color.

Finally, the function converts the canvas into a blob and returns it as a Promise.

Note that this function appears to be designed to be run in a browser environment, as it references several browser-specific objects and methods, such as document.createElement, URL.createObjectURL, and canvas.toBlob.

Summary

In this blog post, we explore how to add watermarks to images in a React application using the HTML5 Canvas API and the useState and useEffect hooks. Specifically, we demonstrate how to add two types of watermarks to an image: an image watermark (e.g., a logo image) and a text watermark (e.g., a copyright notice).

By the end of this blog post, readers should have a good understanding of how to use watermarks to process images in a React application and be able to apply this knowledge to their own projects.

I hope you found this tutorial helpful! If you have any questions or feedback, feel free to let me know.

Download full source code from GitHub

0
Subscribe to my newsletter

Read articles from LuKman directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

LuKman
LuKman