0

I'm currently trying to do some Javascript work in Laserfiche forms which requires me to save the base64 data for an image in a separate text area, and feed that data back into the image to allow the canvas to be turned into an image in which I can save into the system.

The issue is I'm trying to have a background image in which the user can draw on (in this case, a vehicle that they can draw a circle on to indicate where the damage is). I am using sketch.js to allow the drawing part of the task.

From what I've read is that the background CSS cannot be saved into the canvas. That's fine but how do I pass the background image when I'm already grabbing the base64 data and passing that back into my canvas?

The saveimage class belongs to the text area and the imagefinish belong to the checkbox that they mark when the image is ready

html

<div class="demo" id="colors_demo">
  <div class="tools">
      <a href="#colors_sketch" data-tool="marker">Marker</a>         
      <a href="#colors_sketch" data-tool="eraser">Eraser</a>
      <a href="#colors_sketch" data-download="png" style="float: right; width: 100px;">Download</a>
  </div>
  <canvas id="colors_sketch" width="750" height="500" style="border:2px solid #000000 ; background: url(http://localhost/forms/img/vanImage.jpg"></canvas>
</div>
<input name="Field11" id="Field11-0" type="checkbox" value="isLocked" vo="e" data-parsley-class-handler="#Field11" data-parsley-errors-container="#Field11" data-parsley-multiple="Field11">
    <textarea id="Field13" name="Field13" aria-labelledby="Field13" class="cf-medium" rows="3" vo="e" data-parsley-id="28"></textarea>

Javascript

//submitted form
  if ($('[name=IsLocked]').val() == 'True') {
    var myCanvas = document.getElementById('colors_sketch');
    var ctx = myCanvas.getContext('2d');
    var img = new Image;
    img.onload = function() {
      ctx.drawImage(this, 0, 0, myCanvas.width, myCanvas.height);
    }
    img.src = $('.saveimage .cf-field').text();  
  }
  else {
  //fill form
  //$.getScript('http://localhost/Forms/js/sketch.js', function() {
     $.getScript('https://rawgit.com/intridea/sketch.js/gh-pages/lib/sketch.js', function() {
      //script is loaded and executed put your dependent JS here
      $.each(['#f00', '#ff0', '#0f0', '#0ff', '#00f', '#f0f', '#000', '#fff'], function() {
        $('#colors_demo .tools').append("<a href='#colors_sketch' data-color='" + this + "' style='width: 10px; background: " + this + ";'></a> ");
      });
      $.each([3, 5, 10, 15], function() {
        $('#colors_demo .tools').append("<a href='#colors_sketch' data-size='" + this + "' style='background: #ccc'>" + this + "</a> ");
      });
      //$('#colors_sketch').sketch();
      $('#colors_sketch').sketch({defaultColor: "#000"});
    });
    $('.imagefinish input').change(function(){
      if(this.checked) {
        var myCanvas = document.getElementById('colors_sketch');
        $('.saveimage textarea').val(myCanvas.toDataURL());
      }
    });
  }

I was able to add an image by adding a variable for my image path

 var image   = 'http://localhost/forms/img/vanImage.jpg'

and I also added the two lines to my onload for the style of "myCanvas". I have a feeling that this solution will only work because of how Laserfiches forms software works but the marked answer is also correct.

img.onload = function() {
      ctx.drawImage(this, 0, 0, myCanvas.width, myCanvas.height);
      myCanvas.style.backgroundRepeat = "no-repeat";
    myCanvas.style.backgroundImage = 'url('+image+')'
    }
    img.src = $('.saveimage .cf-field').text();  
  }
Alex
  • 673
  • 3
  • 9
  • 22

1 Answers1

1

Load the background image on page load, or an appropriate time and when the client is ready, draw the background onto the canvas behind the user content using ctx.globalCompositeOperation = "destination-over";

Create an image to hold the background.

const backgroundImage = new Image();
backgroundImage.src = "http://localhost/forms/img/vanImage.jpg";

You need to ensure that the image has loaded when the client clicks ready, incase it has not loaded you can set a callback that will call back the ready function if needed.

var backgroundLoadedCallback = null;
backgroundImage.onload = function(){
     if( typeof backgroundLoadedCallback === "function"){
         backgroundLoadedCallback();
     }
}

Then create the canvas -> textarea function

function canvasToTextarea(){

    const myCanvas = document.getElementById('colors_sketch');
    const ctx = myCanvas.getContext("2d");
    ctx.globalCompositeOperation = "destination-over";  // make sure the background go under the drawn pixels
    // draw and fit background to the canvas
    ctx.drawImage(backgroundImage,0,0,ctx.canvas.width,ctx.canvas.height);
    // then convert to URL for textarea
    $('.saveimage textarea').val(myCanvas.toDataURL());

}

In the checked function

$('.imagefinish input').change(function(){
  if(this.checked) {
     if(backgroundImage.complete) { // image has loaded so all good
         canvasToTextarea(); // put canvas data URL to textarea
     } else { // background has not yet loaded (or could be an error you will have to deal with that as well
          // set the callback to the canvasToTextarea function
          backgroundLoadedCallback = canvasToTextarea;
          // when the background has loaded the canvas and background
          // will be moved to the textarea as a data URL
     } 
  }
});

Or modify sketch.js

Below is the draw function from sketch.js (and it`s very old school)

Sketch.prototype.redraw = function() {
  var sketch;
  this.el.width = this.canvas.width();
  this.context = this.el.getContext('2d');
  sketch = this;
  $.each(this.actions, function() {
    if (this.tool) {
      return $.sketch.tools[this.tool].draw.call(sketch, this);
    }
  });
  if (this.painting && this.action) {
    return $.sketch.tools[this.action.tool].draw.call(sketch, this.action);
  }
};

Just replace it with the following. You dont need to modify the sketch.js file justy overwrite the redraw Prototype

In your code load the background and set the new redraw

const backgroundImage = new Image();
backgroundImage.src = "http://localhost/forms/img/vanImage.jpg";


// replace Sketch.js redraw function
Sketch.prototype.redraw = function(){
  var sketch;
  // dont need the next line use clear instead
  // this.el.width = this.canvas.width();

  const ctx = this.context = this.el.getContext('2d');
  // clear canvas
  this.context.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  // If backgroundimage complete draw it first
  if(backgroundImage && backgroundImage.complete){
      ctx.drawImage(backgroundImage,0,0,ctx.canvas.width,ctx.canvas.height);
  }

  // back to the original code. :P
  sketch = this;
  $.each(this.actions, function() {
    if (this.tool) {
      return $.sketch.tools[this.tool].draw.call(sketch, this);
    }
  });
  if (this.painting && this.action) {
    return $.sketch.tools[this.action.tool].draw.call(sketch, this.action);
  }
}
Blindman67
  • 51,134
  • 11
  • 73
  • 136
  • I was able to find a super solution of having a placeholder background url that isn't being used to save at all, just sitting there in the CSS. I then added a variable for my path then in the load AFTER the image has been drawn. myCanvas.style.backgroundRepeat = "no-repeat"; myCanvas.style.backgroundImage = 'url('+image+')' . My solution is fairly specific to the software I'm building in but yours also works so I'm going to mark it as an answer and update my OP. – Alex Sep 04 '17 at 15:53