4

enter image description here

This is the output screen, where image is not properly positioned, sized inside bootstrap modal.

I am trying to use the 'cropper.js' plugin in 'avatar' update section of my project.I added the related CSS and JS files also using jquery and the plugin is working good. My issues is image positioning/size inside bootstrap modal. its showing in small and left middle. I am trying to make them well sized, positioned and a responsive output. what i tried is adding below.Any help to this question is highly appreciated.

WHAT I DONE :

main.js file for initializing/uploading and cropping image with cropper plugin

(function (factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as anonymous module.
    define(['jquery'], factory);
  } else if (typeof exports === 'object') {
    // Node / CommonJS
    factory(require('jquery'));
  } else {
    // Browser globals.
    factory(jQuery);
  }
})(function ($) {

  'use strict';

  var console = window.console || { log: function () {} };

  function CropAvatar($element) {
    this.$container = $element;

    this.$avatarView = this.$container.find('.avatar-view');
    this.$avatar = this.$avatarView.find('img');
    this.$avatarModal = this.$container.find('#avatar-modal');
    this.$avatarModal1 = this.$container.find('#avatar-modal1');
    this.$loading = this.$container.find('.loading');

    this.$avatarForm = this.$avatarModal1.find('.avatar-form');
    this.$avatarUpload = this.$avatarForm.find('.avatar-upload');
    this.$avatarSrc = this.$avatarForm.find('.avatar-src');
    this.$avatarData = this.$avatarForm.find('.avatar-data');
    this.$avatarInput = this.$avatarForm.find('.avatar-input');
    this.$avatarSave = this.$avatarForm.find('.avatar-save');
    this.$avatarBtns = this.$avatarForm.find('.avatar-btns');

    this.$avatarWrapper = this.$avatarModal.find('.avatar-wrapper');
    this.$avatarPreview = this.$avatarModal.find('.avatar-preview');

    this.init();
  }

  CropAvatar.prototype = {
    constructor: CropAvatar,

    support: {
      fileList: !!$('<input type="file">').prop('files'),
      blobURLs: !!window.URL && URL.createObjectURL,
      formData: !!window.FormData
    },

    init: function () {
      this.support.datauri = this.support.fileList && this.support.blobURLs;

      if (!this.support.formData) {
        this.initIframe();
      }

      this.initTooltip();
      this.initModal();
      this.addListener();
    },

    addListener: function () {
      this.$avatarView.on('click', $.proxy(this.click, this));

      this.$avatarInput.on('change', $.proxy(this.change, this));
      //this.$avatarInput.on('change', $.proxy(this.click, this));

      this.$avatarForm.on('submit', $.proxy(this.submit, this));
      this.$avatarBtns.on('click', $.proxy(this.rotate, this));
    },

    initTooltip: function () {
      this.$avatarView.tooltip({
        placement: 'bottom'
      });
    },


    initPreview: function () {
      var url = this.$avatar.attr('src');

      this.$avatarPreview.html('<img src="' + url + '">');
    },

    initIframe: function () {
      var target = 'upload-iframe-' + (new Date()).getTime();
      var $iframe = $('<iframe>').attr({
            name: target,
            src: ''
          });
      var _this = this;

      // Ready ifrmae
      $iframe.one('load', function () {

        // respond response
        $iframe.on('load', function () {
          var data;

          try {
            data = $(this).contents().find('body').text();
          } catch (e) {
            console.log(e.message);
          }

          if (data) {
            try {
              data = $.parseJSON(data);
            } catch (e) {
              console.log(e.message);
            }

            _this.submitDone(data);
          } else {
            _this.submitFail('Image upload failed!');
          }

          _this.submitEnd();


        });
      });

      this.$iframe = $iframe;
      this.$avatarForm.attr('target', target).after($iframe.hide());
    },
     initModal: function () {
      this.$avatarModal.modal({
        show: false
      });
    },


    click: function () {
      this.$avatarModal.modal('show');
      this.initPreview();
    },
    change: function () {
      //alert("changed");
      this.$avatarModal.modal('show');
             this.initPreview();

      var files;
      var file;

      if (this.support.datauri) {
        files = this.$avatarInput.prop('files');

        if (files.length > 0) {
          file = files[0];

          if (this.isImageFile(file)) {
            if (this.url) {
              URL.revokeObjectURL(this.url); // Revoke the old one
            }



            this.url = URL.createObjectURL(file);
            this.startCropper();

          }
        }
      } else {
        file = this.$avatarInput.val();

        if (this.isImageFile(file)) {
          //alert("changed");
          this.syncUpload();
        }
      }
    },


    isImageFile: function (file) {
      if (file.type) {
        return /^image\/\w+$/.test(file.type);
      } else {
        return /\.(jpg|jpeg|png|gif)$/.test(file);
      }
    },

    startCropper: function () {
      var _this = this;

      if (this.active) {
        this.$img.cropper('replace', this.url);
      } else {
        this.$img = $('<img src="' + this.url + '">');
        this.$avatarWrapper.empty().html(this.$img);
        this.$img.cropper({
          //aspectRatio: 1,
          aspectRatio: 1,
          preview: this.$avatarPreview.selector,
          crop: function (e) {
            var json = [
                  '{"x":' + e.x,
                  '"y":' + e.y,
                  '"height":' + e.height,
                  '"width":' + e.width,
                  '"rotate":' + e.rotate + '}'
                ].join();

            _this.$avatarData.val(json);
             $("#output").html("("+e.width+","+e.height+")");
          }
        });

      this.active = true;
      }

      this.$avatarModal.one('hidden.bs.modal', function () {
        _this.$avatarPreview.empty();
        _this.stopCropper();
      });
    },

    stopCropper: function () {
      if (this.active) {
        this.$img.cropper('destroy');
        this.$img.remove();
        this.active = false;
      }
    },

    syncUpload: function () {
      this.$avatarSave.click();
    },

    alert: function (msg) {
      var $alert = [
            '<div class="alert alert-danger avatar-alert alert-dismissable">',
              '<button type="button" class="close" data-dismiss="alert">&times;</button>',
              msg,
            '</div>'
          ].join('');

      this.$avatarUpload.after($alert);
    }
  };

  $(function () {
    return new CropAvatar($('#crop-avatar'));
  });

}); 

Home page - HTML view page

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Image Cropping with Cropper JS</title>
  <link rel="stylesheet" href="css/bootstrap.min.css">
  <link rel="stylesheet" href="css/cropper.min.css">
  <link rel="stylesheet" href="css/cropper.css">
  <link rel="stylesheet" href="css/styles.css">

</head>
<body>
  <div class="container" id="crop-avatar">

    <!-- Current avatar -->
    <div class="avatar-view" title="Change the avatar">
      <img src="img/img.jpg" alt="Avatar">
    </div>

    <div id="avatar-modal1">
    <form class="avatar-form" action="crop.php" enctype="multipart/form-data" method="post">

      <!-- Upload image and data -->
                <div class="avatar-upload">
                  <input type="hidden" class="avatar-src" name="avatar_src">
                  <input type="hidden" class="avatar-data" name="avatar_data">
                  <label for="avatarInput">Upload</label>
                  <input type="file" class="avatar-input" id="avatarInput" name="avatar_file">
                </div>
    <!-- Cropping modal -->
    <div class="modal fade" id="avatar-modal" aria-hidden="true" aria-labelledby="avatar-modal-label" role="dialog" tabindex="-1">
      <div class="modal-dialog modal-lg">
        <div class="modal-content">

            <div class="modal-header">
              <button type="button" class="close" data-dismiss="modal">&times;</button>
              <h4 class="modal-title" id="avatar-modal-label">Change Image</h4>
            </div>
            <div class="modal-body">
              <div class="avatar-body">



                <!-- Crop and preview -->
                <div class="row">
                  <div class="col-md-9">
                    <div class="avatar-wrapper"> </div>
                  </div>
                  <div class="col-md-3">
                    <div class="avatar-preview preview-lg" style="border-radius:90px !important;"></div>
                    <!-- <div class="avatar-preview preview-md"></div>
                    <div class="avatar-preview preview-sm"></div>-->
                    <br>
                    <h5>Output value(width,height)</h5>
                    <div id="output"></div>



                  </div>
                </div>

                <div class="row avatar-btns">
                  <!--<div class="col-md-9">
                    <div class="btn-group">
                      <button type="button" class="btn btn-primary" data-method="rotate" data-option="-90" title="Rotate -90 degrees">Rotate Left</button>
                      <button type="button" class="btn btn-primary" data-method="rotate" data-option="-15">-15deg</button>
                      <button type="button" class="btn btn-primary" data-method="rotate" data-option="-30">-30deg</button>
                      <button type="button" class="btn btn-primary" data-method="rotate" data-option="-45">-45deg</button>
                    </div>
                    <div class="btn-group">
                      <button type="button" class="btn btn-primary" data-method="rotate" data-option="90" title="Rotate 90 degrees">Rotate Right</button>
                      <button type="button" class="btn btn-primary" data-method="rotate" data-option="15">15deg</button>
                      <button type="button" class="btn btn-primary" data-method="rotate" data-option="30">30deg</button>
                      <button type="button" class="btn btn-primary" data-method="rotate" data-option="45">45deg</button>
                    </div>
                  </div>
                  <div class="col-md-3">
                    <button type="submit" class="btn btn-primary btn-block avatar-save">Done</button>
                  </div>-->
                </div>
              </div>
            </div>

        </div>
      </div>
    </div><!-- /.modal -->

  </form>
  </div>
    <!-- Loading state -->
     <div class="loading" aria-label="Loading" role="img" tabindex="-1"></div>
  </div>

  <script src="js/jquery.min.js"></script>
  <script src="js/bootstrap.min.js"></script>
  <script src="js/cropper.min.js"></script>
  <script src="js/main.js"></script>
</body>
</html>
Ajesh VC
  • 635
  • 5
  • 13

3 Answers3

3

Class modal fade caused the problem for me, removed fade and it worked like a charm.

If you want to keep the fade effect, use setTimeout for cropper initialization, so $('#yourModal').modal('show'); the setTimeout for the function that initialize the cropper.

This was mentioned in Arnab Chaudhuri's answer:

If you are using cropper in a modal, you should initialize the cropper after the modal shown completely. Otherwise, you will not get a correct cropper.

Duong Nguyen
  • 149
  • 4
  • 12
2

Check below Note from https://github.com/fengyuanchen/cropperjs#notes

Notes

  1. The size of the cropper inherits from the size of the image's parent element (wrapper), so be sure to wrap the image with a visible block element.

    If you are using cropper in a modal, you should initialize the cropper after the modal shown completely. Otherwise, you will not get a correct cropper.

  2. The outputted cropped data bases on the original image size, so you can use them to crop the image directly.

  3. If you try to start cropper on a cross-origin image, please make sure that your browser supports HTML5 CORS settings attributes, and your image server supports the Access-Control-Allow-Origin option (see the HTTP access control (CORS)).

1

I Figure out the issue,

Issue

Bootstrap modal contents (CSS and elements) not loading completely while we initialising like above

Solution

(1) . Ready Bootstrap modal contents first, after load the events using bootstrap-modal events methods.

(2) . Use delay or Remove Delay based on use

Ajesh VC
  • 635
  • 5
  • 13