Generating small image previews in JavaScript without distorting proportions

I am currently working on a client-side Drag and Drop file upload script as a bookmarklet. To prepare for the upload process, I am utilizing the File API to convert the images into base64 format and showcase them as thumbnails.

These are examples of how my current thumbnails appear. I am hoping to achieve a more square-like appearance without distorting their aspect ratio (please disregard the progress bar).

The desired outcome for the thumbnails is to have them centered and cropped based on the minimum height and width.

Is it possible to accomplish this using only JavaScript (adjusting styles through scripts)? Additionally, it's important to ensure that the solution is compatible with base64 images (after they have been converted via the file API as DATA URL).

You can view the exact set of images I have uploaded here.

Thank you in advance for any assistance provided.

Answer №1

Happy to share my solution for this problem! To achieve a JavaScript-only approach, I utilized throwaway canvas elements for the necessary tasks.

Below is the code snippet showcasing my implementation:

function adjustImageSize(url, width, height, callback, file) {
  console.log("Inside_adjustImageSize");
  var originalImage = new Image();

  originalImage.onload = (function (f) {
      return function (evt) {
        console.log("Inside_originalImage_onload");
        console.log("originalImage.width:" + originalImage.width);
        console.log("originalImage.height:" + originalImage.height);
        var canvas = document.createElement("canvas");
        canvas.width = width;
        canvas.height = height;

        if (originalImage.width == originalImage.height) {
          canvas.getContext("2d").drawImage(originalImage, 0, 0, width, height);
        } else {
          minVal = Math.min(originalImage.width, originalImage.height);
          if (originalImage.width > originalImage.height) {
            canvas.getContext("2d").drawImage(originalImage, (originalImage.width - minVal) / 2, 0, minVal, minVal, 0, 0, width, height);
          } else {
            canvas.getContext("2d").drawImage(originalImage, 0, (originalImage.height - minVal) / 2, minVal, minVal, 0, 0, width, height);
          }
        }
        callback(canvas.toDataURL(), f);
      }
    })(file);

  originalImage.src = url;
}

Since I was working directly with image files, I could utilize the Image object. However, some adjustments may be needed for others to implement this solution.

Answer №2

Instantiate a different html element (I prefer using a table, but I'm a bit old-fashioned) and position the image as a background image with CSS, something like this:

thetable { background: url('planets.jpg') 0px -150px no-repeat; width: 60 px; height: 60 px}

All credit goes to the original source: How to create CSS-sprites

Answer №3

This feature enables support for both images and videos while maintaining the media aspect ratio by creating thumbnails.

Furthermore, this functionality includes a cleanup process to prevent event leaks from persisting, ensuring smooth thumbnail image generation.

/* Example: Resize an image to 300px width 
while preserving the aspect ratio */ 
resizeMedia('/images/logo.jpg', 300, function(data)
{
    console.log(data); // The new thumbnail data URI 
});  


/* This function creates a thumbnail of an image or video 
preserving the aspect ratio.
@param (string | object) media = image file path or video object 
@param (int) width = desired width for the media 
@param (function) callBack = callback function to handle 
the generated image data URI */ 
function resizeMedia(media, width, callBack) 
{
    var self = this; 

    /* Check whether the media type is string or object
    to determine if it's an image or video */ 
    var type = typeof media === 'string'? 'image' : 'video'; 

    /* Calculate the modified height based on the update size 
    while maintaining the aspect ratio. 
    @param (int) updateSize = new width value  
    @param (int) width = current width 
    @param (int) height = current height 
    @return (object) containing the updated width and height for modification
    of the media */ 
    var getModifySize = function(updateSize, width, height) 
    { 
        var getModifyAspect = function(max, min, value)
        {
            var ratio = max / min;
            return value * ratio;
        };   

        return { 
            width: updateSize,
            height: getModifyAspect(updateSize, width, height) 
        }; 
    }; 

    /* Create a canvas element and draw the media content onto it.
    @param (object) media = image or video object 
    @param (int) width = canvas width
    @param (int) height = canvas height 
    @return (object) representing the new canvas with media content */ 
    var createCanvas = function(media, width, height)
    { 
        var canvas = document.createElement("canvas");
        var ctx = canvas.getContext("2d"); 

        canvas.width = width;
        canvas.height = height;

        ctx.mozImageSmoothingEnabled = true;
        ctx.webkitImageSmoothingEnabled = true;
        ctx.msImageSmoothingEnabled = true;
        ctx.imageSmoothingEnabled = true;

        /* Drawing the media content on load ensures its ready for use */
        ctx.drawImage(media, 0, 0, width, height);

        /* Convert the canvas to a data URI using JPEG for optimization.
        For transparency preservation, remove the MIME type to default to PNG */ 
        callBack(canvas.toDataURL('image/jpeg'));

        return canvas; 
    }; 

    if(type === 'image') 
    { 
        var img = new window.Image();
        img.crossOrigin = "anonymous";
        img.addEventListener('load', function loadImage() 
        {
            var modify = getModifySize(width, img.width, img.height);

            createCanvas(img, modify.width, modify.height); 
            img.removeEventListener('load', loadImage);
        });

        img.src = media;
    } 
    else if(type === 'video') 
    { 
        var modify = getModifySize(width, media.videoWidth, media.videoHeight);
        createCanvas(media, modify.width, modify.height);
    }
}; 

Answer №4

To add a centered square image directly onto the canvas using drawImage(), you simply need to include the following parameters:

canvas.getContext("2d").drawImage(img, sx, sy, swidth, sheight, x, y, width, height);

sx: Optional. Starting x coordinate for clipping.

sy: Optional. Starting y coordinate for clipping.

swidth: Optional. Width of the clipped image.

sheight: Optional. Height of the clipped image.

x: The x coordinate to place the image on the canvas.

y: The y coordinate to place the image on the canvas.

width: Optional. The width of the image to use (stretch or reduce).

height: Optional. The height of the image to use (stretch or reduce).

Best regards,

Pierrick

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Clearing all data in a form upon opening

Within a single portlet, I have organized two forms under separate tabs. What I am aiming for is that whenever I switch to tab 2, all fields within its associated form should automatically be cleared without the need for a button click. Even after attempti ...

Nodejs and express authentication feature enables users to securely access their accounts by verifying their identity before they

I am currently working on a straightforward registration page that needs to save user input (name, email, password) into a database. My tools for this task are express and node. What I am experimenting with is consolidating all the database operations (suc ...

Position the flex item adjacent to the initial flex item using wrap mode only if there is extra space

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <titl ...

Connecting Next.js to a Database: A Step-by-Step Guide

I am interested in developing an application similar to Samsung Health that will involve heavy querying on a database. I am unsure whether it would be more beneficial to create a custom server using Node.js (with Express.js) instead of using the integrate ...

What could be causing the absence of a background image in CSS?

When I try to view my local desktop file, the CSS background image isn't showing up. Here's the code I'm using: <!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <link rel="stylesheet" text="type/css" href="dis ...

AngularJS Error: Attempting to Access Undefined Object - Jasmine Testing

Encountering an error while running Jasmine tests when trying to mock/spy on an API call in the tests. Below is the code snippet responsible for calling the API: UploadedReleasesController.$inject = ['$log', '$scope', '$filter&apo ...

Calculate the sum of floating point or decimal numbers from a textarea using JavaScript

I'm trying to work with a text area that contains decimal/float numbers. However, the code I found online seems to be ignoring commas and periods when summing up the values. Is there a way to handle decimal/float numbers correctly in this scenario? ...

React component using Highcharts is displaying categories as numeric values instead of text

Incorporating the highcharts-react package (https://github.com/highcharts/highcharts-react) into my Laravel project has presented a unique challenge. I am attempting to fetch data from my reducer and transform it into a string to populate the categories in ...

``The presence of symlink leading to the existence of two different versions of React

Currently, I am working on a project that involves various sub custom npm modules loaded in. We usually work within these submodules, then publish them to a private npm repository and finally pull them into the main platform of the project for use. In orde ...

Comparing Yii's CHtml::link() function with the use of regular <a href=''></a> HTML tags

When using elements like yii CHtml::link, what are the recommended practices? I'm working on a button that needs to include an icon, text, respond to hover events, and be wide. Are there any benefits to using CHtml instead of a regular tag that can e ...

Transferring binary fragments to Node.js for assembly into a complete file. Creating a file

Hey there, I'm in a bit of a bind. I'm trying to send file chunks using multiple XMLHttpRequest requests and then receive these parts in Node.js to reconstruct the original file from the binary data. The issue I'm facing is that the final f ...

I'm facing an issue with the footer and header that is preventing me from properly positioning any content on the website

When attempting to include an h3 element on the website, it remains stuck at the top. I have tried using CSS properties like position, top, and left but it does not respond to them. Additionally, I experimented with a gallery template from w3schools to see ...

Configuring Jest unit testing with Quasar-Framework version 0.15

Previously, my Jest tests were functioning properly with Quasar version 0.14. Currently, some simple tests and all snapshot-tests are passing but I am encountering issues with certain tests, resulting in the following errors: console.error node_modules/vu ...

Unable to fetch data from MongoDB in Node.js when using objectid

After passing objectid of hospital 1 from Postman to this program, it only returns an empty array. However, there is data that matches that objectid. Can you assist me in resolving this issue? When attempting to debug the program in the console, it shows t ...

Utilizing the synchronous approach to access the Facebook Messenger API

My current project involves creating a basic chatbot using the Facebook Messenger API. I am encountering an issue where the messages I send are not always displayed in the correct order. How can I utilize async/await functionality to ensure that the messag ...

The focal point of a Three JS rotation

My goal is to rotate the geometry around a pivot point and set that as the new definition of the geometry. I want the current rotationZ to become the new rotationZ 0 without having to keep editing the rotationZ. This way, when I create a new rotation task ...

Issues with button padding in Firefox and Opera

Having an issue with the alignment of my buttons in HTML. In Opera, the button appears centered vertically, but in Firefox it seems like the text is slightly lower, messing up the design of my website. Below is the HTML code for the button: <input ty ...

Accelerate loading times of HTML files on Android apps

After clicking on an image, I want the corresponding HTML page to load instantly. However, there is a delay of a few seconds before the page actually appears. How can I reduce or eliminate this waiting time and ensure that the page loads as quickly as po ...

What is the method for obtaining the viewModel in a Knockout component?

I need to prepopulate a knockout component when the document is ready. This is the code I have written: function Finding(id, trigger) { var self = this; self.id = ko.observable(id); self.trigger = ko.observable(trigger); } function FindingVi ...

Running into an issue while attempting to generate functions in nodejs

I'm currently working on a function to authenticate a URL for a fetch request. However, when I attempt to call this function within the app.post callback, my Node.js server throws an error: "TypeError: authenticateUrl(...) is not a function". Does Nod ...