Search code examples
bootstrap-modaldropzone.jscropperjs

Dropzone.js upload an image in modal using cropper.js


I have a code that is responsible for opening an image loaded in a drag and drop field (dropzone.js) in a modal window for cropping using cropper.js. The code works halfway. The modal window opens, but the buttons inside the modal window do not work. An error is appeared. "Uncaught TypeError: Cannot read property 'cropper' of null". How can this problem be solved? Here is the code.

<script type="text/javascript">
    Dropzone.autoDiscover = false;
    var c = 0;
    var cropped = false;
    var myDropzone = new Dropzone('div#myDropzone', {
    url: "/plugins/dropzone/dist/upload.php",
    addRemoveLinks: true,
    createImageThumbnails: true,
    autoProcessQueue: false
    });

myDropzone.on('addedfile', function(file) {
    if (!cropped) {
      myDropzone.removeFile(file);
      cropper(file);
    } else {
      cropped = false;
      var previewURL = URL.createObjectURL(file);
      var dzPreview = $(file.previewElement).find('img');
      dzPreview.attr("src", previewURL);
    }
  });

    var cropper = function(file) {
    var fileName = file.name;
    var loadedFilePath = getSrcImageFromBlob(file);
    // @formatter:off
    var modalTemplate =
      '<div class="modal fade" tabindex="-1" role="dialog">' +
      '<div class="modal-dialog" role="document">' +
      '<div class="modal-content">' +
      '<div class="modal-header">' +
      '<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i class="fa fa-times" aria-hidden="true"></i></button>' +
      '</div>' +
      '<div class="modal-body">' +
      '<div class="cropper-container">' +
      '<img id="img-' + c + '" src="' + loadedFilePath + '" data-vertical-flip="false" data-horizontal-flip="false">' +
      '</div>' +
      '</div>' +
      '<div class="modal-footer">' +
      '<button type="button" class="btn btn-warning rotate-left"><span class="fa fa-rotate-left"></span></button>' +
      '<button type="button" class="btn btn-warning rotate-right"><span class="fa fa-rotate-right"></span></button>' +
      '<button type="button" class="btn btn-warning scale-x" data-value="-1"><span class="fa fa-arrows-h"></span></button>' +
      '<button type="button" class="btn btn-warning scale-y" data-value="-1"><span class="fa fa-arrows-v"></span></button>' +
      '<button type="button" class="btn btn-warning reset"><span class="fa fa-refresh"></span></button>' +
      '<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>' +
      '<button type="button" class="btn btn-primary crop-upload">Crop & upload</button>' +
      '</div>' +
      '</div>' +
      '</div>' +
      '</div>';
    // @formatter:on
jQuery(modalTemplate).modal('show').on("shown.bs.modal", function() {
      var $image = $('#img-' + c);
      console.log($image);
      var cropper = $image.cropper({
          autoCropArea: 1,
          aspectRatio: 9 / 16,
          cropBoxResizable: false,
          movable: true,
          rotatable: true,
          scalable: true,
          viewMode: 2,
          minContainerWidth: 250,
          maxContainerWidth: 250
        })
        .on('hidden.bs.modal', function() {
          $image.cropper('destroy');
        });

$cropperModal.on('click', '.crop-upload', function() {
          // get cropped image data
          $image.cropper('getCroppedCanvas', {
            width: 160,
            height: 90,
            minWidth: 256,
            minHeight: 256,
            maxWidth: 4096,
            maxHeight: 4096,
            fillColor: '#fff',
            imageSmoothingEnabled: false,
            imageSmoothingQuality: 'high'
          }).toBlob(function(blob) {
            var croppedFile = blobToFile(blob, fileName);
            croppedFile.accepted = true;
            var files = myDropzone.getAcceptedFiles();
            for (var i = 0; i < files.length; i++) {
              var file = files[i];
              if (file.name === fileName) {
                myDropzone.removeFile(file);
              }
            }
            cropped = true;

            myDropzone.files.push(croppedFile);
            myDropzone.emit('addedfile', croppedFile);
            myDropzone.createThumbnail(croppedFile); //, width, height, resizeMethod, fixOrientation, callback)
            $cropperModal.modal('hide');
          });
        })
        .on('click', '.rotate-right', function() {
          $image.cropper('rotate', 90);
        })
        .on('click', '.rotate-left', function() {
          $image.cropper('rotate', -90);
        })
        .on('click', '.reset', function() {
          $image.cropper('reset');
        })
        .on('click', '.scale-x', function() {
          if (!$image.data('horizontal-flip')) {
            $image.cropper('scale', -1, 1);
            $image.data('horizontal-flip', true);
          } else {
            $image.cropper('scale', 1, 1);
            $image.data('horizontal-flip', false);
          }
        })
        .on('click', '.scale-y', function() {
          if (!$image.data('vertical-flip')) {
            $image.cropper('scale', 1, -1);
            $image.data('vertical-flip', true);
          } else {
            $image.cropper('scale', 1, 1);
            $image.data('vertical-flip', false);
          }
        });
    });
  };

  function getSrcImageFromBlob(blob) {
    var urlCreator = window.URL || window.webkitURL;
    return urlCreator.createObjectURL(blob);
  }

  function blobToFile(theBlob, fileName) {
    theBlob.lastModifiedDate = new Date();
    theBlob.name = fileName;
    return theBlob;
  }
</script>

Solution

  • Make sure $image is initialized outside of on("shown.bs.modal", function() { function

    Working example

    var dropzone = new Dropzone('div#dropzone', {
      url: "/plugins/dropzone/dist/upload.php",
      addRemoveLinks: true,
      createImageThumbnails: true,
      autoProcessQueue: false
    });
    
    dropzone.on('addedfile', function(file) {
      
      dropzone.removeFile(file);
      cropper(file);
      
    });
    
    $(function() {
      fetch("https://i.picsum.photos/id/539/200/300.jpg")
        .then(response => response.blob())
        .then(blob => {
          cropper(blob);
        })
    })
    
    function cropper(file) { 
      const url = URL.createObjectURL(file);
        
      var modalTemplate = `<div class="modal fade" tabindex="-1" role="dialog">
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <div class="modal-header">
          </div>
          <div class="modal-body">
            <img id="img" src="${url}" data-vertical-flip="false" data-horizontal-flip="false">
          </div>
          <div class="modal-footer">
            <button class="rotate-right">Rotate Right</button>
          </div>  
        </div>
      </div>
    </div>`
    
      var $cropperModal = $(modalTemplate);
      var $image = null;
      
      $cropperModal.on("shown.bs.modal", function() {
    
        $image = $('#img');
        var cropper = $image.cropper({
          autoCropArea: 1,
          aspectRatio: 9 / 16,
          cropBoxResizable: false,
          movable: true,
          rotatable: true,
          scalable: true,
          viewMode: 2,
          minContainerWidth: 250,
          maxContainerWidth: 250
        });
      
      }).on('click', '.rotate-right', function() {
        $image.cropper('rotate', 90);
      }).on('hidden.bs.modal', function() {
        $(this).remove();
        $image.cropper('destroy');
      })
      .modal({show: true})
    }
    img {
      display: block;
      max-width: 100%;
    }
    
    #dropzone {
      height: 100px 
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.7/cropper.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.7.0/basic.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.7.0/dropzone.css">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.7.0/dropzone.js"></script>
    <script>window.Dropzone.autoDiscover = false;</script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.7/cropper.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/jquery-cropper@1.0.1/dist/jquery-cropper.min.js"></script>
    
    <div id="dropzone" class="dropzone"></div>