15
votes

What I want to do here is before Dropzone.js send the dropped image to the server, a modal appears with cropper.js (fengyuanchen script) so the user can crop the image, and when image is cropped, send it with Dropzone.js to the server.

So when i change the image src of #cropbox with the function fileToBase64 and replace the image of the cropper with the function cropper('replace'), it keeps showing default.jpg image, instead of the new one uploaded from the user

HTML

<div class="wrapper-crop-box">
    <div class="crop-box">
        <img src="default.jpg" alt="Cropbox" id="cropbox">
    </div>
</div>

JS:

function fileToBase64(file) {
  var preview = document.querySelector('.crop-box img');
  var reader  = new FileReader();

  reader.onloadend = function () {
    preview.src = reader.result;
  }

  if (file) {
    reader.readAsDataURL(file);
  } else {
    preview.src = "";
  }
}

$(function() {
    Dropzone.options.avtDropzone = {
        acceptedFiles: 'image/*',
        autoProcessQueue: true,
        paramName: 'file',
        maxFilesize: 2,
        maxFiles: 1,
        thumbnailWidth: 200,
        thumbnailHeight: 200,
        accept: function(file, done) {
            fileToBase64(file); 
            $('#cropbox').cropper('replace', $('#cropbox').attr('src'));
            $('.wrapper-crop-box').fadeIn();
            done();
        },
        init: function() {
            this.on("addedfile", function(file) {
                if (this.files[1]!=null){
                    this.removeFile(this.files[0]);
                }
            });
        }
    };

    $('#cropbox').cropper({
      aspectRatio: 1 / 1,
      resizable: false,
      guides: false,
      dragCrop: false,
      autoCropArea: 0.4,
      checkImageOrigin: false,
      preview: '.avatar'
    });
});
4

4 Answers

12
votes

You probably don't need it anymore, but I'll just leave it here :)

It was a bit tricky and my solution is probably somehow 'hackish', but it works and you don't have to upload files on server to resize.

I'm also using cropper in a modal window. I wanted to force user to crop image to a certain dimensions before uploading to server.

After you confirm crop in a modal image is instantly uploaded.

// transform cropper dataURI output to a Blob which Dropzone accepts
function dataURItoBlob(dataURI) {
    var byteString = atob(dataURI.split(',')[1]);
    var ab = new ArrayBuffer(byteString.length);
    var ia = new Uint8Array(ab);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }
    return new Blob([ab], { type: 'image/jpeg' });
}

// modal window template
var modalTemplate = '<div class="modal"><!-- bootstrap modal here --></div>';

// initialize dropzone
Dropzone.autoDiscover = false;
var myDropzone = new Dropzone(
    "#my-dropzone-container",
    {
        autoProcessQueue: false,
        // ..your other parameters..
    }
);

// listen to thumbnail event
myDropzone.on('thumbnail', function (file) {
    // ignore files which were already cropped and re-rendered
    // to prevent infinite loop
    if (file.cropped) {
        return;
    }
    if (file.width < 800) {
        // validate width to prevent too small files to be uploaded
        // .. add some error message here
        return;
    }
    // cache filename to re-assign it to cropped file
    var cachedFilename = file.name;
    // remove not cropped file from dropzone (we will replace it later)
    myDropzone.removeFile(file);

    // dynamically create modals to allow multiple files processing
    var $cropperModal = $(modalTemplate);
    // 'Crop and Upload' button in a modal
    var $uploadCrop = $cropperModal.find('.crop-upload');

    var $img = $('<img />');
    // initialize FileReader which reads uploaded file
    var reader = new FileReader();
    reader.onloadend = function () {
        // add uploaded and read image to modal
        $cropperModal.find('.image-container').html($img);
        $img.attr('src', reader.result);

        // initialize cropper for uploaded image
        $img.cropper({
            aspectRatio: 16 / 9,
            autoCropArea: 1,
            movable: false,
            cropBoxResizable: true,
            minContainerWidth: 850
        });
    };
    // read uploaded file (triggers code above)
    reader.readAsDataURL(file);

    $cropperModal.modal('show');

    // listener for 'Crop and Upload' button in modal
    $uploadCrop.on('click', function() {
        // get cropped image data
        var blob = $img.cropper('getCroppedCanvas').toDataURL();
        // transform it to Blob object
        var newFile = dataURItoBlob(blob);
        // set 'cropped to true' (so that we don't get to that listener again)
        newFile.cropped = true;
        // assign original filename
        newFile.name = cachedFilename;

        // add cropped file to dropzone
        myDropzone.addFile(newFile);
        // upload cropped file with dropzone
        myDropzone.processQueue();
        $cropperModal.modal('hide');
    });
});
3
votes

For devs that don't want or can't have the jQuery dependency.

I've recently written an article on integrating Dropzone with Cropper.js, it might be useful. See code snippet from the article below.

The HTML

<div class="dropzone" id="myDropzone"></div>

The JavaScript

Dropzone.options.myDropzone = {
    url: '/post',
    transformFile: function(file, done) {

        var myDropZone = this;

        // Create the image editor overlay
        var editor = document.createElement('div');
        editor.style.position = 'fixed';
        editor.style.left = 0;
        editor.style.right = 0;
        editor.style.top = 0;
        editor.style.bottom = 0;
        editor.style.zIndex = 9999;
        editor.style.backgroundColor = '#000';

        // Create the confirm button
        var confirm = document.createElement('button');
        confirm.style.position = 'absolute';
        confirm.style.left = '10px';
        confirm.style.top = '10px';
        confirm.style.zIndex = 9999;
        confirm.textContent = 'Confirm';
        confirm.addEventListener('click', function() {

            // Get the canvas with image data from Cropper.js
            var canvas = cropper.getCroppedCanvas({
                width: 256,
                height: 256
            });

            // Turn the canvas into a Blob (file object without a name)
            canvas.toBlob(function(blob) {

                // Update the image thumbnail with the new image data
                myDropZone.createThumbnail(
                    blob,
                    myDropZone.options.thumbnailWidth,
                    myDropZone.options.thumbnailHeight,
                    myDropZone.options.thumbnailMethod,
                    false, 
                    function(dataURL) {

                        // Update the Dropzone file thumbnail
                        myDropZone.emit('thumbnail', file, dataURL);

                        // Return modified file to dropzone
                        done(blob);
                    }
                );

            });

            // Remove the editor from view
            editor.parentNode.removeChild(editor);

        });
        editor.appendChild(confirm);

        // Load the image
        var image = new Image();
        image.src = URL.createObjectURL(file);
        editor.appendChild(image);

        // Append the editor to the page
        document.body.appendChild(editor);

        // Create Cropper.js and pass image
        var cropper = new Cropper(image, {
            aspectRatio: 1
        });

    }
};

A demo of the integration code can be found here: https://codepen.io/rikschennink/pen/PXQNGp?editors=0010

1
votes

Here is a gist that provides this functionality.

https://gist.github.com/maria-p/8633b51f629ea8dbd27e

// transform cropper dataURI output to a Blob which Dropzone accepts
function dataURItoBlob(dataURI) {
    var byteString = atob(dataURI.split(',')[1]);
    var ab = new ArrayBuffer(byteString.length);
    var ia = new Uint8Array(ab);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }
    return new Blob([ab], { type: 'image/jpeg' });
}

// modal window template
var modalTemplate = '<div class="modal"><!-- bootstrap modal here --></div>';

// initialize dropzone
Dropzone.autoDiscover = false;
var myDropzone = new Dropzone(
    "#my-dropzone-container",
    {
        autoProcessQueue: false,
        // ..your other parameters..
    }
);

// listen to thumbnail event
myDropzone.on('thumbnail', function (file) {
    // ignore files which were already cropped and re-rendered
    // to prevent infinite loop
    if (file.cropped) {
        return;
    }
    if (file.width < 800) {
        // validate width to prevent too small files to be uploaded
        // .. add some error message here
        return;
    }
    // cache filename to re-assign it to cropped file
    var cachedFilename = file.name;
    // remove not cropped file from dropzone (we will replace it later)
    myDropzone.removeFile(file);

    // dynamically create modals to allow multiple files processing
    var $cropperModal = $(modalTemplate);
    // 'Crop and Upload' button in a modal
    var $uploadCrop = $cropperModal.find('.crop-upload');

    var $img = $('<img />');
    // initialize FileReader which reads uploaded file
    var reader = new FileReader();
    reader.onloadend = function () {
        // add uploaded and read image to modal
        $cropperModal.find('.image-container').html($img);
        $img.attr('src', reader.result);

        // initialize cropper for uploaded image
        $img.cropper({
            aspectRatio: 16 / 9,
            autoCropArea: 1,
            movable: false,
            cropBoxResizable: true,
            minContainerWidth: 850
        });
    };
    // read uploaded file (triggers code above)
    reader.readAsDataURL(file);

    $cropperModal.modal('show');

    // listener for 'Crop and Upload' button in modal
    $uploadCrop.on('click', function() {
        // get cropped image data
        var blob = $img.cropper('getCroppedCanvas').toDataURL();
        // transform it to Blob object
        var newFile = dataURItoBlob(blob);
        // set 'cropped to true' (so that we don't get to that listener again)
        newFile.cropped = true;
        // assign original filename
        newFile.name = cachedFilename;

        // add cropped file to dropzone
        myDropzone.addFile(newFile);
        // upload cropped file with dropzone
        myDropzone.processQueue();
        $cropperModal.modal('hide');
    });
});

Note, I am not the author.

-3
votes

I've done it using dropzone.js and cropper.js inside angular.js module. maybe it can help someone.

[http://pastebin.com/4miiWbyZ][1] - angularjs
[http://pastebin.com/1kBkvWt8][2] - dropzone.html
[http://pastebin.com/jmDdNWFf][3] - cropper.html