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

Guide on implementing Regular Expressions in Directives for validation in Angular 8

Managing 8 different angular applications poses its unique challenges. In one of the applications, there is a directive specifically designed for validating YouTube and Vimeo URLs using regular expressions. Unfortunately, once the RegExp is declared, ther ...

"Woops! An error occurred with the message: "SassError: Could not locate the specified target selector" while working with SCSS Modules and incorporating

Currently, I am working with Next.js and utilizing SCSS Modules. To incorporate Bootstrap into my project, I opted to install it using the command npm install bootstrap. Following this installation, I went ahead and created a new file titled common.scss wi ...

Tips for aligning a custom icon to the left in a Jquery mobile header

I have successfully implemented a custom icon in the header, but it is currently displaying in the center. How can I adjust it to be positioned on the left side instead? <div data-role="header" data-theme="a"> <h3> ...

Directional Div CSS Transition

Is it feasible to determine the starting direction of a CSS transition? I've designed a div that enlarges in height when hovered over; however, it's situated at the bottom of the page, causing the scrollbar to extend downward. Ideally, I would ...

Identify when a user switches tabs within the browser and when they switch applications away from the

I am interested in understanding the behavior of the tab's visibility state when a user switches tabs in a specific browser and when they switch out of the application entirely (switching away from the browser). var visibilityState, activeTab = ( ...

Error: Unable to access unknown properties (reading 'extend')

Struggling to integrate the Vuetify library into my current Vue 3 project, encountering complications. An error message popped up post-compilation: vuetify.js?ce5b:42021 Uncaught TypeError: Cannot read properties of undefined (reading 'extend') ...

Trouble with mapping an array in my Next JS application

When working on my Next JS app, I encountered an error while trying to map an array for the nav bar. The error message reads: TypeError: _utils_navigation__WEBPACK_IMPORTED_MODULE_6___default(...).map is not a function. Here is the code snippet that trigge ...

Is there a way to enable code completion for Firebase on VS Code?

After successfully setting up Typescript for code completion following the guidelines provided in this resource, I now want to enable code completion for Firebase in VS Code. However, I am unsure of the steps to achieve this. How can I activate code compl ...

Struggling to delete event listeners in TypeScript using object-oriented programming with JavaScript

After researching the issue, I have discovered that the onMouseUp event is being fired but it is not removing the EventListeners. Many individuals facing a similar problem fail to remove the same function they added initially. Upon reading information fr ...

Sending information to a jQuery UI Dialog

I'm currently working on an ASP.Net MVC website where I display booking information from a database query in a table. Each row includes an ActionLink to cancel the booking based on its unique BookingId. Here's an example of how it looks: My book ...

Energetic flair for Vue animations

I am currently developing a VueJS sidebar component. The objective is to allow the parent to define a width and display a toggle button that smoothly slides the sidebar in and out. Here is an example: <template> <div class="sidebarContainer ...

Modifying Data with MomentJS when Saving to Different Variable

After attempting to assign a moment to a new variable, I noticed that the value changes on its own without any modification from my end. Despite various attempts such as forcing the use of UTC and adjusting timezones, the value continues to change unexpec ...

Modifying the chart width in Chart.js: A step-by-step guide

After creating a chart using Chart Js, I encountered an issue where the chart did not fit within the specified width. Adjusting the attributes of canvas proved to be ineffective, specifically with regards to the width attribute. Despite changing the value, ...

Angular2: the setTimeout function is executed just a single time

Currently, I am working on implementing a feature in Angular2 that relies on the use of setTimeout. This is a snippet of my code: public ngAfterViewInit(): void { this.authenticate_loop(); } private authenticate_loop() { setTimeout (() =& ...

React.memo is failing to stop unnecessary re-renders of a stateless functional child component that does not receive any props

There is a stateless functional component known as Title, which utilizes React-Reveal to showcase simple heading elements upon rendering. Despite having no internal state and not receiving any props from its parent component (GameHeader), the issue arises ...

javascript utilizing key inputs

Is there a way I can create a function that activates when the "A" key on my keyboard is pressed, sending a signal to nupp('/TS'), and stops sending the signal when the "A" key is released? <html> <head> <script src=\"htt ...

Strange HTML antics

On my website, I encountered an issue when trying to register without entering any information into the required fields. The errors were correctly displayed in this screenshot: However, after inserting random characters into each field and attempting to r ...

Background and checked styles for radio buttons

Thank you in advance for any assistance you can provide. I am looking to create a unique radio button design. When the radio button is not checked, I want it to display as a simple white circle. However, once it is checked, I would like it to appear eithe ...

Ajax: The response from xmlhttp.responseText is displaying the entire inner HTML rather than the specified text needed

This is my Ajax function. It is functioning correctly, however after the function is called, it returns a response containing HTML tags and required text. Response in value variable " <br/> <font size='1'> <table class='x ...

Struggling to add a border to an HTML div element

I'm completely baffled as to why I can't seem to add a border around my div. Here is the link to my jsfiddle: http://jsfiddle.net/4HnKs/1/ It's possible that I've been staring at my computer screen for too long, but no matter how hard ...