Slim Image Cropper

Five star rating4.94 out of 5 stars based on 287 reviews

Price
$89
Includes
  • future updates
  • 6 months professional support

Slim is a cross platform JavaScript Image Cropper. It's easy to setup and features beautiful graphics and animations.

Your users will be cropping and rotating images in no time.

Feature overview:

Works standalone but ships with handy wrappers for jQuery, React, Angular (1.x and newer versions), VueJS, Bootstrap and Foundation.

Slim image cropper is no longer available for purchase.

Slim image cropper has been discontinued in favor of Pintura Image Editor and CropGuide Image Cropper.

Pintura is a JavaScript Image Editor SDK similar to Slim, it offers cropping functionality and much more.
Learn more about Pintura

CropGuide is a NoCode Image Cropper, it is the easiest way to add image cropping to a file upload field, it doesn't require any code changes.
Learn more about CropGuide

What Our Customers Say...

One of the best downloads I ever made. Design is top notch. The customer support is excellent.
StefanSwe
Great, easy to use plugin for image upload and cropping. Sleek design that will fit the look and feel of any modern website. Had some issues with implementing into a complicated AJAX form and the customer support was superb!
BlackSnowDigital
Worth every cent. Does the job as promised. Had some server-side problems setting it up, but the support was pure gold. Even got some extra coding help.
Lauripaasio

Let's take a look and see why Slim Image Cropper is super awesome!

Drag, Drop 'n Crop

Let's give Slim a test run, drop an image in the Slim drop area below.

If you're old school, clicking the drop area will work just as well.

<div class="slim"
     data-ratio="16:9"
     data-service="async.php"
     data-fetcher="fetch.php"
     data-size="640,640">
    <input type="file" name="slim[]"/>
</div>

Edit Server Images

Got images on your server that are in need of some cropping? Put an img within the Slim element and it will automatically be made ready for cropping.

<div class="slim"
     data-service="async.php"
     data-fetcher="fetch.php"
     data-ratio="3:2"
     data-size="600,400"
     data-max-file-size="2">
    <img src="./media/stars.jpg" alt=""/>
    <input type="file" name="slim[]"/>
</div>

Form Post

In the first demo the content was posted using AJAX, in this one, well use the good old form to post our cropped image.

Note that we prevent form submission with the required attribute on the input file tag.

Actual post is disabled for privacy reasons

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

    <div class="slim"
         data-label="Drop your avatar here"
         data-fetcher="fetch.php"
         data-size="240,240"
         data-ratio="1:1">
        <input type="file" name="slim[]" required />
    </div>

    <button type="submit">Upload now!</button>

</form>

Easy Configuration

Slim can be setup using only HTML. See the attribute overview below for an outline of Slims configuration options.

Attribute Description
data-ratio

What ratio should the crop be in, default ratios are all supported "16:10", "16:9", "5:3", "5:4", "4:3", "3:2" and "1:1"

Custom ratios can also be set, Slim will calculate the correct container size automatically. e.g. "14:2"

Set to "free" to allow the user to pick their own crop size.

Set the value to "input" to force the input image ratio.

The default value is "free"

<div class="slim"
     data-ratio="1:1">
    <img src="./media/sea.jpg" alt=""/>
</div>
data-size

Determine the target size of the resulting image.

For example "320,320" will scale down the image to fit within those dimensions. The aspect ratio of the image will be respected.

By default Slim does not resize the output image.

<div class="slim"
     data-size="640,640"
     data-service="async.php">
    <input type="file" name="slim[]"/>
</div>
data-min-size

Determine minimum size of the crop in pixels

For example "900,400" will make sure the cropped image is at least 900 pixels by 400 pixels big.

If the minimum width defined is bigger than the height of the image the rotation button will only rotate the image by 180 degrees instead of 90.

By default Slims minimum size is 100 by 100 pixels this prevents the controls of the cropper from overlapping.

<div class="slim"
     data-min-size="900,400">
    <img src="./media/dune.jpg" alt=""/>
</div>
data-force-size

Determine the forced output size of the resulting image.

For example "320,320" will scale the image to fit those dimensions.

Force size will also set the aspect ratio to fit the supplied size, so with force-size set to "320,320" this would be "1:1".

By default Slim does not resize the output image.

data-force-min-size

Set to "false" to allow output images to be smaller than required input.

Default is "true"

data-filter-sharpen

Slim can sharpen images after they've been resized, this can be used to highlight colors and high contrast edges. This can lead to a slight improvement in image clarity. Takes a value between 0 and 100, best subtle results are achieved with values between 10 and 20.

By default Slim does not sharpen images.

data-service

When set, the cropped image will be sent to the set URL using AJAX. Slim ships with an example upload PHP file which is also used on this website (async.php).

An upload button will appear unless the data-push property is set to "true". In which case Slim automatically uploads images.

The server can optionally return a JSON response to indicate a successful upload.

{
    "status":"success",
    "name":"uid_filename.jpg",
    "path":"path/uid_filename.jpg"
}

You can also set a function reference, in that case Slim will call the function passing the following parameters.

function(formdata, progress, success, failure) {

    // "formdata" is a FormData object ready to be send to the server
    // more on working with formdata can be found here:
    // https://developer.mozilla.org/en-US/docs/Web/API/FormData

    // "progress(current, total)" is a function you can call to update the progress indicator
    // it expects the uploaded amount of bytes and the total bytes as parameters
    progress(500, 1000); // will put the progress indicator at 50%

    // "success(response)" should be called after the upload is done, expects a response object or string
    success("upload done");

    // "error(message)" should be called in case of upload problems, expects a string
    failure("something went wrong");

}

By default the image information is posted to the server without AJAX.

data-service-format

If the data-service attribute is set to a function you can use data-service-format to define how Slim will pass the data to the function.

By default Slim will send data as a FormData Object. Set this attribute to "file" and Slim will send an array of files instead. If only output data is requested then the array will only contain one file, if input and output data is requested it will contain the input file first and then the output file.

data-fetcher

By default Slim will not load dropped URLs as it needs a server script to do so.

The data-fetcher attribute can be given a url to the server script that loads remote images. The Slim cropper ships with a fetch.php file which handles remote image fetching.

Configuring Slim like shown below will enable the URL drop feature.

<div class="slim"
     data-fetcher="fetch.php">
    <input type="file" name="slim[]"/>
</div>

By default no remote fetch service is defined.

data-upload-base64

Determines if the asynchronous upload method (data-service) sends base64 data or sends a file object.

"false"

data-upload-method

The default request method used to asynchronously send data to the server.

"POST"

data-meta-*

Use this attribute to send additional data to the server.

For example setting data-meta-user-id to "1234" wil send along that user id to the server which can then be accessed on the image object as shown below.

// get the image data
$images = Slim::getImages();
$image = $images[0];

// print metadata
echo $image['meta']->userId; // "1234"

See Sending Additional data to the server for more information.

data-push

When set to "true" will hide the upload button and will automatically upload cropped images to the server.

Default is "false"

data-post

What information to send to the server.

The original input image, the cropped output image and/or the actions (crop position and size).

Can be set as a comma separated list "input, output, actions".

Note that when sending the input image along the resulting upload size can potentially get two times as big as the original image (input + output).

Input and output metadata like filename, size, width and height will always be sent.

By default only the "output" and the user "actions" are sent.

data-jpeg-compression

The amount of JPEG compression to apply to cropped JPEG images. Ranges from 0 (for high compression) to 100 (no compression).

By default Slim uses the browser JPEG compression value which is around 90%.

data-internal-canvas-size

Some devices have limited memory and therefor cannot load super high resolution images. To cope with big amounts of data you can set the maximum internal canvas size Slim uses to load data, images that are bigger are scaled down to this size.

By default the value is set to "4096,4096" which should be okay for most devices. If you notice problems (images not showing) you can attempt to set it to a lower value.

By default Slim uses a maximum of 4096 by 4096 pixels.

data-force-type

Force output type, set to either "png", or "jpg" and Slim will replace the extension and save file in the given format.

By default Slim tries to detect the file type, if it cannot be determined it falls back to "png".

Default is "false"

data-default-input-name

The name of the default input field. The default value is in array format so multiple Slim croppers can post to the same input name. This is only used if there's no hidden input to write to and no service url has been set.

By default this is set to "slim[]"

data-save-initial-image

When set to "true" Slim will save the initially loaded image to the hidden input.

Default is "false"

data-download

When set to "true" shows a button to download the cropped image.

Default is "false"

<div class="slim"
     data-download="true">
    <img src="./media/sea.jpg" alt="">
</div>
data-rotate-button

When set to "false" will disable the rotation button in the image editor popup.

Default is "true"

data-edit

When set to "false" will disable the edit button, effectively turning Slim into an auto cropping upload tool.

Default is "true"

data-instant-edit

When set to "true" will open the editor immidiately on file drop or load.

Default is "false"

data-crop

Pass initial crop coordinates. The format is a comma separated list of coordinates (x, y, width, height).

Setting "0,0,100,100" will crop a 100 pixel square starting from the top left corner.

Please not that this only crops the initial image. Subsequent images will not be cropped with these settings but will use auto crop. Use case could be an avatar upload form. Where you might want to allow the user to edit the previously uploaded original image instead of the cropped version but do want to show the cropped preview.

data-rotation

Pass rotation for initial image. When set, has to be either "90", "180" or "270".

Please not that this only rotates the initial image.

data-copy-image-head

Set to "true" to copy JPEG image header information to the output image.

Default is "false"

data-device-pixel-ratio

The device pixel ratio used to render the images.

Set to "auto" to automatically detect the pixel ratio. Higher resolution preview images might have a significant performance impact.

Default is "1"

data-popover-class-name

Add custom class name to popover editor.

data-label

The label shown in the drop area.

"Drop your image here"

<div class="slim"
     data-label="Files please...">
    <input type="file" name="slim[]">
</div>
data-label-loading

The label shown in the drop area while loading.

"Image loading..."

data-button-*-label

Replace the star with edit, remove, download, upload, cancel or confirm to set the label for the related button.

For example data-button-edit-label.

Note that the label is only shown on the cancel and confirm buttons in the image editor popup. The other buttons feature an icon.

data-button-*-title

Replace the star with edit, remove, download, upload, rotate, cancel or confirm to set the title for the related button.

By default the title contains the same value as the label

data-button-*-class-name

Useful for when you want to add additional class names to on of the action buttons.

data-max-file-size

The maximum file size the user is allowed to upload in megabytes. A value of 3.5 would limit the images to 3.5 megabytes. Keep in mind that you'll also have to configure your server to accept certain file sizes.

By default no limit is set on file size

data-status-file-size

The status text shown when a user tries to upload a file that's too big.

The default is: "File is too big, maximum file size: $0 MB."

The $0 will be replaced by Slim with the value of the data-max-file-size attribute.

data-status-file-type

The status text shown when a user tries to upload an invalid file. You can set the allowed file types using the accept attribute on the input element. If it's not set, all generally supported image types are allowed (jpeg, png, gif and bmp).

<input type="file" accept="image/jpeg">

You can also supply multiple mime types by comma separating them: "image/jpeg, image/png"

The default text is as follows: "Invalid file type, expects: $0."

The $0 is replaced with the extensions of the mime types set in the accept property.

data-status-no-support

The status text shown when the user is not running a modern web browser.

The following browsers and devices are supported.

  • Firefox
  • Chrome
  • Opera
  • Internet Explorer 10+
  • Safari OSX & iOS (Safari on Windows is no longer supported by Apple)
  • Android (Not all Android devices behave the same, should work on most modern Android devices)

On very old browsers (older than Internet Explorer 8), Slim won't load due to lack of JavaScript functionality.

The default text reads as follows: "Your browser does not support image cropping."

data-status-image-too-small

The status text shown when the user is trying to load an image that is smaller than the value set in data-min-size.

"Image is too small, minimum size is: $0 pixels."

The $0 is replaced with the size defined in the data-min-size attribute.

data-status-content-length

The status text shown when the user uploads a file that is too big for the server to handle. Slim tries to interpret the error page the server returns if it contains the term "Content-Length" it will assume it's because the image is too big.

Keep in mind that while the input file may fall below the limit of your server, the total uploaded bytes could possibly be higher (if you for example send both "input" and "output" to the server).

The default text reads as follows: "The file is probably too big"

data-status-unknown-response

The error text shown when the server returns an unknown response.

Default: "An unknown error occurred"

data-status-upload-success

The status text shown when the image is uploaded correctly.

Default: "Saved"

data-did-init

A callback method that gets called when Slim has initialised.

The function arguments consist of a single object reflecting the current crop state.

function slimInitialised(data) {
    console.log(data);
}
<div class="slim"
     data-did-init="slimInitialised">
    <input type="file" name="slim[]"/>
</div>
data-did-load

This callback method is called when an image was loaded and passed the default input validation methods.

The return value of the did-load callback determines what Slim does next.

return true All is fine, and Slim continues with this image.

return 'error message' All is not fine, Slim will halt and display the given error message.

return false All is not fine, the image will not be loaded but Slim will not throw an error.

This setup makes it possible to do custom validation. For instance, the validation rule below checks if the dropped image contains "warm" enough colors. Dropping images with mostly blue, green or purple tones will result in an error.

<div class="slim"
     data-service="async.php"
     data-size="500,500"
     data-did-load="isHotEnough">
    <input type="file" name="slim[]">
</div>

<script>
function isHotEnough(file, image, meta) {

    // average image color
    var averageColor = averagePixelColor(image);

    // color to HSL
    var color = rgbToHsl(averageColor);

    // does the hue part fall in the warm range
    // and is the image not too dark or bright
    if ((color.h > 300 || color.h < 60) &&
            (color.l > 10 && color.l < 90)) {
        return true;
    }

    return 'This image is just not hot enough.';
}
</script>
data-did-transform

This callback method is called after the image has just been transformed (cropped or otherwise updated). The only passed parameter contains the current crop state.

data-will-crop-initial

This callback method is called before the initial crop is made.

It receives the image file as only parameter and expects an initial crop rectangle as result.

<div class="slim"
     data-service="async.php"
     data-will-crop-initial="determineInitialCropRect">
    <input type="file" name="slim[]">
</div>

<script>
function determineInitialCropRect(file, done) {

    // determine the initial crop rectangle based on the input file
    var rect = {
        x: 0,
        y: 0,
        width: 100,
        height: 100
    };

    // return the rectangle back to Slim
    done(rect);
}
</script>
data-will-transform

A callback method that gets called after each image transform.

Slim sends the following two parameters along: data and ready. The data parameter contains the current crop state. The ready parameter is a function that when called will let Slim continue working with the given data.

When calling the ready method you need to pass the altered (or original) crop state object along like this ready(data).

You could for example use this method to add watermarks to images, or mask images, like shown in the demo below.

<div class="slim"
     data-service="async.php"
     data-size="500,500"
     data-will-transform="addTextWatermark">
    <input type="file" name="slim[]">
</div>

<script>
function addTextWatermark(data, ready) {

    // get the drawing context for the output image
    var ctx = data.output.image.getContext('2d');

    // draw our watermark on the center of the image
    var size = data.output.width / 20
    ctx.font = size + 'px sans-serif';

    var x = data.output.width * .5;
    var y = data.output.height * .5;
    var text = ctx.measureText('Slim is Awesome');
    var w = text.width * 1.15;
    var h = size * 1.75;

    ctx.fillStyle = 'rgba(0,0,0,.75)';
    ctx.fillRect(
        x - (w * .5),
        y - (h * .5),
        w, h
    );
    ctx.fillStyle = 'rgba(255,255,255,.9)';
    ctx.fillText(
        'Slim is Awesome',
        x - (text.width * .5),
        y + (size * .35)
    );

    // continue saving the data
    ready(data);

}
</script>

Here we apply a mask to the image in the will-transform callback

<div class="slim"
     data-service="async.php"
     data-size="500,500"
     data-ratio="1:1"
     data-will-transform="addMask">
    <input type="file" name="slim[]">
</div>

<script>
function addMask(data, ready) {

    // get the drawing context for the output image
    var ctx = data.output.image.getContext('2d');

    // only draw image where mask is
    ctx.globalCompositeOperation = 'destination-in';

    ctx.fillStyle = 'black';
    ctx.beginPath();
    ctx.arc(
        data.output.width * .5,
        data.output.height * .5,
        data.output.width * .5,
        0, 2 * Math.PI);
    ctx.fill();

    // restore to default composite operation (is draw over current image)
    ctx.globalCompositeOperation = 'source-over';

    // continue saving the data
    ready(data);

}
</script>

In this example we add a watermark to the image.

<div class="slim"
     data-service="async.php"
     data-size="500,500"
     data-will-transform="addImageWatermark">
    <input type="file" name="slim[]">
</div>

<script>
function addImageWatermark(data, ready) {

    // load the image, in this case the PQINA logo
    var watermark = new Image();
    watermark.onload = function() {

        // set watermark x and y offset to 5% of output image width
        var offset = data.output.width * .05;

        // set watermark width to 25% of the output image width
        var width = data.output.width * .25;
        var height = width * (this.naturalHeight / this.naturalWidth);

        // get the drawing context for the output image
        var ctx = data.output.image.getContext('2d');

        // make watermark transparant
        // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalAlpha
        ctx.globalAlpha = .75;

        // have watermark blend with background image
        // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
        ctx.globalCompositeOperation = 'multiply';

        // draw the image
        ctx.drawImage(watermark, offset, offset, width, height);

        // continue saving the data
        ready(data);

    }

    watermark.src = './static/assets/pqina-logo.svg';

}
</script>
data-will-save

Similar to the data-will-transform attribute, but data-will-save is only used just before saving the crop information and does not contain the original canvas elements but instead contains base64 formatted information.

Keep in mind that, as with the data-will-transform function, this function also relies on you calling the ready callback and returning the crop state object to Slim.

When adding custom properties to the crop state object, make sure they are all contained in a parent property called meta as other properties will be cleaned up by the Slim PHP script.

data-did-save

Called after the current crop object was saved. Receives the crop state object as it's only parameter.

data-did-confirm

Called after the user pressed the image editor confirm button.

data-did-cancel

Called after the user pressed the image editor cancel button.

data-did-upload

Callback function that gets called when Slim uploads a file and the server returns with a success or failure.

If something went wrong the error parameter contains the error information returned from the server. If the request is successful the response parameter contains the server response (if the server returned a JSON string this will be a JSON object).

function imageUpload(error, data, response) {
    console.log(error, data, response);
}
<div class="slim"
     data-did-upload="imageUpload">
    <input type="file" name="slim[]"/>
</div>
data-did-receive-server-error

By default Slim will render the "unknown error" response if the server triggers an error (if the status property in the response object equals 'failure'). If you want more fine control over what server error you show you can use this callback method.

The error returned in the callback is the error message Slim will show in the visual status message.

/* Example Server JSON Response
{
    status: "failure",
    message: "The server is having a bad day"
}
*/

function handleServerError(error, defaultError) {

    // the error parameter is equal to string set
    // to message property of server response object
    // in this case 'The server is having a bad day'
    console.log(error);

    // the defaultError parameter contains the
    // message set to data-status-unknown-response
    console.log(defaultError);

    return error;
}
<div class="slim"
     data-did-receive-server-error="handleServerError">
    <input type="file" name="slim[]"/>
</div>
data-will-remove

A callback method that can be used to block the actual removal of the image. This allows you to setup a "please confirm image removal" message.

function imageWillBeRemoved(data, remove) {
    if (window.confirm("Are you sure?")) {
        remove();
    }
}

This method will not be called when the API remove() method is used.

data-will-request

A callback method that can be used to add additional request headers to the upload XMLHttpRequest object.

data-will-fetch

A callback method that can be used to add additional request headers to the fetch XMLHttpRequest object.

data-will-load

A callback method that can be used to add additional request headers to the load local image XMLHttpRequest object.

<div class="slim"
     data-service="async.php"
     data-size="400,300"
     data-will-request="handleRequest">
    <input type="file" name="slim[]">
</div>

<script>
function handleRequest(xhr) {
    xhr.setRequestHeader('X-Custom-Header', 'Some value');
}
</script>
data-did-remove

A callback method that allows you to detect when the user has removed an image.

Slim returns the previous crop state object that would have otherwise been send to the server.

function imageRemoved(data) {
    console.log(data);
}
<div class="slim"
     data-did-remove="imageRemoved">
    <input type="file" name="slim[]"/>
</div>
data-did-throw-error

A callback method that receives all visual errors thrown by Slim. Receives the error as only parameter.

function handleError(error) {
    console.log(error);
}
<div class="slim"
     data-did-throw-error="handleError">
    <input type="file" name="slim[]"/>
</div>

JavaScript API

If you're using jQuery, Slim automatically sets up the jQuery API for your convenience. Want to go Vanilla? No problem at all.

Creating a new cropper

Here's how you'd turn the following HTML into a Slim cropper.

<input type="file" id="myCropper"/>

Using good ol' JavaScript.

new Slim(document.getElementById('myCropper'));

And this is how you'd do that with the jQuery API.

$('#myCropper').slim();

Setting custom options

The JavaScript/jQuery API unlocks the same set of options as the HTML API.

Replace the * in the button properties with one of the following buttons to alter it's properties.

An example code snippet passing custom options in JavaScript.

new Slim(element, {
    ratio: '4:3',
    minSize: {
        width: 640,
        height: 480,
    },
    crop: {
        x: 0,
        y: 0,
        width: 100,
        height: 100
    },
    service: 'upload-async.php',
    download: false,
    willSave: function(data, ready) {
        alert('saving!');
        ready(data);
    },
    label: 'Drop your image here.',
    buttonConfirmLabel: 'Ok',
    meta: {
        userId:'1234'
    }
});

The options object is identical.

$(selector).slim({
    ratio: '4:3',
    minSize: {
        width: 640,
        height: 480,
    },
    crop: {
        x: 0,
        y: 0,
        width: 100,
        height: 100
    },
    service: 'upload-async.php',
    download: false,
    willSave: function(data, ready) {
        alert('saving!');
        ready(data);
    },
    label: 'Drop your image here.',
    buttonConfirmLabel: 'Ok',
    meta: {
        userId:'1234'
    }
});

Initialising Slim within a HTML snippet

<div id="my-snippet">

    <!-- my first cropper -->
    <input type="file" class="slim"/>

    <!-- my second cropper -->
    <input type="file" class="slim"/>

</div>

Below two examples on how to turn the elements with class slim into Slim croppers.

Using JavaScript with we select the parent element using the getElementById method.

Slim.parse(document.getElementById('my-snippet'));

In jQuery we can just pass the parent selector to the jQuery method.

$('#my-snippet').slim('parse');

Destroying a Slim image cropper

If you've manually created a Slim image cropper using the JavaScript/jQuery API you can destroy it using the destroy function.

The API exposes a destroy method.

// Create a new cropper
var cropper = new Slim(document.getElementById('my-cropper'));

// Destroy the cropper
cropper.destroy();

If you want to destroy a cropper that was initialised automatically (or by using the parse method) you can use the destroy method on the Slim Class itself.

// Destroy cropper on element
Slim.destroy(document.getElementById('my-cropper'));

If you're using jQuery creating and destroying croppers can be done with a single destroy call.

// Create cropper(s)
var croppers = $('#my-cropper').slim(options);

// Destroy cropper(s)
croppers.slim('destroy');

Manual loading and uploading of images

// Create a new cropper
var cropper = new Slim(document.getElementById('my-cropper'));

// Load image by url or pass a File object
// - The error parameter contains error messages
cropper.load('path/to/image.jpg', function(error, data){

    // Done loading the image!

    // Upload a selected image to the server
    cropper.upload(function(error, data, response){

        // Done uploading!

    });

});

Summary of instance API methods

Create a new Slim Cropper.

var cropper = new Slim(document.getElementById('my-cropper'));

Test if Slim is bound to given element.

var state = cropper.isAttachedTo(element);

Test if Slim is still bound to DOM.

var state = cropper.isDetached();

Upload an image by url or data-uri.

// basic implementation
cropper.load(src);

// if you need to know when the image has loaded, pass a function
// as the second parameter
cropper.load(src, function(error, data) {
    // image load done!
});

// you can also pass an options object
// this options object also accepts a rotation and crop parameter
// these options reflect the functionality of the rotation and
// crop options on the slim options object
cropper.load(src, { rotation:90, crop: {} }, function(error, data) {
    // image load done!
});

// if 'push' is set to true, you can pass the blockPush
// property to prevent immediate upload on image load
cropper.load(src, { blockPush:true }, function(error, data) {
    // image load done!
});

Upload currently loaded image

cropper.upload(function(error, data, response) {
// upload done!
});

Download currently loaded image as file

cropper.download();

Request current output data

cropper.requestOutput(function(fileData, formData) {
    // fileData files
    // formData files wrapped in FormData object ready for submit
});

Remove currently loaded image

var data = cropper.remove();

Destroy the cropper

cropper.destroy();

Get current data object

var data = cropper.data;

// format of data object
{
    // server response object after async upload
    server: null,

    // meta data set with data-meta-*
    meta: {},

    // the source file
    input:{
        // input field id
        field: null,

        // name of file
        name: null,

        // mime type of image
        type: null,

        // file loaded
        file: null,

        // file size in bytes
        size: null,

        // image base64 data or canvas
        image: null,

        // original dimensions of image
        width: 0,
        height: 0
    },

    // the result of the crop
    output:{

        // name of file
        name: null,

        // mime type of image
        type: null,

        // image base64 data or canvas
        image: null,

        // output image dimensions
        width: 0,
        height: 0
    },

    // user actions
    actions:{
        // angle of rotation in degrees
        rotation: null,

        // crop coordinates and type
        crop: {
            // coordinates
            x: 0,
            y: 0,
            width: 0,
            height: 0,

            // type of crop
            // - 'auto'
            // - 'initial'
            // - 'manual'
            type: null
        },

        // output size
        size: {
            width: 0,
            height: 0
        },

        // applied filters
        filters: {
            sharpen: 0
        }
    }
}

Get current data object in base64 format

var data = cropper.dataBase64;

Get root element

var element = cropper.element;

Do manual crop, please keep in mind that this method does not check if the input matches the required ratio or image size.

cropper.crop({x:0, y:0, width:100, height:100}, function(result) {
// crop done!
});

Open the editor

cropper.edit();

Set new output size

cropper.size = { width:640, height:480 };

// directly apply with set method
cropper.setSize({ width:640, height:480 }, function(data) {
    // done!
});

set new forced size

cropper.forceSize = { width:640, height:480 };

// directly apply with set method
cropper.setForceSize({ width:640, height:480 }, function(data) {
    // done!
});

Set new ratio

cropper.ratio = '3:2';

// directly apply with set method
cropper.setRatio('3:2', function(data) {
    // done!
});

Set new rotation

cropper.rotation = 90;

// directly apply with set method
cropper.setRotation(90, function(data) {
    // done!
});

Summary of Static API methods

Tests if slims can be used on this browser.

var supported = Slim.supported;

Parses the given DOM context for Slim croppers.

var croppers = Slim.parse(context);

Finds a previously initialised cropper by element.

var cropper = Slim.find(context);

Creates a cropper on the given element.

var cropper = Slim.create(element, options);

Destroys cropper attached to element

var success = Slim.destroy(element);

Create a new Slim Cropper.

var $cropper = $('#my-cropper').slim();

Test if Slim is bound to given element.

var state = $(selector).slim('isAttachedTo', element);

Test if Slim is still bound to DOM.

var state = $(selector).slim('isDetached');

Upload an image by url or data-uri.

// basic implementation
$(selector).slim('load', src);

// if you need to know when the image has loaded, pass a function
// as the second parameter
$(selector).slim('load', src, function(error, data) {
    // image load done!
});

// you can also pass an options object
// this options object also accepts a rotation and crop parameter
// these options reflect the functionality of the rotation and
// crop options on the slim options object
$(selector).slim('load', src, { rotation:90, crop: {} }, function(error, data) {
// image load done!
});

// if 'push' is set to true, you can pass the blockPush
// property to prevent immediate upload on image load
$(selector).slim('load', src, { blockPush:true }, function(error, data) {
// image load done!
});

Upload currently loaded image

$(selector).slim('upload', function(error, data, response) {
    // upload done!
});

Download currently loaded image as file

$(selector).slim('download');

Remove currently loaded image

$(selector).slim('remove');

Destroy the cropper

$(selector).slim('destroy');

Get current data object

$(selector).slim('data');


// format of data object
{
    // server response object after async upload
    server: null,

    // meta data set with data-meta-*
    meta: {},

    // the source file
    input:{
        // input field id
        field: null,

        // name of file
        name: null,

        // mime type of image
        type: null,

        // file loaded
        file: null,

        // file size in bytes
        size: null,

        // image base64 data or canvas
        image: null,

        // original dimensions of image
        width: 0,
        height: 0
    },

    // the result of the crop
    output:{

        // name of file
        name: null,

        // mime type of image
        type: null,

        // image base64 data or canvas
        image: null,

        // output image dimensions
        width: 0,
        height: 0
    },

    // user actions
    actions:{
        // angle of rotation in degrees
        rotation: null,

        // crop coordinates and type
        crop: {
            // coordinates
            x: 0,
            y: 0,
            width: 0,
            height: 0,

            // type of crop
            // - 'auto'
            // - 'initial'
            // - 'manual'
            type: null
        },

        // output size
        size: {
            width: 0,
            height: 0
        },

        // applied filters
        filters: {
            sharpen: 0
        }
    }
}

Get current data object in base64 format

$(selector).slim('dataBase64');

Get root element

$(selector).slim('element');

Do manual crop, please keep in mind that this method does not check if the input matches the required ratio or image size.

$(selector).slim('crop', {x:0, y:0, width:100, height:100}, function(result) {
// crop done!
});

Open the editor

$(selector).slim('edit');

Set new output size

// only set the size
$(selector).slim('size', { width:640, height:480 });

// directly apply, callback method is called on completion
$(selector).slim('setSize', { width:640, height:480 }, function(data) {

});

Set new ratio

// only set a new ratio
$(selector).slim('ratio', '3:2');

// directly apply the ratio
$(selector).slim('setRatio', '3:2', function(data) {

});

Set new ratio

// only set a new rotation
$(selector).slim('rotation', 90);

// directly apply the rotation
$(selector).slim('setRotation', 90, function(data) {

});

Summary of Static API methods

Tests if slims can be used on this browser.

var supported = $().slim('supported');

Parses the given DOM context for Slim croppers.

// Return value is not croppers but nodes parsed as
// it's possible to supply multiple nodes using a
// single selector.
var $elements = $(selector).slim('parse');

Creates a cropper on the given element.

var cropper = $(selector).slim(options);

Destroys cropper attached to element

var success = $(selector).slim('destroy');

PHP API

Slim ships with a static PHP Class which you can use to access the posted data.

Use the getImages() method to quickly get the posted image(s).

$images = Slim::getImages();

If you've setup to use a different name than "slim" pass it along.

$images = Slim::getImages('myFieldName');

If only one image was posted it can be found at index 0.

As the default slim field name ("Slim[]") allows to upload multiple images under the same name the PHP script assumes it receives an array of images.

$images = Slim::getImages();
$image = $images[0];

Store the image on your server with the saveFile() method (ready to use examples of this are included in the package). All the information of the original image can be accessed through the image object returned by the getImages() method.

// let's create some shortcuts
$name = $image['output']['name'];
$data = $image['output']['data'];

// store the file
$file = Slim::saveFile($data, $name);

By default the saveFile method stores the image in the "tmp" folder. If you want to store images in a different folder you can pass the folder name along as the third parameter to the store method.

$file = Slim::saveFile($data, $name, 'myFolder/');

The saveFile() method returns a file object. This object contains the name of the file and the file path. The name of the file will be different from the name of the image as the original image name is prepended with a unique id before saving.

$file = Slim::saveFile($data, $name);
echo $file['name'];
// 573f60ddd630f_imagename.jpg

echo $file['path'];
// tmp/573f60ddd630f_imagename.jpg

If you don't want a unique id call the saveFile() method like shown below.

$file = Slim::saveFile($data, $name, 'myFolder/', false);

Sending Additional Data to the Server

If you want to add custom data to the Slim object you have to use the meta property. The Slim.php script will clean up the data sent to the server but will leave the meta property alone.

Using the data-meta attribute (or option property) you can set static values in String format only. For example setting data-meta-user-id to 1234 wil result in the following additional object being sent to the server.

{
    "meta":{
        "userId":"1234"
    }
}

You can now access the metadata on the server as you would any other object, see the code snippet below.

// get the image data
$images = Slim::getImages();
$image = $images[0];

// print metadata
echo $image['meta']->userId; // "1234"

If you need to add metadata in a certain format (say Number or Boolean) you can add it in the willSave phase. See the example below where we send a timestamp to the server in Number format.

By submitting the form below you can view the metadata in the table rendered by the post.php page.

<script>
function addTime(data, ready) {

    // add additional data
    data.meta.now = Date.now();

    // continue saving the data
    ready(data);

}
</script>

<form action="post.php" method="post" enctype="multipart/form-data">

    <div class="slim"
         data-meta-user-id="1234"
         data-ratio="16:9"
         data-size="600,300"
         data-will-save="addTime">
        <input type="file" name="slim[]">
    </div>

    <button type="submit">Send!</button>

</form>

Form Post or AJAX Upload

If you're planning to send the cropped data to your server using AJAX, add the data-service attribute and supply it with the url to the API endpoint.

If you don't set the data-service attribute Slim will store the data in a automatically created hidden input field named "slim[]". If you add a hidden input field yourself, Slim will use that field. When using AJAX Slim will store the server response in the hidden input field so after a form submit you know which file was uploaded last.

You can change the default name of the automatically created input using the defaultInputName property or the data-default-input-name attribute.

In both situations Slim will prevent the original file input from posting so data is not sent to the server twice.

Look 'n Feel

A large part of Slim can be restyled with basic CSS rules.

Rounded Corners

By default the Slim landing areas do not have rounded corners. Adding the following CSS rule will make them nice and round immidiately.

.slim {
    border-radius: 0.5rem;
}

Create a circular drop area with the following snippet.

.slim {
    border-radius: 50%;
}

Square Buttons

If circular buttons are not your thing, you can turn the Slim edit buttons into squares using the following snippet.

.slim-btn {
    border-radius: 0;
}

Show Buttons on Hover

If you only want to show buttons when the mouse hovers over the drop area.

.slim .slim-btn-group {
    opacity:0;
    transition:.25s opacity;
}

.slim:hover .slim-btn-group {
    opacity:1;
}

White Overlay

The default overlay is dark, you can turn it around to the bright side using the following CSS snippet.

.slim-popover {
    background-color: #efefef;
}

.slim-image-editor-btn {
    color:#999;
}

.slim-image-editor-btn:focus,
.slim-image-editor-btn:hover {
    color:#777;
}

.slim-image-editor-preview::after {
    background-color: rgba(0, 0, 0, 0.25);
}

FontAwesome Icons

By default Slim features SVG icons. If you want to use an icon font use the following snippet.

This example replaces the icon of the edit button with the Font Awesome Cog icon.

<div class="slim"
     data-button-edit-label="<i class='fa fa-cog'></i>"
     data-button-edit-title="Edit">

    ...

</div>

We have to hide the original icon and restore the font-size.

.slim-btn {
    font-size: 1em;
    background-image: none;
}

Text Buttons

If icons are just not your thing, hide the icons with the following CSS snippet.

.slim-btn {
    width:auto;
    font-size:1em;
    padding: 0 1em;
    border-radius: 1em;
    background-image: none;
}

White Buttons

The default buttons are black with a white icon. You can quickly make this white with a black icon by using the invert filter.

.slim-btn {
    filter: invert(100%);
}

Note that this does not work on Internet Explorer 11 and below.

React, Angular 'n Vue

Slim ships with React, Angular 1.x, Angular, and Vue wrappers. These wrappers function as a basic gateway with the single purpose of binding Slim to the supplied node and handling callbacks.

React

React has a custom component syntax. We use the Slim element instead of a class and instead of the data attributes we pass the JavaScript options directly like shown in the example below.

import React, { Component } from 'react';
import Slim from './slim/slim.react';

class App extends Component {

    // called when slim has initialized
    slimInit(data, slim) {
        // slim instance reference
        console.log(slim);

        // current slim data object and slim reference
        console.log(data);
    }

    // called when upload button is pressed or automatically if push is enabled
    slimService(formdata, progress, success, failure, slim) {
        // slim instance reference
        console.log(slim)

        // form data to post to server
        // set serviceFormat to "file" to receive an array of files
        console.log(formdata)

        // call these methods to handle upload state
        console.log(progress, success, failure)
    }

    render() {
        return (
            <div className="App">
                <Slim ratio="1:1"
                      initialImage="test.jpg"
                      minSize={ { width:600, height:400 } }
                      service={ this.slimService.bind(this) }
                      didInit={ this.slimInit.bind(this) }>
                    <input type="file" name="foo"/>
                </Slim>
            </div>
        );
    }
}

Angular 1.x

The Slim directive binds on the "slim" tag, you can add the exact same HTML attributes as in the other code examples.

<div ng-app="myApp" ng-controller="AppController">
    <slim data-size="200,200"
          data-download="true"
          data-initial-image="test.jpg"
          data-service="slim.service"
          data-did-init="slim.init">
        <input type="file" name="slim[]"/>
    </slim>
</div>

An example controller with callback method.

(function(angular){

    var app;
    app = angular.module('myApp', ['slim']);
    app.controller('AppController', function($scope) {

        $scope.slim = {

            // called when slim has initialized
            init: function (data, slim) {
                // slim instance reference
                console.log(slim);

                // current slim data object and slim reference
                console.log(data);
            },

            // called when upload button is pressed or automatically if push is enabled
            service: function (formdata, progress, success, failure, slim) {
                // form data to post to server
                // set data-service-format to "file" to receive an array of files
                console.log(formdata);

                // call these methods to handle upload state
                console.log(progress, success, failure);

                // reference to Slim instance
                console.log(slim);
            }
        }

    });

}(angular));

Angular

Angular is a tiny bit different. Options are passed to the Slim module as an object.

This code is compatible with Angular 2 and up.

When using Angular CLI you'll have to make some changes to the configuration files.

add to /tconfig.js

  • "allowJs": true,
  • "allowUnreachableCode": true,

add to /.angular-cli.json "styles" array

  • "app/slim/slim.css"

For Angular version 7 and lower:

You need the slim.angular2.ts file and the slim.commonjs.js file.

/**
 * app.module.ts
 */
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { Slim } from './slim/slim.angular2'; // import from slim.angular2 file

@NgModule({
    imports: [ BrowserModule ],
    declarations: [ AppComponent, Slim ], // add declaration
    bootstrap: [ AppComponent ]
})

export class AppModule { }

For Angular version 8 and up:

You need the slim.angular.component.ts, slim.angular.module.ts and the slim.commonjs.js file.

/**
 * app.module.ts
 */
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { SlimModule } from './slim/slim.module'; // import from slim.module file

@NgModule({
    declarations: [AppComponent],
    imports: [BrowserModule, SlimModule], // add imports
    providers: [],
    bootstrap: [AppComponent]
})

export class AppModule { }

Usage is the same for all modern Angular versions.

/**
 * app.component.ts
 */
import { Component } from '@angular/core';

@Component({
    selector: 'my-app',
    template: `
        <slim [options]="slimOptions">
            <input type="file" name="foo">
        </slim>
    `
})

export class AppComponent {

    slimOptions = {
        ratio: '1:1',
        download: true,
        initialImage: './test.jpg',
        service: this.slimService.bind(this),
        didInit: this.slimInit.bind(this)
    };

    // called when slim has initialized
    slimInit(data:any, slim:any) {
        // slim instance reference
        console.log(slim);

        // current slim data object and slim reference
        console.log(data);
    };

    // called when upload button is pressed or automatically if push is enabled
    slimService(formdata:any, progress:any, success:any, failure:any, slim:any) {
        // form data to post to server
        // set serviceFormat to "file" to receive an array of files
        console.log(formdata);

        // call these methods to handle upload state
        console.log(progress, success, failure);

        // reference to Slim instance
        console.log(slim);
    };

}

Vue

Options are passed to the Slim module as an object and we use the `slim-cropper` tag.

<template>
    <div>
        <slim-cropper :options="slimOptions">
            <input type="file" name="slim"/>
        </slim-cropper>
    </div>
</template>

<script>
import Slim from './slim/slim.vue'

// called when slim has initialized
function slimInit (data, slim) {
    // slim instance reference
    console.log(slim)

    // current slim data object and slim reference
    console.log(data)
}

// called when upload button is pressed or automatically if push is enabled
function slimService (formdata, progress, success, failure, slim) {
    // slim instance reference
    console.log(slim)

    // form data to post to server
    // set serviceFormat to "file" to receive an array of files
    console.log(formdata)

    // call these methods to handle upload state
    console.log(progress, success, failure)
}

export default {
    name: 'hello',
    components: {
        'slim-cropper': Slim
    },
    data () {
        return {
            slimOptions: {
                ratio: '1:1',
                initialImage: require('../assets/test.jpg'),
                service: slimService,
                didInit: slimInit
            }
        }
    }
}
</script>

Bootrap 'n Foundation

A snippet showing how to use Slim with Bootstrap.

<form>

    <fieldset class="form-group">
        <label for="email">E-mail</label>
        <input type="email" class="form-control" id="email">
    </fieldset>

    <fieldset class="form-group">
        <label for="avatar">Avatar</label>
        <input type="file"
               class="form-control slim"
               data-ratio="1:1"
               id="avatar">
    </fieldset>

</form>

An example of Slim being used in a Foundation form.

<form>

    <label>E-mail
        <input type="email">
    </label>

    <label>Avatar
        <input type="file" class="slim" data-ratio="1:1">
    </label>

</form>

Feature Summary

  • Beautiful Animations
  • Correctly handles Device Orientation
  • Very Fast
  • Includes PHP sample files for both sync and async posting
  • Compatible with both Bootstrap and Foundation
  • Easy to Configure
  • High Quality User Experience
  • Edit Server Images
  • Responsive
  • Vanilla JavaScript and jQuery
  • Contains wrappers for Angular, React and Vue

Slim image cropper is no longer available for purchase and has been discontinued in favour of Pintura image editor.