3

I'm trying to realize, how to use cropper.js, and so far it looks pretty good, but... when I trying to crop the image, how can I put the result back to my form?

My input has id 'profile_avatar'. So I try to put there updated image, and cannot.

$('#cut_button').click(function(e) {
  e.preventDefault();
  var croppedImageDataURL = cropper.getCroppedCanvas()
  $('#profile_avatar').val(croppedImageDataURL.toDataURL("image/png"))
});

But when I click 'cut button' I got

InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable

in console. What am I doing wrong? I'm trying to beat it few hours. Thank you so much!

slim file with view:

  = form_for @profile do |f|
    = f.label :avatar
      = f.file_field :avatar
      .canvas_window
        canvas id='canvas'
      .preview
      = link_to('Cut', '', id: 'cut_button')
      = f.label :photos
      = f.file_field :photos, multiple: true
      = f.label :description
      = f.text_area :description
      = @profile.errors.messages[:description].presence
      = hidden_field :dimensions, ''
      = f.submit I18n.t('user.forms.buttons.submit')

javascript:
  var canvas  = $("#canvas"),
      context = canvas.get(0).getContext("2d"),
      $result = $('#result');

  $('#profile_avatar').on( 'change', function(){
    console.log('start')
    if (this.files && this.files[0]) {
      if ( this.files[0].type.match(/^image\//) ) {
        var reader = new FileReader();
        reader.onload = function(evt) {
          var img = new Image();
          img.onload = function() {
            context.canvas.height = img.height;
            context.canvas.width  = img.width;
            context.drawImage(img, 0, 0);
            const image = document.getElementById('canvas');
            const cropper = new Cropper(image, {
              preview: '.preview',
              aspectRatio: 16 / 9,
              crop(event) {
                dimensions = event.detail.width + 'x' + event.detail.width + '+' + event.detail.x + '+' + event.detail.y
                $('#new_profile input#dimensions_').val(dimensions)
                console.log(dimensions);
                console.log(event.detail.x);
                console.log(event.detail.y);
                console.log(event.detail.width);
                console.log(event.detail.height);
              },
            });

            $('#cut_button').click(function(e) {
              e.preventDefault();
              var croppedImageDataURL = cropper.getCroppedCanvas()
              HERE IS ERROR \/
              $('#profile_avatar').val(croppedImageDataURL.toDataURL("image/png"))
              console.log(croppedImageDataURL.toDataURL("image/png"))
             });
           };
           img.src = evt.target.result;
        };
        reader.readAsDataURL(this.files[0]);
      }
      else {
        alert("Invalid file type! Please select an image file.");
      }
    }
    else {
      alert('No file(s) selected.');
    }
  });

html:

<form class="new_profile" id="new_profile" enctype="multipart/form-data" action="/profiles" accept-charset="UTF-8" method="post">
  <input type="hidden" name="authenticity_token" value="j53mqq1th1kb17ynj2jxIMcPFRC210EQVLBSgEu2n4FLOONtFBH3Vu7wrjc+iDrogn99H/emvN5qUdyZo2pAkg==">
  <label for="profile_avatar">Avatar</label>
  <input type="file" name="profile[avatar]" id="profile_avatar">
  <div class="canvas_window">
    <canvas id="canvas"></canvas>
  </div>
  <div class="preview"></div>
  <a id="cut_button" href="">Cut</a>
  <label for="profile_photos">Photos</label>
  <input multiple="multiple" type="file" name="profile[photos][]" id="profile_photos">
  <label for="profile_description">Description</label>
  <textarea name="profile[description]" id="profile_description"></textarea>
  <input type="hidden" name="dimensions[]" id="dimensions_">
  <input type="submit" name="commit" value="Submit" data-disable-with="Submit">
</form>
Ruslan Valeev
  • 1,529
  • 13
  • 24

2 Answers2

1

One thing that stands out is that you need to set the source of an image to the output of toDataUrl(). For example:

HTML:

<img id="cropped_image" src="/blank.gif"></img>

JS:

$("#cropped_image").attr("src", croppedImageDataURL.toDataURL("image/png"));

You could create that image on the fly with JavaScript or include the <img/> tag in your markup with a blank image or hidden (revealing it after you add the cropped image).

To pass the image in your form, you should use an input field because the value at this point is a string (e.g. "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNby").

HTML:

<input id="cropped_image" type="hidden" />

JS:

$("#cropped_image").val(croppedImageDataURL.toDataURL("image/png"));
Ed Lucas
  • 5,955
  • 4
  • 30
  • 42
  • Yeah, but it will be an image tag isn't it? How to send it to server, thats what I mean? – Ruslan Valeev May 22 '20 at 15:56
  • Sorry, I misunderstood your intention. I think you just need to use a different field type, since the image has been reduced to a string by `toDataUrl()`. See above. – Ed Lucas May 22 '20 at 16:24
  • `` is specific to file uploads. See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file – Ed Lucas May 22 '20 at 16:34
  • Even using `toBlob()` to create an image "file" would not work with this form, as you cannot set the value of `` programatically: [How to set a value to a file input in HTML?](https://stackoverflow.com/questions/1696877/how-to-set-a-value-to-a-file-input-in-html). – Ed Lucas May 22 '20 at 16:47
  • Yes, your solution is works, I got kinda url of cropped image, but now I cannot transform this string to image on server side. – Ruslan Valeev May 23 '20 at 03:20
  • So maybe it is possible only through ajax? Like they says here? [cropping-an-image-on-rails](https://stackoverflow.com/questions/33998886/cropping-an-image-on-rails) – Ruslan Valeev May 23 '20 at 03:28
  • You could use ajax to post the blob to your server or you could just store the string version in your database with your other form fields. – Ed Lucas May 23 '20 at 03:38
0

Check this link if your server need a File as input.

The overall process is: CroppedData() -> Base64 image -> Blob -> File ===> Send to Server

Minh Quang
  • 21
  • 4