Front-End Development VideoJS and React: A Perfect Match for Modern Video Players React Optimization 101: Tips and Tricks Integrating Cloudinary with Astro Building an Image Upload Feature with JavaScript Mastering Image Alignment: Centering Images with HTML & CSS Adding Video to Your React Native App with react-native-video HTML Image Slider: Do It Yourself and 1-Step Image Gallery Widget How to Effectively Manage Digital Assets in a PHP Image Gallery Introducing Angular Image Editor: Your New Editing Too Mastering Javascript Image Annotation Mastering JavaScript Image Popup Python Video Player: 3 Free Options and a Quick Tutorial Image Recognition Machine Learning: Use Cases and Common Algorithms HTML/CSS: How to Center Images Vertically and Horizontally How to Create an Image Map Understand CSS Background Position with 4 Simple Examples Java for Image Processing: 4 Libraries You Should Know Python Video Processing: 6 Useful Libraries and a Quick Tutorial Blur Image CSS: Two Ways to Blur Images for Gorgeous Effects Designing a Video Flipping App for Android Build an App for Embedding Video Watermarks on Android Devices Change Image on Hover with HTML and CSS How to Align Images with CSS Full Page Background Image with CSS: Tutorial and 5 Automation Tips Using CSS to Scale Page Elements and a Better Way to Scale Your Images CSS Background Image: Quick Tutorial and 3 Automation Tips Featured Image: Best Practices to Feature Images on Your Website Image Gallery Websites: Tips and Tricks for a Stunning Image Gallery 6 Ways to Stretch a Background Image with CSS Auto Cropping for Images and Video: Features & Best Practices FLAC vs. WAV: 4 Key Differences and How to Choose Converting Audio to Video: A Practical Guide FLAC vs. AIFF: 5 Key Differences and How to Choose FLAC vs. MQA: 5 Key Differences and How to Choose Converting WAV Files To OGG The Ultimate Guide On Converting OGG Files To WAV Sound Choices: FLAC vs. MP3 AAC vs MP3 – The Future of Audio Files All about AIFF and how it compares to WAV and MP3 Integrating Cloudinary with Netlify Integrating Cloudinary with Svelte and SvelteKit Integrating Cloudinary with Nuxt Integrating Cloudinary with Gatsby File Upload as a Service: How It Works and 5 Leading Solutions Native Mobile App Development Creative Uses for CSS Inner Border and 3 Ways to Set a Border Integrating Cloudinary with Next.js Front-End Development: The Complete Guide

Introducing Angular Image Editor: Your New Editing Too

angular image editor

Photo editing apps play a significant role in our digital lives. Over 4 in 10 smartphone users regularly use photo editing apps, enhancing their photos and selfies before sharing them online. Whether you’re building a personal website, an e-commerce platform, or a content-rich application, image editing capabilities play a crucial role. That’s where an image editor comes into play—a versatile component that empowers developers to integrate image editing features into their Angular applications.

In this article, we’ll create an image editor in Angular. From resizing and rotating to adding attractive filters, this tool can simplify image manipulation while utilizing the robust capabilities of Cloudinary, a leading cloud-based media management platform.

In this article:

angular image editor

Features of Angular Image Editors

Angular Image Editors are powerful tools designed to integrate seamlessly into web applications, providing sophisticated features for manipulating images directly within the browser. Here’s a glimpse into what these editors can typically offer:

  • Layer Management – Enables the addition, removal, and adjustment of multiple layers within an image, akin to professional-grade software.
  • Undo/Redo Commands – Essential for a forgiving editing process, allowing users to experiment without fear of making irreversible mistakes.
  • Real-time Previews – Offers an immediate visual feedback loop, ensuring adjustments are seen instantaneously.
  • Customizable Filters and Effects – These editors let users easily enhance their images with basic brightness and contrast adjustments and more complex effects like blurs and color corrections.
  • Annotation Tools – A suite of annotation options, including text addition, shapes, and drawing tools for detailed image markups.
  • Placeholders/icons: Provides placeholder images/icons for intros, error states, or building out UI mockups.
  • Responsive Design – Ensures the editor’s interface is accessible and user-friendly across all device sizes, from desktops to mobile phones.
  • Integration Capabilities – Designed to fit smoothly into Angular applications, these editors can be easily plugged into existing projects, enhancing their functionality without disrupting workflow.

Leveraging these features, Angular Image Editors empower developers to provide end-users with a rich, interactive experience directly in their web applications. By offering various tools and options, they bridge the gap between professional image editing and web-based applications, meeting the growing demand for in-browser creativity and customization.

angular image editor

Creating An Angular Image Editor

For this tutorial, we will create our image editor using Cloudinary’s various image transformation features. If you haven’t already, you can sign up for free. For now, we will use our Cloudinary API credentials, so head over to Cloudinary and log in to your account.

Next, click the Programmable Media button on your Cloudinary Programmable Media Dashboard. Copy these credentials, as we’ll need them to connect to Cloudinary’s cloud:

angular image editor

With this, we can begin creating our Angular app.

Setting Up and Creating Our Angular Image Editor

Before we begin creating our image editor, we will need to install Angular. To do this, simply run the following command:

npm install -g @angular/cli@17

Next, open up a sample project directory and use the following command to create a project:

ng new image-editor

This will create a new image-editor directory and generate the initial project structure. Navigate to the image-editor directory and open up the project in your IDE. Here, we will begin by installing the Cloudinary Angular JS SDK using the following command:

npm i @cloudinary/url-gen @cloudinary/ng

With this, our setup is complete, and we can begin coding our image editor. We will start by opening up the app.component.ts file in your project’s src/app directory. Here, we will first add some imports that will allow us to access Cloudinary’s features:

import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';
import { CloudinaryModule } from '@cloudinary/ng';
import { Cloudinary, CloudinaryImage } from '@cloudinary/url-gen';
import { byAngle } from '@cloudinary/url-gen/actions/rotate';
import { fill } from '@cloudinary/url-gen/actions/resize';
import { sepia, grayscale, cartoonify } from '@cloudinary/url-gen/actions/effect';

Next, we will add a simple component decorator and start defining our AppComponent class:

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, RouterOutlet, CloudinaryModule],
  templateUrl: './app.component.html',
})

Now, we will begin by creating two variables, img!, to store the Cloudinary image object, and imageId, to store the public ID of our image. Then, we declare a Cloudinary instance and initialize our Cloudinary API:

export class AppComponent implements OnInit {
  img!: CloudinaryImage;
  imageId: string = 'cld-sample';
  cld: Cloudinary; // Declare a Cloudinary instance

  constructor() {
    // Initialize Cloudinary configuration
    this.cld = new Cloudinary({
      cloud: {
        cloudName: 'your_cloud_name',
        apiKey: 'your_api_key',
        apiSecret: 'your_api_secret',
      },
    });

Next, we will use the ngOnInit() hook to load a default image and resize it to a width and height of 300 and 200, respectively:

  ngOnInit() {
    // Load the default image and resize it
    this.img = this.cld.image(this.imageId);
    this.img = this.cld.image(this.imageId).resize(fill().width(300).height(200));
  }

Now, we will create a loadImage() function that takes the public ID of an image as a parameter and loads it onto the app.

  loadImage(imageId: string) {
    this.imageId = imageId; // Update the imageId property
    this.img = this.cld.image(imageId);
    this.img = this.cld.image(this.imageId).resize(fill().width(300).height(200)); // Default dimensions of image
  }

Finally, we will define a transformImage() function that will use the effects selected on our app to transform our loaded image. Here is what our complete app.component.ts file looks like:

import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';
import { CloudinaryModule } from '@cloudinary/ng';
import { Cloudinary, CloudinaryImage } from '@cloudinary/url-gen';
import { byAngle } from '@cloudinary/url-gen/actions/rotate';
import { fill } from '@cloudinary/url-gen/actions/resize';
import { sepia, grayscale, cartoonify } from '@cloudinary/url-gen/actions/effect';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, RouterOutlet, CloudinaryModule],
  templateUrl: './app.component.html',
})
export class AppComponent implements OnInit {
  img!: CloudinaryImage;
  imageId: string = 'cld-sample';
  cld: Cloudinary; // Declare a Cloudinary instance

  constructor() {
    // Initialize Cloudinary configuration
    this.cld = new Cloudinary({
      cloud: {
        cloudName: 'your_cloud_name',
        apiKey: 'your_api_key',
        apiSecret: 'your_api_secret',
      },
    });
  }

  ngOnInit() {
    // Load the default image and resize it
    this.img = this.cld.image(this.imageId);
    this.img = this.cld.image(this.imageId).resize(fill().width(300).height(200));
  }

  loadImage(imageId: string) {
    this.imageId = imageId; // Update the imageId property
    this.img = this.cld.image(imageId);
    this.img = this.cld.image(this.imageId).resize(fill().width(300).height(200)); // Default dimensions of image
  }

  transformImage() {
    const selectedEffect = (<HTMLInputElement>document.querySelector('input[name="effect"]:checked')).value;
    const selectedRotation = parseInt((<HTMLInputElement>document.querySelector('input[name="rotate"]:checked')).value);
    const width = parseInt((<HTMLInputElement>document.querySelector('#imageWidth')).value);
    const height = parseInt((<HTMLInputElement>document.querySelector('#imageHeight')).value);


    switch (selectedEffect) { // Apply effect transformation
      case 'cartoonify':
        this.img = this.cld.image(this.imageId).effect(cartoonify())
        break;
      case 'sepia':
        this.img = this.cld.image(this.imageId).effect(sepia())
        break;
      case 'grayscale':
        this.img = this.cld.image(this.imageId).effect(grayscale())
        break;
      case 'none':
        break;
    }


  if (selectedRotation !== 0) { // Apply rotation
    this.img = this.img.rotate(byAngle(selectedRotation));
  }


  if (!isNaN(width) && !isNaN(height)) { // Resize the image
    this.img = this.img.resize(fill().width(width).height(height));
  }
}
}

Now that our backend is complete, all we need to do is create a UI for our image editor. To do this, open up the app.component.html file. Here, we will begin by linking Bootstrap for styling the layout and form elements:

<main class="main d-flex justify-content-center align-items-center flex-column">
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
  <style>
      .image-form {
          margin: 10px;
      }
      fieldset {
          border: 1px solid #ccc;
          padding: 10px;
          margin-bottom: 10px;
      }
      legend {
          font-weight: bold;
      }
      .form-check {
          margin-bottom: 5px;
      }
  </style>

Next, we define an input field for the Cloudinary image ID and its corresponding load button, allowing the user to enter the public ID of their Cloudinary image. Next, we use the advanced-image custom component. This component displays the loaded image with transformations applied.

Additionally, we provide user control via two fieldsets with radio buttons. The first allows selecting an effect (sepia, cartoonify, grayscale, or none), while the second lets users choose a rotation angle (0°, 90°, or -90°). Finally, we offer two input fields for custom sizing for the desired width and height alongside a transform button that triggers the transformImage() function to apply the selections. Here’s what our complete HTML file looks like:

<main class="main d-flex justify-content-center align-items-center flex-column">
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
  <style>
      .image-form {
          margin: 10px;
      }
      fieldset {
          border: 1px solid #ccc;
          padding: 10px;
          margin-bottom: 10px;
      }
      legend {
          font-weight: bold;
      }
      .form-check {
          margin-bottom: 5px;
      }
  </style>

  <!-- Input field for Cloudinary image ID -->
  <div class="image-form">
      <input class="m-2" type="text" placeholder="Enter Cloudinary image ID" #imageIdInput>
      <button class="btn btn-outline-secondary" type="button" (click)="loadImage(imageIdInput.value)">Load Image</button>
  </div>

  <!-- Display the loaded image -->
  <advanced-image [cldImg]="img"></advanced-image>

  <!-- Effect selection -->
  <fieldset>
      <legend>Effect</legend>
      <div role="group" aria-label="Basic radio toggle button group">
        <div class="form-check">
          <input type="radio" class="form-check-input" id="sepia" name="effect" value="sepia" autocomplete="off">
          <label class="form-check-label" for="sepia">Sepia</label>
        </div>
        <div class="form-check">
          <input type="radio" class="form-check-input" id="cartoonify" name="effect" value="cartoonify" autocomplete="off">
          <label class="form-check-label" for="cartoonify">Cartoonify</label>
        </div>
        <div class="form-check">
          <input type="radio" class="form-check-input" id="grayscale" name="effect" value="grayscale" autocomplete="off">
          <label class="form-check-label" for="grayscale">Grayscale</label>
        </div>
        <div class="form-check">
          <input type="radio" class="form-check-input" id="none" name="effect" value="none" autocomplete="off">
          <label class="form-check-label" for="none">None</label>
        </div>
      </div>
  </fieldset>

  <!-- Rotation selection -->
  <fieldset>
      <legend>Rotate</legend>
      <div role="group" aria-label="Rotation radio toggle button group">
        <div class="form-check">
          <input type="radio" class="form-check-input" id="rotate0" name="rotate" value="0" autocomplete="off">
          <label class="form-check-label" for="rotate0">0°</label>
        </div>
        <div class="form-check">
          <input type="radio" class="form-check-input" id="rotate90" name="rotate" value="90" autocomplete="off">
          <label class="form-check-label" for="rotate90">90°</label>
        </div>
        <div class="form-check">
          <input type="radio" class="form-check-input" id="rotateNeg90" name="rotate" value="-90" autocomplete="off">
          <label class="form-check-label" for="rotateNeg90">-90°</label>
        </div>
      </div>
  </fieldset>

  <!-- Input fields for width and height -->
  <div class="image-form">
    <input class="m-2" type="text" placeholder="Width" id="imageWidth">
    <input class="m-2" type="text" placeholder="Height" id="imageHeight">
  </div>


  <!-- Transform button -->
  <button class="btn btn-primary mt-2" (click)="transformImage()">Transform Image</button>
</main>

Testing Our Image Editor App

Now that our app is complete, all we need to do is run our code using the following command:

ng serve

angular image editor

Here is what our app looks like:

angular image editor

We will start by loading up an image with a public id, turtles, from our Cloudinary assets. To do this, search for turtles in the search bar and click on the Load Image button:

angular image editor

Next, we can add different transformations to our image. For example, here, we have added a Cartoonify effect and rotated the image by 90 degrees:

angular image editor

Final Thoughts

Conclusively, an image editor is more than just a tool; it’s a creative companion for developers. With its rich feature set and seamless integration with Cloudinary, you can elevate your web applications by providing users with powerful image editing capabilities. Whether you’re building a photo-sharing app, an e-commerce storefront, or a portfolio website, an image editor app in a language like Angular is your go-to solution.

So, why wait? Sign up for Cloudinary and unlock new possibilities for your projects!

More from Cloudinary:

How to Upload Images in Angular

Video Manipulations and Delivery for Angular Video Apps

QUICK TIPS
Colby Fayock
Cloudinary Logo Colby Fayock

In my experience, here are tips that can help you better implement and optimize an Angular image editor using Cloudinary:

  1. Use lazy loading for better performance
    Implement lazy loading for images within your Angular image editor to improve performance. This ensures that only the images currently being edited or viewed are loaded, reducing the initial load time and memory usage.
  2. Incorporate drag-and-drop functionality
    Enhance user experience by integrating drag-and-drop functionality for uploading images. This feature makes it easier for users to interact with the editor, allowing them to drag images directly into the editor for immediate editing.
  3. Optimize images before editing
    Automatically compress and optimize images upon upload using Cloudinary’s q_auto and f_auto transformations. This ensures that images are in the best possible format and quality for editing, without compromising on performance.
  4. Use environment variables for Cloudinary credentials
    Store your Cloudinary API credentials in environment variables instead of hardcoding them in your app. This practice enhances security and makes it easier to manage different environments (development, staging, production).
  5. Implement responsive design for the editor UI
    Ensure your image editor is fully responsive by using Angular’s Flex Layout or Bootstrap’s grid system. This guarantees a seamless experience across devices, allowing users to edit images effectively on both desktops and mobile devices.
  6. Add undo/redo functionality
    Implement undo and redo capabilities to allow users to revert or reapply changes. This feature is crucial for a better user experience, enabling users to experiment with different edits without fear of losing their progress.
  7. Leverage Angular’s change detection efficiently
    Optimize Angular’s change detection by using OnPush strategy where applicable. This minimizes unnecessary re-renders and improves the performance of your image editor, especially when handling large images or complex transformations.
  8. Cache edited images for a smoother experience
    Implement client-side caching for images that have been edited to allow users to quickly revert or view previous versions without needing to reprocess the image. This can be done using Angular’s built-in caching mechanisms or third-party libraries.
  9. Utilize Angular’s reactive forms for user inputs
    Use Angular’s reactive forms for handling user inputs such as width, height, and transformation options. This approach provides better form validation, easier state management, and a more scalable codebase.
  10. Test transformations thoroughly
    Before deploying, rigorously test all image transformations across different devices and browsers. Ensure that transformations such as resizing, rotating, and applying filters work consistently and deliver the expected results on various platforms.

By following these tips, you can build a robust, efficient, and user-friendly Angular image editor that leverages the power of Cloudinary for seamless image management and editing.

Last updated: Aug 24, 2024