Creación de un editor de imágenes simple usando JavaScript

En este artículo, crearemos un editor de imágenes simple que se puede usar para ajustar los valores de la imagen como brillo, contraste, tono, saturación, escala de grises y sepia. Los editores de imágenes permiten editar imágenes rápidamente después de haberlas capturado para mejorarlas o cambiar su apariencia por completo. Usando una combinación de varios valores, se puede dar a una imagen un aspecto completamente nuevo. Haremos todo esto en el navegador utilizando solo HTML, CSS, JavaScript y la API de Canvas, lo que hará que la aplicación sea liviana y rápida.

Editor de imágenes sencillo

Aquí, aprenderemos cómo seleccionar un archivo de nuestro sistema local y usarlo en el lienzo para realizar cambios. También aprenderemos cómo guardar la imagen modificada en nuestro sistema.

Hemos dividido esta tarea en 3 secciones, es decir, secciones HTML, CSS y Javascript. Entenderemos la creación de Image Editor en estas secciones de manera secuencial. 

Diseño HTML: El diseño HTML define la estructura del elemento que se mostraría en la página. Esto incluye:

  • Encabezado: Contendrá el título de la aplicación y botones para guardar la imagen modificada o restablecer los filtros.
  • Vista previa de la imagen: esta sección contendrá el lienzo en el que trabajaremos. Ayudará a obtener una vista previa de la imagen mientras cambiamos los valores del filtro.
  • Controles deslizantes de filtro: esta sección tendrá todos los controles deslizantes que manejarán los valores de filtro de imagen.
  • Filtros preestablecidos: esta sección contiene un conjunto de botones que se pueden usar para configurar múltiples valores a la vez para lograr un estilo particular.
  • Área de controles de archivos: esta sección contiene un botón para seleccionar una nueva imagen del sistema de archivos.

Usaremos la biblioteca MaterializeCSS para diseñar la aplicación y posicionar el contenido usando su sistema de cuadrícula para garantizar la capacidad de respuesta móvil.

Ejemplo:

HTML

<!DOCTYPE html>
<html lang="en">
  
<head>
    <title>Simple Image Editor</title>
    <link rel="stylesheet" href=
"https://fonts.googleapis.com/icon?family=Material+Icons">
  
    <!-- Compiled and minified CSS -->
    <link rel="stylesheet" href=
"https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
    
    <!-- Import custom stylesheet -->
    <link rel="stylesheet" href="style.css">
  
    <!-- Compiled and minified JavaScript -->
    <script defer src=
"https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js">
    </script>
</head>
  
<body>
    <nav class="green">
        <div class="nav-wrapper container">
            <span class="nav-header">
              Simple Image Filters
            </span>
            <ul class="right image-save">
                <button class="btn btn-flat blue white-text"
                        onclick="saveImage()">
                  Save
                </button>
                <button class="btn btn-flat red white-text"
                        onclick="resetImage()">
                  Reset
                </button>
            </ul>
        </div>
    </nav>
  
    <!-- Hidden image that will be used for
    holding the source image -->
    <img id="sourceImage" crossorigin="anonymous">
  
    <div class="image-preview">
  
        <!-- Canvas that will hold the 
        image to be edited -->
        <canvas id="canvas" height="0"></canvas>
    </div>
    <div class="container app">
  
        <!-- Text to be shown at the beginning
        of the application -->
        <div class="help-text center-align">
            <h5>Please Upload an Image to Start Editing</h5>
        </div>
  
        <!-- All the image controls that will be
        used for modifying the image -->
        <div class="image-controls">
            <h6>Image Controls</h6>
            <div class="row">
                <div class="col s6">
                    <span class="range-field">
                        <label for="brightnessSlider">
                          Brightness
                        </label>
                        <input id="brightnessSlider"
                               type="range" value="100"
                               min="0" max="300" 
                               onchange="applyFilter()">
                    </span>
                </div>
                <div class="col s6">
                    <span class="range-field">
                        <label for="contrastSlider">
                          Contrast
                        </label>
                        <input id="contrastSlider"
                               type="range" value="100"
                               min="0" max="200" 
                               onchange="applyFilter()">
                    </span>
                </div>
            </div>
  
            <div class="row">
                <div class="col s6">
                    <span class="range-field">
                        <label for="grayscaleSlider">
                          Grayscale
                        </label>
                        <input id="grayscaleSlider" 
                               type="range" value="0"
                               min="0" max="100" 
                               onchange="applyFilter()">
                    </span>
                </div>
                <div class="col s6">
                    <span class="range-field">
                        <label for="saturationSlider">
                          Saturation
                        </label>
                        <input id="saturationSlider"
                               type="range" value="100"
                               min="0" max="300"
                               onchange="applyFilter()">
                    </span>
                </div>
            </div>
  
            <div class="row">
                <div class="col s6">
                    <span class="range-field">
                        <label for="sepiaSlider">
                          Sepia
                        </label>
                        <input id="sepiaSlider" 
                               type="range" value="0" 
                               min="0" max="200" 
                               onchange="applyFilter()">
                    </span>
                </div>
                <div class="col s6">
                    <span class="range-field">
                        <label for="hueRotateSlider">
                          Hue
                        </label>
                        <input id="hueRotateSlider"
                               type="range" value="0"
                               min="0" max="360"
                               onchange="applyFilter()">
                    </span>
                </div>
            </div>
        </div>
  
        <!-- Buttons that will be used to change
        the values to preset ones -->
        <div class="preset-filters">
            <h6>Preset Filters</h6>
            <button class="btn green" 
                    onclick="brightenFilter()">
              Brighten
            </button>
            <button class="btn green" 
                    onclick="bwFilter()">
              Black and White
            </button>
            <button class="btn green"
                    onclick="funkyFilter()">
              Funky
            </button>
            <button class="btn green" 
                    onclick="vintageFilter()">
              Vintage
            </button>
        </div>
  
        <!-- File control to upload a new file -->
        <div class="file-controls">
            <h6>File Controls</h6>
            
            <!-- Element that will be later used to download
            the canvas image from code -->
            <a id="link"></a>
            
            <!-- File Selector for uploading the image -->
            <div class="file-field input-field">
                <div class="btn green">
                    <span>Upload Image</span>
                    <input type="file" accept="image/*"
                           onchange="uploadImage(event)">
                </div>
                <div class="file-path-wrapper">
                    <input class="file-path" type="text">
                </div>
            </div>
        </div>
    </div>
    
      <!-- Load the script for the editor -->
    <script src="app.js"></script>
</body>
  
</html>

Estilo CSS: 

Haremos algunos cambios menores junto con los estilos Materialise predeterminados de acuerdo con nuestras aplicaciones. Los principales cambios incluyen:

  • Ocultar los controles deslizantes y los botones del filtro de imagen cuando no se ha seleccionado una imagen y mostrar un mensaje de ayuda en su lugar utilizando la propiedad de visualización .
  • Centrar la vista previa de la imagen mediante el uso de las propiedades Flex y Justify-Content .
  • Establecer la altura máxima del lienzo en un valor fijo y usar la propiedad de ajuste de objeto para asegurarse de que se ajuste al área determinada. Esto ayudará a utilizar la aplicación en todos los tamaños de pantalla sin que la imagen ocupe demasiado espacio.

Ejemplo:

CSS

.nav-header {
    font-size: 1.5rem;
}
  
.row {
    margin-bottom: 0;
}
  
#sourceImage,
.image-controls,
.image-save,
.preset-filters {
    display: none;
}
  
.image-preview {
    display: flex;
    justify-content: center;
    margin-top: 20px;
}
  
#canvas {
    max-height: 420px;
    object-fit: contain;
}

Sección JavaScript: 

La sección contiene la lógica principal de la aplicación. Aquí, dividiremos la lógica en 5 pasos, que se describen a continuación:

Paso 1: Definición de las variables y creación de una referencia a los elementos HTML:

Primero seleccionaremos todos los elementos que necesitan ser modificados por JavaScript usando el método querySelector(). Luego se les asignan nombres de variables para que se pueda acceder a ellos fácilmente. También se crea una referencia al lienzo y su contexto 2D para dibujar la imagen.

Javascript

// Get the source image to be edited
let image = document.getElementById('sourceImage');
  
// Get the canvas for the edited image
let canvas = document.getElementById('canvas');
  
// Get the 2D context of the image
let context = canvas.getContext('2d');
  
// Get all the sliders of the image
let brightnessSlider = document.getElementById("brightnessSlider");
let contrastSlider = document.getElementById("contrastSlider");
let grayscaleSlider = document.getElementById("grayscaleSlider");
let hueRotateSlider = document.getElementById("hueRotateSlider");
let saturateSlider = document.getElementById("saturationSlider");
let sepiaSlider = document.getElementById("sepiaSlider");

Paso 2: Cargar una imagen desde el sistema de archivos:

  • Usaremos el elemento invisible <img>, que hemos definido en el HTML como imagen de origen antes de dibujarlo en el lienzo.
  • En la función uploadImage() , primero cargaremos la imagen utilizando el método URL.createObjectURL() y la asignaremos a la propiedad src del elemento de imagen de origen mencionado anteriormente. Se puede acceder al archivo de imagen usando event.target.files[0] desde el evento de cambio del selector de archivos.
  • Luego estableceremos la altura y el ancho del lienzo para que sean iguales a las dimensiones de la imagen. También estableceremos la propiedad crossOrigin en anónimo para evitar el problema del lienzo contaminado. Luego llamaremos a la función applyFilter() para dibujar la imagen en el lienzo. Esta función se explica en el siguiente paso.
  • También haremos visibles los controles de imagen y ocultaremos el texto de ayuda que se mostró anteriormente.

Javascript

function uploadImage(event) {
  
    // Set the source of the image from the uploaded file
    image.src = URL.createObjectURL(event.target.files[0]);
  
    image.onload = function () {
        // Set the canvas the same width and height of the image
        canvas.width = this.width;
        canvas.height = this.height;
        canvas.crossOrigin = "anonymous";
        applyFilter();
    };
  
    // Show the image editor controls and hide the help text
    document.querySelector('.help-text').style.display = "none";
    document.querySelector('.image-save').style.display = "block";
    document.querySelector('.image-controls').style.display = "block";
    document.querySelector('.preset-filters').style.display = "block";
};

Paso 3: dibujar la imagen y aplicar los valores de filtro actuales.

  • La función applyFilter(): esta es la función principal que maneja el dibujo de la imagen y la aplicación de los valores de filtro. Canvas API tiene una propiedad de filtro que aplica filtros al lienzo. Como la propiedad solo se puede asignar una vez, tendremos que especificar todos los filtros a la vez para que se apliquen juntos.
  • Comenzaremos creando la string que contendrá los filtros que hemos elegido para esta aplicación. Estos son los filtros comunes que están disponibles en CSS. Obtendremos los valores actuales de los controles deslizantes usando la propiedad de valor y la usaremos en la string en los lugares correctos.
  • Luego asignaremos esta string a la propiedad de filtro del lienzo. Esto asignará aplicar los filtros al lienzo.
  • Finalmente, dibujaremos la imagen usando el método drawImage() de la API Context.

Javascript

// This function is used to update the image
// along with all the filter values
function applyFilter() {
  
    // Create a string that will contain all the filters
    // to be used for the image
    let filterString =
        "brightness(" + brightnessSlider.value + "%" +
        ") contrast(" + contrastSlider.value + "%" +
        ") grayscale(" + grayscaleSlider.value + "%" +
        ") saturate(" + saturateSlider.value + "%" +
        ") sepia(" + sepiaSlider.value + "%" +
        ") hue-rotate(" + hueRotateSlider.value + "deg" + ")";
  
    // Apply the filter to the image
    context.filter = filterString;
  
    // Draw the edited image to canvas
    context.drawImage(image, 0, 0);
}

Paso 4: Uso de filtros preestablecidos para la imagen:

  • Cada uno de los botones de filtro preestablecidos que hemos definido anteriormente se puede usar para establecer un valor determinado para cada uno de los controles deslizantes. Esto aplicará una combinación única de valores que se pueden usar para lograr rápidamente una apariencia y luego modificar aún más los valores según sea necesario.
  • Cada una de las funciones primero restablecerá la imagen usando la función resetImage() que definiremos en el siguiente paso, luego establecerá los valores de filtro requeridos para ese ajuste preestablecido y luego llamará a applyFilter() para volver a dibujar la imagen con los filtros.
  • Luego, el usuario puede cambiar los controles deslizantes después de que se haya aplicado el ajuste preestablecido para cambiar aún más el aspecto del filtro.

Javascript

// A series of functions that handle the preset filters
// Each of these will first reset the image
// and then apply a certain parameter before
// redrawing the image
function brightenFilter() {
    resetImage();
    brightnessSlider.value = 130;
    contrastSlider.value = 120;
    saturateSlider.value = 120;
    applyFilter();
}
  
function bwFilter() {
    resetImage();
    grayscaleSlider.value = 100;
    brightnessSlider.value = 120;
    contrastSlider.value = 120;
    applyFilter();
}
  
function funkyFilter() {
    resetImage();
  
    // Set a random hue rotation everytime
    hueRotateSlider.value =
        Math.floor(Math.random() * 360) + 1;
    contrastSlider.value = 120;
    applyFilter();
}
  
function vintageFilter() {
    resetImage();
    brightnessSlider.value = 120;
    saturateSlider.value = 120;
    sepiaSlider.value = 150;
    applyFilter();
}

Paso 5: restablecer y guardar la imagen:

  • Restablecer imagen: la imagen se puede restablecer configurando los valores de filtro a sus valores predeterminados y luego llamando al método applyFilter() para volver a dibujar la imagen original. Esto también ajustará los controles deslizantes a sus posiciones predeterminadas.
  • Guardar imagen: la imagen se puede guardar haciendo referencia al elemento temporal <a> que hemos creado en la sección HTML. Usaremos la propiedad de descarga para configurar el nombre del archivo y la propiedad href para configurar los datos del lienzo que se guardarán. Convertiremos el lienzo en una URL de datos usando el método toDataURL() y luego en un flujo de imágenes para que comience a descargarse automáticamente.

Javascript

// Reset all the slider values to there default values
function resetImage() {
    brightnessSlider.value = 100;
    contrastSlider.value = 100;
    grayscaleSlider.value = 0;
    hueRotateSlider.value = 0;
    saturateSlider.value = 100;
    sepiaSlider.value = 0;
    applyFilter();
}
  
function saveImage() {
    // Select the temporary element we have created for
    // helping to save the image
    let linkElement = document.getElementById('link');
    linkElement.setAttribute(
      'download', 'edited_image.png'
    );
  
    // Convert the canvas data to a image data URL
    let canvasData = canvas.toDataURL("image/png")
  
    // Replace it with a stream so that
    // it starts downloading
    canvasData.replace(
      "image/png", "image/octet-stream"
    )
  
    // Set the location href to the canvas data
    linkElement.setAttribute('href', canvasData);
  
    // Click on the link to start the download 
    linkElement.click();
}

Después de completar los pasos anteriores, el editor de imágenes está listo para usar en cualquier navegador. Puede agregar más filtros preestablecidos según su gusto o incluso usar los otros filtros CSS disponibles.

Salida :

Editor de imágenes sencillo

Publicación traducida automáticamente

Artículo escrito por sayantanm19 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *