Cropperjs Save Issues

Hi All… I want to use cropperjs 1.6.2 and have got everything working the way i want except for 1 thing… the image being saved is not the cropped image… it is the original/holding image… my js is not great and the help so far has not fixed this issue… cropperjs 1.6.2 is here - https://fengyuanchen.github.io/cropperjs/ - here is my submit code so far:

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $filename = $_POST['filename'] ?? 'profile_photo.png';
    $img = $_POST['croppedImage'] ?? '';

    if ($img) {
        $img = str_replace('data:image/png;base64,', '', $img);
        $img = str_replace(' ', '+', $img);
        $data = base64_decode($img);

        // Save the image
        $directory = '/home/website/public_html/images/account/profile-photos/';
        $filePath = $directory . $filename;

        // Ensure the directory exists
        if (!is_dir($directory)) {
            mkdir($directory, 0755, true);
        }

        if (file_put_contents($filePath, $data)) {
            echo 'Image uploaded successfully.';
        } else {
            echo 'Failed to save the image.';
        }
    } else {
        echo 'No cropped image data received.';
    }
} else {
    echo 'Invalid request.';
}

and here is my form and js:

<form id="cropperForm" action="index.php?action=missionControl&nav=missionControl&subnav=yourAccount&do=profile&process=save " method="post" enctype="multipart/form-data">
  <input type="hidden" name="croppedImage" id="croppedImageInput">
  <input type="hidden" name="filename" value="profile_photo.png">

  <link rel="stylesheet" href="https://website.com/resources/general/web-functions/cropperjs/docs/css/cropper.css">
  <link rel="stylesheet" href="https://website.com/resources/general/web-functions/cropperjs/docs/css/main.css">	

  <div>
    <div class="container text-center">
      <div class="row justify-content-center">
        <div class="col-md-9">
          <div class="docs-demo">
            <div class="img-container" style="max-height: 400px;">
              <img id="imageToCrop" src="https://website.com/resources/general/mission-control/website-account/images/user-holding.png">
            </div>
          </div>
        </div>
      </div>
      
      <div class="row justify-content-center" id="actions">
      <div class="col-md-12 docs-buttons d-flex justify-content-center flex-wrap">
        <!-- Button Groups -->
        <div class="btn-group">
          <button type="button" class="btn btn-main-custom" data-method="zoom" data-option="0.1" title="Zoom In">
            <span class="docs-tooltip" data-toggle="tooltip" title="Zoom In">
              <span class="fa-light fa-magnifying-glass-plus"></span>
            </span>
          </button>
          <button type="button" class="btn btn-main-custom" data-method="zoom" data-option="-0.1" title="Zoom Out">
            <span class="docs-tooltip" data-toggle="tooltip" title="Zoom Out">
              <span class="fa-light fa-magnifying-glass-minus"></span>
            </span>
          </button>
        </div>

        <div class="btn-group">
          <button type="button" class="btn btn-main-custom" data-method="move" data-option="-10" data-second-option="0" title="Move Left">
            <span class="docs-tooltip" data-toggle="tooltip" title="Move Left">
              <span class="fa-duotone fa-light fa-arrow-left"></span>
            </span>
          </button>
          <button type="button" class="btn btn-main-custom" data-method="move" data-option="10" data-second-option="0" title="Move Right">
            <span class="docs-tooltip" data-toggle="tooltip" title="Move Right">
              <span class="fa-duotone fa-light fa-arrow-right"></span>
            </span>
          </button>
          <button type="button" class="btn btn-main-custom" data-method="move" data-option="0" data-second-option="-10" title="Move Up">
            <span class="docs-tooltip" data-toggle="tooltip" title="Move Up">
              <span class="fa-duotone fa-light fa-arrow-up"></span>
            </span>
          </button>
          <button type="button" class="btn btn-main-custom" data-method="move" data-option="0" data-second-option="10" title="Move Down">
            <span class="docs-tooltip" data-toggle="tooltip" title="Move Down">
              <span class="fa-duotone fa-light fa-arrow-down"></span>
            </span>
          </button>
        </div>

        <div class="btn-group">
          <button type="button" class="btn btn-main-custom" data-method="rotate" data-option="-45" title="Rotate Left">
            <span class="docs-tooltip" data-toggle="tooltip" title="Rotate Left">
              <span class="fa-light fa-arrow-rotate-left"></span>
            </span>
          </button>
          <button type="button" class="btn btn-main-custom" data-method="rotate" data-option="45" title="Rotate Right">
            <span class="docs-tooltip" data-toggle="tooltip" title="Rotate Right">
              <span class="fa-light fa-arrow-rotate-right"></span>
            </span>
          </button>
        </div>

        <div class="btn-group">
          <button type="button" class="btn btn-main-custom" data-method="scaleX" data-option="-1" title="Flip Horizontal">
            <span class="docs-tooltip" data-toggle="tooltip" title="Flip Left/Right">
              <span class="fa-light fa-arrows-left-right"></span>
            </span>
          </button>
          <button type="button" class="btn btn-main-custom" data-method="scaleY" data-option="-1" title="Flip Vertical">
            <span class="docs-tooltip" data-toggle="tooltip" title="Flip Up/Down">
              <span class="fa-sharp fa-light fa-arrows-up-down"></span>
            </span>
          </button>
        </div>

        <div class="btn-group">
          <label class="btn btn-main-custom btn-upload" for="inputImage" title="Upload image file">
            <input type="file" class="sr-only" id="inputImage" name="file" accept="image/*">
            <span class="docs-tooltip" data-toggle="tooltip" title="Upload Image">
              <span class="fa-light fa-upload"></span>
            </span>
          </label>
        </div>
      </div>

      <div class="col-md-3 docs-toggles"></div>
    </div>
    </div>
  </div>

  <style>
    /* Ensure the crop area and preview are circular */
    .cropper-view-box,
    .cropper-face {
      border-radius: 50%; /* Circular crop area */
    }
  </style>

  <script src="https://unpkg.com/jquery@3/dist/jquery.slim.min.js" crossorigin="anonymous"></script>
  <script src="https://website.com/resources/general/web-functions/cropperjs/docs/js/cropper.js"></script>

  <script>
    document.addEventListener('DOMContentLoaded', function () {
  const Cropper = window.Cropper;
  const image = document.getElementById('imageToCrop');
  const inputImage = document.getElementById('inputImage');
  const form = document.getElementById('cropperForm');
  const croppedImageInput = document.getElementById('croppedImageInput');
  let cropper;
  let scaleX = 1; // Track horizontal flip
  let scaleY = 1; // Track vertical flip

  // Initialize Cropper.js
  cropper = new Cropper(image, {
    aspectRatio: 1, // Ensures square cropping, but will appear circular due to CSS
    viewMode: 1, // Restrict crop to image boundaries
    dragMode: 'move', // Allow moving the image
    cropBoxResizable: false, // Fixed crop box size
    cropBoxMovable: false, // Prevent moving crop box
    toggleDragModeOnDblclick: false, // Disable toggling drag mode
    ready: function () {
      console.log('Cropper is ready.');
    },
  });

  // Attach toolbar actions
  document.querySelectorAll('[data-method]').forEach((button) => {
    button.addEventListener('click', function () {
      const method = this.getAttribute('data-method');
      const option = this.getAttribute('data-option');
      const secondOption = this.getAttribute('data-second-option');

      if (cropper && method) {
        if (method === 'scaleX') {
          // Handle horizontal flip
          scaleX = -scaleX;
          cropper.scaleX(scaleX);
        } else if (method === 'scaleY') {
          // Handle vertical flip
          scaleY = -scaleY;
          cropper.scaleY(scaleY);
        } else {
          // Handle other actions
          const parsedOption = option ? parseFloat(option) : undefined;
          const parsedSecondOption = secondOption ? parseFloat(secondOption) : undefined;

          if (parsedSecondOption !== undefined) {
            cropper[method](parsedOption, parsedSecondOption);
          } else {
            cropper[method](parsedOption);
          }
        }
      }
    });
  });

  // Handle new image uploads
  inputImage.addEventListener('change', function () {
    const files = this.files;

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

      if (/^image\/\w+/.test(file.type)) {
        const uploadedImageURL = URL.createObjectURL(file);
        image.src = uploadedImageURL;

        image.onload = function () {
          cropper.destroy(); // Destroy previous Cropper instance
          cropper = new Cropper(image, {
            aspectRatio: 1,
            viewMode: 1,
            dragMode: 'move',
            cropBoxResizable: false,
            cropBoxMovable: false,
            toggleDragModeOnDblclick: false,
          });
        };
      } else {
        alert('Please choose a valid image file.');
      }
    }
  });

  // Handle form submission
  form.addEventListener('submit', function (event) {
    event.preventDefault();

    if (cropper) {
      const croppedCanvas = cropper.getCroppedCanvas({
        width: 250,
        height: 250,
        fillColor: '#fff', // Background color for transparency
      });

      if (croppedCanvas) {
        // Add Base64 cropped image to hidden input field
        croppedImageInput.value = croppedCanvas.toDataURL('image/png');

        // Submit the form
        form.submit();
      } else {
        alert('Failed to generate cropped image.');
      }
    } else {
      alert('Cropper is not initialized.');
    }
  });
});

  </script>

  <div style="padding-bottom:1em;"></div>
  <div style="padding-bottom:1em;">
    <button type="submit" class="btn btn-main-custom" style="width:100%;">
      SAVE PROFILE PHOTO NOW&nbsp;&nbsp;&nbsp;<i class="fa-sharp fa-light fa-map-location"></i>&nbsp;<i class="fa-light fa-chevrons-right"></i>
    </button>
  </div>
</form>

Any help with this would be great… I have a feeling that this is a pretty easy fix if you know what to do.

mrmbarnes

Hi @mrmbarnes, hm I just tested your code and the cropped image seams to get uploaded just fine. Are there any extra steps required to reproduce the issue?

1 Like

Thank you so much for testing my code… from your response I have also tested the code with no other code and it worked the first time but then not after that… after some further investigation I worked out that my server seems to be caching the last image uploaded so I updated the upload code and it now works correctly… thanks for your help!

1 Like

Ah yeah, the cache. ^^ Glad you got it sorted!