Build an Image Generator with html2canvas


March 18, 2016

marco-mag-blog.jpg

 

From the countless riffs on a photo of Jeb Bush pointing in front of a green screen during a campaign stop late last year to an image of Donald Trump's lips Photoshopped over his eyes that went viral last month (disturbingly, he looks exactly the same), there's huge potential in image editing for sharing messages and poking fun at political opponents.

With the enormous ecosystem of photo-editing tools available, it's no surprise that photo manipulation has become a weapon of choice for aspiring political satirists this election cycle. But the high cost of professional tools like Photoshop and the prohibitive learning curve of graphic design raise the barriers to entry to produce images like these. Many developers, sensing opportunity in this knowledge gap, have built tools that make it easier to caption images and produce commonly shared memes.

When Time Magazine put Marco Rubio on the cover in February, calling him "The Republican Savior," the image was just begging for snarky photoshop shenanigans. I put together an image generator in CodePen allowing users to make their own Marco cover. If you've got some front-end development skill, you can build an image generator, too (this tutorial assumes some familiarity with HTML, CSS, JavaScript, and jQuery). A jQuery plugin called html2canvas makes them relatively straightforward to build.

Here's how.


Step 1: Preparation and dependencies

We'll need to create an HTML file, a CSS stylesheet, and a JavaScript script. We'll need to link the stylesheet and script in our HTML, along with two dependencies—jQuery and html2canvas.

Fortunately, CloudFlare's cdnjs.com provides both dependencies as CDNs. So we can put them right into our HTML:

  
    
    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
     <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.js"></script>
     <script type="text/javascript" src="script.js"></script>
     <link rel="stylesheet" type="text/css" href="style.css">
  

Now we're ready to start building the HTML structure.


Step 2: HTML

Before we dive into the jQuery, let's think about all the pieces we'll need for our generator and have them ready-built so they're good to go when we start implementing the script.

We want the user to be able to type a message, watch as that message is dynamically appended to the background image, and then press a button to take a 'screenshot' that turns the message and the background image into a new image the user can save.

So, we're going to need:

  • An input where the user can type in their message and submit it to generate the image
  • An image preview section where text from the input is appended as the user types, so the user can see what their image will look like
  • A method of delivering the newly created image to the user—either in a new browser tab or in a section of the current tab

For that last one, in order to avoid dealing with security restraints involved in opening a new tab on a user's browser, we'll use a simple lightbox overlay that shows the new image.

  
    
  <form class="message-form">
        <input type="text" class="message-box"/>
        <button type="button" class="message-submit-button">
          Save PNG Image
        </button>
      </form>
      <div class="image-preview-wrap">
        <div class="image-preview">
          <p class="image-message">
            Default text here
          </p>
        </div>
      </div>
      <div class="lightbox">
      <a href="#" class="close-lightbox">&times;</a>
      <img src="" class="new-image"/>
      </div>
  

The CSS to make this work is fairly simple. We'll need a background-image on the and some font and color styles on the to make the generated image look interesting.

We'll also need to set the .lightbox div to display: none; position: fixed; z-index: 99 to make sure that it's hidden by default and that, when we use jQuery to show it, it appears on top of the rest of the page.

A word of warning: certain styles can mess with html2canvas's ability to render the image preview properly. For example, setting background-size: cover on a background image on or within the preview element can cause that background image to appear transparent or solid black in the image html2canvas creates. If you're getting similar errors, try playing with the styles, and especially consider testing with a simpler version of any dynamic or experimental styles, which may be less likely to render.


Step 3: jQuery

Now that we have our HTML structured, let's think about what we need our script to accomplish. There are 6 primary tasks:

  • Appending text from our message box to our image preview in real time
  • Preventing our form from triggering the default submit event so we can hijack it with jQuery to build our new image instead
  • Using a listener on the submit button to tell html2canvas to construct our image
  • Displaying our lightbox
  • Outputting the image html2canvas gives us as a source for the blank image in our lightbox
  • And finally, attaching a listener on the lightbox's close button so users can return to the app after they've created an image

To accomplish that, we'll need a couple blocks of jQuery. We'll use this to append text to the image preview:

  
  $('.message-box').keyup(
    function(){
      var value = $(this).val();
      $('.image-message').text(value);
    }
  );
  

jQuery is smart enough to know whether we intend to use its functions to grab an element's content or replace it with new content. So all we need to do is attach a listener to the input box that triggers on every keystroke. Each time a user hits a key, jQuery will look at the contents of the input and append them as text inside the element in our image preview.

Next, we'll need a code snippet that cancels the default response when a user hits the submit button:

  
  $('.message-form').submit(function(e){
    e.preventDefault;
    return false;
  });
  

By default, browsers will assume you want to send the contents of the form's inputs somewhere and load a new page when a form's submit button is triggered. But html2canvas actually doesn't look at our input boxes at all. It's entirely focused on making a copy of the image preview area. So we want to override the default response and replace it with our own script.

Our final two code blocks involve attaching an .on('click') event listener to the submit button and lightbox close button. Because we need to wait until the page has loaded until we can do so, we need to wrap both of these blocks in a $(document).ready function like this:

  
  $(document).ready(function(){
    [ First code block goes here ]
    [ Second code block goes here ]
  });
  

First, we need to make the image, swap it in for the missing image in the lightbox, and display the lightbox.

When the submit button is clicked, we'll trigger the html2canvas function, which we set as an argument in jQuery's .on('click') function. The html2canvas function takes two arguments: the element we want to convert into a canvas, and an object that allows us to configure options. The onrendered key allows us to set as its value a callback function that waits until the rendering is complete and then triggers, with the canvas that html2canvas has created passed through as its argument:

  
  $('.message-submit-button').on('click', function(){
    html2canvas($('.image-preview-wrap'), {
      onrendered: function(canvas) {
        var myImage = canvas.toDataURL('image/png');
        $('.lightbox').fadeIn(200);
        $('.new-image').attr('src', myImage).fadeIn(200);
      }
    });
  });
  

Now we have the element as a canvas, and although it's not yet an image, it's simple to parse it with JavaScript and return a data URI, which the user will see as a regular png image file they can save like any other. We just need to use the canvas.toDataURL function, with the file type as our argument(you can also pass through 'image/jpeg' for a jpg instead of a png.)

Then, we'll use jQuery's .fadeIn() function, which flips our lightbox element's display style on and also animates it so it fades in over a period of milliseconds we can specify. We'll also feed our new image the data URI we just created as its source by calling jQuery's .attr() function and passing in the src attribute and our data URI as arguments.

Finally, we'll add one more block to put on a listener on the lightbox's close button so the user can close the lightbox.

  
  $('.closebox').on('click', function(){
    $('.lightbox').css('display', 'none');
  });
  

And that's it! Our image generator is now ready to go.


Now for the hard part: figuring out what kind of meme to make. We used Marco Rubio's TIME cover and made a generator that lets you put your own spin on it. Click the image below to check it out on CodePen.

Screen_Shot_2016-03-02_at_6.11.28_PM.png

Let Veracity help you engage, activate, inform, and inspire your community.

Click me