Jonjie's avatar
Level 12

How to add scale in Laravel Image Intervention package?

I'm doing a crop functionality using Laravel Image Intervention and jQuery Guillotine but it crops the different part of the image. Please see the code and screenshot below.

Controller

$data = explode('|', $image_details);

$data = [
            'angle' => $data[0],
            'h' => $data[1],
            'scale' => $data[2],
            'w' => $data[3],
            'x' => $data[4],
            'y' => $data[5]
        ];

        // Create image instance from source
        $image = Image::make($request_file);

        // we get the image width then multiply it by the scale factor, it will also scale the height automatically
        $image->widen(intval(floatval($image->width()) * floatval($data['scale'])));

        // File details
        $extension = explode('/', mime_content_type($request_file))[1];
        $name = md5(uniqid()) . time() . '.' . $extension; // as6d57a9sd7a5sd67856a9s.jpg

        // Temp details
        $key = md5(uniqid());
        $tmp_file_name = "{$key}.{$extension}"; // et7e98r7t9e79rt9e098g9e0.jpg
        $tmp_file_path = base_path() . "/" . $server_path . '/'. $tmp_file_name; // Server path

        // Image Cropper
        $image->crop(
        	$data['w'],
        	$data['h'],
        	$data['x'],
        	$data['y']
        );
        
        // Upload image to filesystem
        $image->save($tmp_file_path);

Guillotine

picture.on('guillotinechange', function(e, data, action){
                showData(data)
                $('#imageDetails').val(data.angle + '|' + data.h + '|' + data.scale + '|' + data.w + '|' + data.x + '|' + data.y);
              })

Screenshot

[Before Upload] enter image description here

[After Upload] enter image description here

0 likes
7 replies
virgiltu's avatar

Honestly do not use this intervention. Do yourself a favor and use gumplet.com it is the same as Cloudinary but much much cheaper with free levels also. These services will manipulate the image for you to server the best images size for what ever the device a user is using. depanding on the amount of images you server cloudinary is also a really nice option.

Jonjie's avatar
Level 12

@artcore Do you have an example like how you crop your cover photo on facebook? The examples in documentation seems like a photoshop crop.

artcore's avatar

@jonjie Here's a short version of my javascript

let cropper;

const fileInput  = document.querySelector('input[name="document[file]"]'),
      image      = document.getElementById('image'),
      input      = document.querySelector('input[name="document[crop]"]');

fileInput.addEventListener('change', e =>
{
  const file      = e.target.files[0];


  //send the uploaded file to the cropbox img src
  image.innerHTML = `<img alt="" src="${URL.createObjectURL(file)}">`;

  //I left out some things but you get the idea
  init(options.width, options.height);

});

const init = (width, height) =>
{
  if (cropper instanceof Cropper)
  {
    const parent = document.querySelector('.cropper-container');
    if (parent)
      parent.remove();//bug in cropper github#1255
    cropper.destroy();
  }

  cropper = new Cropper(image.querySelector('img'),
    {
      dragMode:           'move',
      minContainerWidth:  image.style.width,
      minContainerHeight: image.style.height,
      minCropBoxWidth:    width,
      minCropBoxHeight:   height,
      wheelZoomRatio:     0.05,
      autoCropArea:       0.65,
      guides:             false,
      background:         false,
      highlight:          false,
      movable:            true,
      cropBoxResizable:   false,
      cropBoxMovable:     false,
      ready:              () =>
                          {
                            set() //I do this elsewhere IF something got changed i.e. cropped, zoomed otherwise just send the original upload to the backend
                          }
    });

};

const set = () =>
{
  const cropped = cropper.getCroppedCanvas(
    {
      width:                 options.width,
      height:                options.height,
      fillColor:             mimeType.value === 'image/png' ? '#FFF0' : '#FFFFFF',
      imageSmoothingEnabled: true,
      imageSmoothingQuality: 'high'
    });

  if (cropped)
    input.value = cropped.toDataURL(mimeType.value || 'image/png', 100);

};

You will get a base64 encoded string in your backend OR without cropping possibly a regular file upload

Here's part of my backend to handle that

$file = $this->document['crop']; //this is the base64 encoded string from toDataURL set by cropperjs

    if (!$file) //regular upload possibly
    {
      $file = $this->document['file'];
      if ($file instanceof UploadedFile)
        $this->source = $file->get();
    }
    else
    {

      $mimeType = $this->document['mime_type'] ?? 'image/jpeg';
      if ($mimeType === 'image/svg+xml')
        $mimeType = 'image/png'; //toDataURL falls back to png | only raster, no vector
      
      $this->source = base64_decode(str_replace("data:$mimeType;base64,", '', $file));
    }

I added some comments to help you understand

I validate mimeType either from UploadedFile or like this:

$source = getimagesizefromstring($this->source);

Never trust user input :)

===

Some DOM elements

<label class="my-20"><input type="file" name="document[file]"></label>


<div id="image">
      <img src="placeholder.jpg" alt="choose file" title="choose file">
</div>

Hope you get the idea - my full code is too elaborate to post ;)

Jonjie's avatar
Level 12

@artcore Can we have jsfiddle for that? So I can understand it better

artcore's avatar

@jonjie If you add the needed dom elements (eg div id=image) and import the cropperjs script, above will guide you to the solution for both frontend and backend. Granted it's a bit fragmented but the cropperjs docs are a good starting point also.

https://fengyuanchen.github.io/cropperjs/ -> examples

Start with an image element, initialize cropperjs on it (see my example in init()) and you will see what my fragments are meant to do.

I'm one of those guys who don't use github and fiddles ;)

Please or to participate in this conversation.