Avatar of mike99c
mike99c

asked on 

How do I use cropper.js to create multiple dynamic crop windows over the same image

I’m creating a web application that takes a user submitted image and uploads two cropped images of different aspect ratios based on user selection.
 
It's documented that cropper.js supports multiple crops in the same window and I would like to know if it's possible (and if so how) to display two cropping windows over the same image. Ideally a user should be able to upload an image at two different crops by adjusting two different viewports on a single image and crop both at the same time, as illustrated below.

User generated image
I’ve included sample code that supports single cropping and has been expanded to support multiple crops by refreshing the window with a new cropper. This approach involves displaying and submitting the crop twice as illustrated below.

User generated image
The sample code below is a simple web page containing a button to upload a valid image for the user to crop.

Index.html
<!DOCTYPE html>
<html>
<head>
    <title>PHP Crop Image Before Upload using Cropper JS</title>
    <meta name="_token" content="{{ csrf_token() }}">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js" i</script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" crossorigin="anonymous" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/js/bootstrap.min.js" </script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.6/cropper.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.6/cropper.js" i</script>
</head>
<style type="text/css">
img {
  display: block;
  max-width: 100%;
}
.preview {
  overflow: hidden;
  width: 160px; 
  height: 160px;
  margin: 10px;
  border: 1px solid red;
}
.modal-lg{
  max-width: 1000px !important;
}
</style>
<body>
<div class="container">
  <h1>PHP Crop Image Before Upload using Cropper JS</h1>
  <form method="post">
  <input type="file" name="image" class="image">
  </form>
  <br />
  <div id="uploaded_image"></div>
</div>

<div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="modalLabel" aria-hidden="true">
  <div class="modal-dialog modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="modalLabel">Single Crop</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true"></span>
        </button>
      </div>
      <div class="modal-body">
        <div class="img-container">
            <div class="row">
                <div class="col-md-8">
                    <img id="image" src="https://avatars0.githubusercontent.com/u/3456749">
                </div>
                <div class="col-md-4">
                    <div class="preview"></div>
                </div>
            </div>
        </div>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
        <button type="button" class="btn btn-primary" id="crop">Crop</button>
      </div>
    </div>
  </div>
</div>

</div>
</div>
<script>

var $modal = $('#modal');
var image = document.getElementById('image');
var cropper;
  
$("body").on("change", ".image", function(e){
    var files = e.target.files;
    var done = function (url) {
      image.src = url;
      $modal.modal('show');
    };
    var reader;
    var file;
    var url;

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

      if (URL) {
        done(URL.createObjectURL(file));
      } else if (FileReader) {
        reader = new FileReader();
        reader.onload = function (e) {
          done(reader.result);
        };
        reader.readAsDataURL(file);
      }
    }
});

$modal.on('shown.bs.modal', function () {
    cropper = new Cropper(image, {
    aspectRatio: 4/5,
    viewMode: 3,
    preview: '.preview'
    });
    
}).on('hidden.bs.modal', function () {
   cropper.destroy();
   cropper = null;
  
});

$("#crop").click(function(){
    canvas = cropper.getCroppedCanvas({
      width: 600, // Adjust here
      height: 600,
    });

    canvas.toBlob(function(blob) {
        url = URL.createObjectURL(blob);
        var reader = new FileReader();
         reader.readAsDataURL(blob); 
         reader.onloadend = function() {
            var base64data = reader.result;  
            
            $.ajax({
                type: "POST",
                dataType: "json",
                url: "upload.php",
                data: {image: base64data},
                success: function(data){
                    console.log(data);
                    
                    $modal.modal('hide');
                    
                    $("#uploaded_image").html(data);
                   
                }
              });
         }
    });
})

</script>
</body>
</html>

Open in new window

upload.php
<?php

if(isset($_POST["image"]))
{

        $folderPath = 'upload/';

        $image_parts = explode(";base64,", $_POST['image']);
        $image_type_aux = explode("image/", $image_parts[0]);
        $image_type = $image_type_aux[1];
        $image_base64 = base64_decode($image_parts[1]);
        $file = $folderPath . uniqid() . '.png';

        file_put_contents($file, $image_base64);
     

        echo json_encode(["image uploaded successfully."]);
}
?>

Open in new window


Using cropper.js the script displays a cropper canvas with the submitted image.

The user can drag, move and select an area of fixed aspect ratio to crop.

When the crop button is selected, the crop is taken from the image and uploaded.  

This process could be repeated for multiple aspect ratios by destroying the cropper canvas and creating a new one with the same original image.  

How could this program be expanded to support the cropping of multiple images, is there an alternative library that is better suited for this implementation and is this the most viable solution for a better user experience?
CSSHTMLPHPjQuery

Avatar of undefined
Last Comment
Julian Hansen
Avatar of Julian Hansen
Julian Hansen
Flag of South Africa image

If I look at the documentation the multiple refers to multiple images on the same page - each with its own cropper. Your question seems to be how to display two crop areas on the same image.

I am just wondering how this would work - how would a crop tool interpret this? Would it see each crop area as applying to the whole image - or would it have to deal with overlapping areas.

From a usability perspective I am trying to see how this will work in a non-confusing way. If you need to create two crops then create two crops - one at a time so the user knows what the crop is for - clear distinction of function and purpose.

This can either be in the same page (as per the cropper multi example) or in a pipeline / wizard format where the first crop is done and transitioned off page and the second is displayed.

What benefits are there (assuming it is possible with this tool) for doing the crop on the same image?
Avatar of mike99c
mike99c

ASKER

Thanks Julian,

At first it may seem confusing but we have alleviated this with the following:
  • Each crop area will have a different colour
  • Each crop area will have a separate preview to the side which will update in real time as you move your crop area.
The main advantage in doing this is that both crops can be done in one step.
Avatar of Julian Hansen
Julian Hansen
Flag of South Africa image

Then you will need to find a tool that does it or modify one that does not. We can't do that for you.

Personally my choice would be to separate them - even if they are both on the same page with different images. I don't beleive the single image multiple crop solution will work well and as such I don't think it is likely you will find an out of the box solution that will do it for you - I stand open to correction - but intuitively that is my feeling on this.

My advice is to go back and give some thought to the user experience and determine if an all in one is the right answer
a) Given the complexity of the task (and finding a tool to do it)
b) The user experience

If it is still something that will make a significant difference to your app then you might need to employ someone to build the functionality for you (assuming you can't find a tool already built).

ASKER CERTIFIED SOLUTION
Avatar of mike99c
mike99c

Blurred text
THIS SOLUTION IS ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
@Mike99c - sometime the answer "there is no solution" is the answer. You were asking how to do something - an answer was given that this is not possible. Your selected answer is essentially an abriged version of what I posted in my last two comments.

PHP
PHP

PHP is a widely-used server-side scripting language especially suited for web development, powering tens of millions of sites from Facebook to personal WordPress blogs. PHP is often paired with the MySQL relational database, but includes support for most other mainstream databases. By utilizing different Server APIs, PHP can work on many different web servers as a server-side scripting language.

125K
Questions
--
Followers
--
Top Experts
Get a personalized solution from industry experts
Ask the experts
Read over 600 more reviews

TRUSTED BY

IBM logoIntel logoMicrosoft logoUbisoft logoSAP logo
Qualcomm logoCitrix Systems logoWorkday logoErnst & Young logo
High performer badgeUsers love us badge
LinkedIn logoFacebook logoX logoInstagram logoTikTok logoYouTube logo