Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cropper JS : Positioning/size issue inside Bootstrap modal

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>
like image 518
Ajesh VC Avatar asked Oct 15 '25 21:10

Ajesh VC


2 Answers

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.

like image 97
Duong Nguyen Avatar answered Oct 18 '25 14:10

Duong Nguyen


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)).

like image 20
Arnab Chaudhuri Avatar answered Oct 18 '25 12:10

Arnab Chaudhuri