5

I have a div that takes a user image and places user text over it. My goal is for the users to, after seeing the preview and customizing the image/text to their like, be able to download or save the image with the click of a button. Is this possible? Here's my code: (I'm new to html/css so please forgive ugly formatting/methods)

HTML:

<script `src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>`

<p>DOM-rendered</p>
<p>&nbsp;</p>

<div id="imagewrap" class="wrap" style="border-style: solid;">
  <img src="../images/environment.gif" id="img_prev" width="640" height="640" />
  <h3 class="desc">Something Inspirational</h3>
</div>

<div id="canvasWrapper" class="outer">
  <p>Canvas-rendered (try right-click, save image as!)</p>
  <p>Or, <a id="downloadLink" download="cat.png">Click Here to Download!</a>
</div>

CSS:

.desc {
text-align: center;
}

.outer, .wrap, .html2canvas, .image_text {
  display: inline-block;
  vertical-align: top;
}
.wrap {
  text-align: center;
}
#imagewrap {
  background-color: white;
}

JavaScript:

window.onload = function() {
html2canvas(document.getElementById("imagewrap"), {
onrendered: function(canvas) {
  canvas.className = "html2canvas";
  document.getElementById("canvasWrapper").appendChild(canvas);
  var image = canvas.toDataURL("image/png");
  document.getElementById("downloadLink").href = image;
},
useCORS: true
});
}

function changePicture(image) {
    var at = $(image).attr('at');
    var newpath = '../images/' + at;
    $("#img_prev").attr('src', newpath);
}


function readURL(input) {
    if (input.files && input.files[0]) {
      var reader = new FileReader();

      reader.onload = function (e) {
        $('#img_prev')
          .attr('src', e.target.result)
          .width(640)
          .height(640);
      };
      reader.readAsDataURL(input.files[0]);
    }
  };


  $(document).on("click", '.font-names li a',  function() {    
      $("#imagewrap h3").css("font-family", $(this).parent().css("font-family"));
      $("#new_tile_font_style").val($(this).parent().css("font-family"));
   });


  $(document).on("click", '.font-sizes li a',  function() {    
      $("#imagewrap h3").css("font-size", $(this).parent().css("font-size"));
      $("#new_tile_font_size").val($(this).parent().css("font-size") + "px");
    });


  $(document).on("click", '.font-colors li a',  function() {    
      $("#imagewrap h3").css("color", $(this).parent().css("color"));
      $("#new_tile_font_color").val($(this).parent().css("color"));
    });


  $("#new_tile_quote").on('keyup', function() {
   var enteredText = $("#new_tile_quote").val().replace(/\n/g, "<br>");
    $("#imagewrap h3").html(enteredText);
    });
Scott Myers
  • 226
  • 9
  • 30
  • 3
    Yes, this is possible, but not with HTML/CSS/JS. You'll need a server-side language to create your image and allow it to be downloaded. – Wes Foster Jul 22 '15 at 18:10
  • I'm unfamiliar with the concept, could you elaborate? - Thanks for quick response as well. – Scott Myers Jul 22 '15 at 18:13
  • @ScottMyers He's referring to an Html->Image converter server side software. You'd send the HTML code to the converter, and it would create the image from it, and ship it back to you. You could do it all over AJAX. There are other options in the modern browser age though. – crush Jul 22 '15 at 18:20
  • Here is a good place to start http://4rapiddev.com/php/download-image-or-file-from-url/ – Adam Buchanan Smith Jul 22 '15 at 18:24
  • @crush Thanks for elaboration. Could you maybe point me in the right direction in terms of how I might start doing this? I'm completely unfamiliar with Ajax. – Scott Myers Jul 22 '15 at 18:26
  • @ScottMyers That's a really broad topic, unfortunately, and isn't a good fit for this site. AJAX is just fancy techno-babble that describes how a browser can send additional HTTP requests to an HTTP server, and receive an HTTP response without reloading the current page. It would be one component of the equation. You'd need some type of server side solution to change the data into an image. I'd skip all that, and use one of the canvas based, client-side solutions below, if you can count on your end users having modern browsers installed. – crush Jul 22 '15 at 18:29
  • @crush alrighty, thanks so much. – Scott Myers Jul 22 '15 at 18:49
  • @WesFoster This is actually possible without using server-side code, instead using a library that will draw DOM elements to canvas. See my answer. – Maximillian Laumeister Jul 22 '15 at 21:00
  • @MaximillianLaumeister very nice. I've upvoted you – Wes Foster Jul 22 '15 at 21:10
  • You can do this with HTML5 Canvas. See [this article for an overview](http://www.html5canvastutorials.com/advanced/html5-canvas-save-drawing-as-an-image/). – Ian Jennings Jul 22 '15 at 18:19
  • Is there any way I could have canvas just use the div I have already created or do I have to re-write / re-assign everything? – Scott Myers Jul 22 '15 at 18:27
  • @ScottMyers You can use the div you have already created. See my answer for details. – Maximillian Laumeister Jul 22 '15 at 21:04

2 Answers2

2

Here's a quick demo that shows how to use JavaScript to convert your markup into a canvas, then render that into an image and replace it on the page.

document.getElementById('generate').onclick = generateImage;

function generateImage() {
    var container = document.getElementById('image_text');
    var imgPrev = document.getElementById('img_prev');
    var desc = document.getElementById('desc');
    var canvas = document.createElement('canvas');
    var context = canvas.getContext('2d');
    canvas.setAttribute('width', container.clientWidth);
    canvas.setAttribute('height', container.clientHeight);
    context.drawImage(imgPrev, 0, 0);
    context.font = "bold 20px serif";
    context.fillText(desc.innerHTML, 0, container.clientHeight-20);
    context.strokeRect(0, 0, canvas.width, canvas.height);
    var dataURL = canvas.toDataURL();
    var imgFinal = new Image();
    imgFinal.src = dataURL;
    container.parentNode.insertBefore(imgFinal, container.nextSibling);
    container.remove();
    document.getElementById('generate').remove();
}
#image_text {
    display: inline-block;
    border: 1px solid #000;
}
<div id="image_text">
  <div class="wrap">
    <img src= "https://placekitten.com/g/200/300" id="img_prev" crossorigin="anonymous" />
    <h3 id="desc" contenteditable>Something Inspirational</h3>
  </div>
</div>

<button id="generate">Generate Image</button>

You can replace the image file with anything you like. I've added a crossorigin property to the img tag, and this is because canvases that use resources from other sites cannot be exported unless a crossorigin attribute is specified (if your scripts and images are on the same domain, this is unnecessary).

I've also made the h3 tag editable. You can click on the text and start typing to change what it says, then click "generate image" and save the rendered output.

This script is just a demonstration. It is not bulletproof, it is only a proof-of-concept that should help you understand the techniques being used and apply those techniques yourself.

The javascript creates a canvas element (detached from the DOM), and sizes it according to the container div in your markup. Then it inserts the image into the canvas (it inserts it at the top-left corner), loads the text from your h3 tag and puts it near the bottom-left of the canvas, and converts that canvas to a data-uri. Then it creates a new img element after the container and deletes the container and button.

Woodrow Barlow
  • 8,477
  • 3
  • 48
  • 86
2

What you're trying to accomplish is entirely possible using just HTML, JS, and CSS, with no server-side code. Here is a simplified demo that uses the html2canvas library to render your entire DOM element to a canvas, where the user can then download it.

Be sure to click "Full page" on the demo so you can see the whole thing!

window.onload = function() {
  html2canvas(document.getElementById("imagewrap"), {
    onrendered: function(canvas) {
      canvas.className = "html2canvas";
      document.getElementById("canvasWrapper").appendChild(canvas);
      var image = canvas.toDataURL("image/png");
      document.getElementById("downloadLink").href = image;
    },
    useCORS: true
  });
}
.desc {
  text-align: center;
}
.outer, .wrap, .html2canvas, .image_text {
  display: inline-block;
  vertical-align: top;
}
.wrap {
  text-align: center;
}
#imagewrap {
  background-color: white;
}
#wow {
  color: red;
  display: block;
  transform: translate(0px, -12px) rotate(-10deg);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>
<div class="outer">
  <p>DOM-rendered</p>
  <p>&nbsp;</p>
  <div id="imagewrap" class="wrap" style="border-style: solid;">
    <img src="https://i.imgur.com/EFM76Qe.jpg?1" id="img_prev" width="170" />
    <h3 class="desc">Something <br /><span style="color: blue;">Inspirational</span></h3>
    <span id="wow">WOW!</span>
  </div>
</div>
<div id="canvasWrapper" class="outer">
  <p>Canvas-rendered (try right-click, save image as!)</p>
  <p>Or, <a id="downloadLink" download="cat.png">Click Here to Download!</a>
</div>
Maximillian Laumeister
  • 19,884
  • 8
  • 59
  • 78
  • Thanks, this works almost perfectly. The only issue is that, in my case, there will be users that update and change the image through javascript functions in real time. How could I have the user download the latest version of the image? Like a re-render function. Sorry for being a noob. Also is it possible to not display the rendered version only make it available to download? I've updated my code in the above so you can see what I'm working with. – Scott Myers Jul 23 '15 at 18:59
  • @ScottMyers To continuously re-render, call the `html2canvas` function from your code every time the user makes a change, and in the `onrendered` function, check to see if the output canvas already exists, and if so, delete it (using something like `canvas.parentNode.removeChild(canvas)`) before you append the new one. And if you don't want to display the rendered version, set the canvas element to `display: none` in CSS - simple as that! Tell me if you need any more details to get it working. – Maximillian Laumeister Jul 23 '15 at 19:08
  • Once again thanks for the quick response, I'm having trouble calling the html2canvas function on user change. Could you possibly give me a sample of code exemplifying this? Thanks so much. – Scott Myers Jul 23 '15 at 19:25
  • @ScottMyers Here's a `reRender` function that you can call every time your user changes the div. In this example, I have it attached to a "Re-Render" button, but in your code you will want to instead call it automatically whenever the user makes a change. https://jsfiddle.net/que0fe08/1/ – Maximillian Laumeister Jul 23 '15 at 19:38
  • Thanks so much for you help! – Scott Myers Jul 23 '15 at 20:03
  • @ScottMyers Glad I could help! If you feel like I answered your question completely, don't forget to mark the answer as "accepted" using the gray checkmark, and mark one or more answers as "helpful" by using the up arrows! – Maximillian Laumeister Jul 23 '15 at 20:06
  • Hey, sorry to bother again: Everything works well, however despite the display:none, or visibility:hidden, after reRendering the canvas still shows on the page. Any ideas? – Scott Myers Jul 23 '15 at 21:57
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/84124/discussion-between-maximillian-laumeister-and-scott-myers). – Maximillian Laumeister Jul 23 '15 at 22:00