Actualización

This commit is contained in:
Xes
2025-04-10 12:53:50 +02:00
parent f7a0ba2b2f
commit 2001ceddea
39284 changed files with 991962 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
{
"name": "cropper",
"description": "A simple jQuery image cropping plugin.",
"main": [
"dist/cropper.js",
"dist/cropper.css"
],
"keywords": [
"image",
"crop",
"cropping",
"move",
"zoom",
"rotate",
"scale",
"jquery",
"plugin",
"html",
"css",
"javascript",
"front-end",
"web",
"development"
],
"homepage": "https://github.com/fengyuanchen/cropper",
"authors": [
"Fengyuan Chen"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"tests",
"test",
"examples",
"assets",
"demo",
"docs",
"gulpfile.js",
"CONTRIBUTING.md",
"FAQ.md"
],
"dependencies": {
"jquery": ">= 1.9.1"
},
"devDependencies": {
"bootstrap": "~3.3.5",
"fontawesome": "~4.4.0",
"jquery": "~1.11.3",
"qunit": "~1.18.0"
},
"version": "1.0.0",
"_release": "1.0.0",
"_resolution": {
"type": "version",
"tag": "v1.0.0",
"commit": "c0805bc83c9270a0be4afdfbd02d517de7a4c970"
},
"_source": "https://github.com/fengyuanchen/cropper.git",
"_target": "1.0.*",
"_originalSource": "cropper"
}

View File

@@ -0,0 +1,351 @@
# Changelog
## 1.0.0 (Oct 10, 2015)
- Improved canvas limitation
- Improved preview
- Improved test
- Fixed an error in the `clear` method (missed parameters)
- Fixed the issue of crop box limitaion (#430)
## 1.0.0-rc.1 (Sep 5, 2015)
- Moved from Less to Sass
- Fixed the issue of `destroy` method (#434)
- Fixed the issue on IE8 (#319)
- Added an example for customizing preview
- Added download button to documentation
- Added FAQ
## 0.11.1 (Aug 22, 2015)
- Optimize "built" and "crop" events
- Improve the starting speed (#422)
- Improve the building process (#428)
- Fix event issue on IE8 (#319)
## 0.11.0 (Aug 10, 2015)
- Improve `setCropBoxData` method (#385)
- Fix event issue on IE10 (#394)
- Optimize code (use `var` for per variable)
### Options
- Add "scalable" option
- Add "wheelZoomRatio" option
- Convert "crop" option to "crop" event
### Methods
- Add "scale" method
- Improve "move" method (the `offsetY` parameter is optional now)
### Events
- Rename "dragstart" to "cropstart"
- Rename "dragmove" to "cropmove"
- Rename "dragend" to "cropend"
- Merge "zoomin" and "zoomout" to "zoom"
- Merge "crop" option and "change" event to "crop" event
## 0.10.1 (Jul 5, 2015)
- Add Pointer Events support (#328)
- Add RTL support (#342)
- Add one new option: "center" (#367)
- Allow cropper to grow vertically (#350)
## 0.10.0 (Jun 8, 2015)
- Add three new options: "change", "cropBoxMovable", "doubleClickToggle"
- Change "movable" option (only for image)
- Rename "resizable" to "cropBoxResizable"
- Add one new event: "change.cropper"
- Locking aspect ratio in "free mode" by holding shift key (#259)
- Sync drag mode to crop box when it is not movable (#300)
## 0.9.3 (May 10, 2015)
- Add one new option: "data"
- Add two new methods: "setData" (#290, #296), "crop" (#275)
- Fix incorrect minWidth/Height size of canvas (#295)
- Fix the strict mode bug (#280)
- Fix the crop box resizing bug (#277)
## 0.9.2 (Apr 18, 2015)
- Improve strict mode to show full image
- Add two new options: "minCanvasWidth" and "minCanvasHeight"
- Reverse mouse wheeling zoom
- Fix incorrect cursor in disabled state
## 0.9.1 (Mar 21, 2015)
- Fix the touch zoom issue (#206)
- Fix the reset issue (#246)
## 0.9.0 (Mar 15, 2015)
- Wraps image with a virtual canvas (for zooming and rotating).
- Limits image position and size in strict mode.
- Supports multiple global croppers by default.
- Outputs cropped canvas for display or get Data URL or get Blob
- Identifies drag events with "event.dragType" property
- Added zoom events for controling the canvas (image) size.
- Improved responsiveness for window resizing.
### Options:
- Change "minContainerWidth" (default value: 300 -> 200)
- Change "minContainerHeight" (default value: 150 -> 100)
- Add "strict"
- Add "zoomin"
- Add "zoomout"
- remove "global"
### Methods:
- Change "replace" (not to change the original image any more)
- Change "getImageData" (move rotation-related properties to canvas data)
- Add "getContainerData"
- Add "getCanvasData"
- Add "setCanvasData"
- Add "getCroppedCanvas"
- Remove "setImageData" (replace with "getCanvasData")
- Remove "getDataURL" (replace with "getCroppedCanvas")
### Events:
- Add "event.dragType" property to drag events
- Add "zoomin.cropper"
- Add "zoomout.cropper"
## 0.8.0 (Feb 19, 2015)
- Refactored source code.
- Compiles CSS with [Less](http://lesscss.org) CSS preprocessors.
- Supports fixed container.
- Supports rotation with CSS3 Transform3d.
### Options:
- Change the default value of "aspectRatio"
- Rename "done" to "crop"
- Rename "dashed" to "guides"
- Rename "multiple" to "global"
- Add "background"
- Add "highlight"
- Add "responsive"
- Add "mouseWheelZoom"
- Add "touchDragZoom"
- Add "minCropBoxWidth"
- Add "minCropBoxHeight"
- Add "minContainerWidth"
- Add "minContainerHeight"
- Remove "data"
- Remove "minWidth"
- Remove "minHeight"
- Remove "maxWidth"
- Remove "maxHeight"
### Methods:
- Change "reset"
- Add "setImageData"
- Add "getCropBoxData"
- Add "setCropBoxData"
- Add "move"
- Remove "setData"
## 0.7.9 (Feb 19, 2015)
- Improve preview.
- Improve rotation.
- Improve responsiveness (#157).
- Enable to move the image when the size of the crop box is the same as the container's (#186).
## 0.7.8 (Feb 8, 2015)
- Add two new options: "minContainerWidth" and "minContainerHeight".
- Improve three methods: "setAspectRatio", "destroy" and "disable".
- Improve mouse wheel zoom.
- Improve drag resizing.
## 0.7.7 (Jan 10, 2015)
- Fix a bug of "dragCrop" option.
- Add a timestamp to the url to bust cache when it's a cross-origin image (#148).
- Fix the issue of "autoCropArea" option (#144).
## 0.7.6 (Dec 20, 2014)
- Fix events binding bugs.
- Change the "done" option and the "getData" method (returns floating-point number) (#130).
- Fix the rotation issue after replace the image (#139).
## 0.7.5 (Nov 27, 2014)
- Reset the ratio when replace the image.
- Add a new option: "checkImageOrigin" (#119).
- Prevent to call the "done" option when it's disabled (#107).
- Improve the preview (#95).
## 0.7.4 (Nov 24, 2014)
- Improve "getDataURL" method, enable to customize the image sizes (#105).
- Fix the issue of destory (#101).
- Fix the issue of canvas (#106).
## 0.7.3 (Nov 15, 2014)
- Supports cross-origin image (#96, #97).
- Add a new option: "autoCropArea".
- Improve "movable" option.
- Output rotation degree by "getImageData" method (#94).
## 0.7.2 (Nov 11, 2014)
- Fix the image rotation error in Firefox (#92).
## 0.7.1 (Nov 8, 2014)
- Rebuild "rotate" method (#88).
- Fix the issue of free ratio (#87).
- Improve "getDataURL" method (#86).
- Optimize event listeners.
## 0.7.0 (Oct 12, 2014)
- Supports zoom (#36, #79).
- Supports rotation (#1, #81).
- Add two new options: "zoomable" and "rotatable".
- Add six new methods: "enable", "disable", "zoom", "rotate", "getDataURL" (#80) and "setDragMode".
- Rename "release" method to "clear".
- Rename "setImgSrc" method to "replace".
- Rename "getImgInfo" method to "getImageData".
- Some other improvements.
## 0.6.2 (Oct 11, 2014)
- Hide the modal when release the crop box.
- Improve touch events.
## 0.6.1 (Oct 3, 2014)
- Fix an event error.
## 0.6.0 (Sep 20, 2014)
- Add six new options: "dashed", "build", "built", "dragstart", "dragmove" and "dragend".
- Add three new events: "dragstart.cropper", "dragmove.cropper" and "dragend.cropper".
- Remove an old event: "render.cropper".
- Supports to toggle the dashed lines by "dashed" option (#68).
- Fix the issue of events (#71).
- Optimize the source code.
## 0.5.5 (Sep 8, 2014)
- Improve the render when the mouse out of the cropper container (#54).
## 0.5.4 (Aug 30, 2014)
- Fix typos: replace "resizeable" with "resizable" and "moveable" with "movable".
## 0.5.3 (Aug 23, 2014)
- Fix the issue (#64) that the crop box could not move after multiple touches.
## 0.5.2 (Aug 16, 2014)
- Fix a bug of type checking in the options.
- Compress the cropper template string.
## 0.5.1 (Aug 12, 2014)
- Supports canvas (#55).
## 0.5.0 (Aug 10, 2014)
- Add a new option: "multiple".
...
## 0.4.0 (Jul 26, 2014)
- Add eight new options: "autoCrop", "dragCrop", "moveable", "resizeable", "maxWidth", "maxHeight", "minWidth" and "minHeight".
- Add three new methods: "reset", "release" and "destroy".
- Add three new events: "build.cropper", "built.cropper" and "render.cropper".
- Remove two old methods: "enbale" and "disable".
- Remove three old events: "dragstart", "dragmove" and "dragend".
- Supports no conflict with the "$.fn.cropper.noConflict" method.
...
## 0.3.0 (May 18, 2014)
- Supports touch.
- Supports events.
- Add three events: "dargstart", "dargmove" and "dargend".
- Add a new method: "setImgSrc".
...
## 0.2.0 (Apr 23, 2014)
- Supports free ratio.
- Add a new option: "data".
- Add four new methods: "getData", "setData", "getImgInfo" and "setAspectRatio".
...
## 0.1.0 (Feb 19, 2014)
- Supports four options: "aspectRatio", "done", "modal" and "preview".
- Supports two methods: "enable" and "disable".

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-2015 Fengyuan Chen and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,941 @@
# [Cropper](https://github.com/fengyuanchen/cropper)
> A simple jQuery image cropping plugin.
- [Demo](http://fengyuanchen.github.io/cropper)
- [Cropper without jQuery](http://github.com/fengyuanchen/cropperjs)
## Features
- Supports [options](#options)
- Supports [methods](#methods)
- Supports [events](#events)
- Supports touch (mobile)
- Supports zoom
- Supports rotation
- Supports scale (flip)
- Supports canvas
- Supports multiple croppers
- Cross-browser support
## Main
```
dist/
├── cropper.css ( 5 KB)
├── cropper.min.css ( 4 KB)
├── cropper.js (67 KB)
└── cropper.min.js (24 KB)
```
## Getting started
### Quick start
Four quick start options are available:
- [Download the latest release](https://github.com/fengyuanchen/cropper/archive/master.zip).
- Clone the repository: `git clone https://github.com/fengyuanchen/cropper.git`.
- Install with [NPM](http://npmjs.org): `npm install cropper`.
- Install with [Bower](http://bower.io): `bower install cropper`.
### Installation
Include files:
```html
<script src="/path/to/jquery.js"></script><!-- jQuery is required -->
<link href="/path/to/cropper.css" rel="stylesheet">
<script src="/path/to/cropper.js"></script>
```
#### [CDNJS](https://cdnjs.com/)
The CDNJS provides CDN support for Cropper's CSS and JavaScript. You can find the links [here](https://cdnjs.com/libraries/cropper).
#### [RawGit](https://rawgit.com/)
```html
<link href="https://cdn.rawgit.com/fengyuanchen/cropper/v1.0.0/dist/cropper.min.css" rel="stylesheet">
<script src="https://cdn.rawgit.com/fengyuanchen/cropper/v1.0.0/dist/cropper.min.js"></script>
```
### Usage
Initialize with `$.fn.cropper` method.
```html
<!-- Wrap the image or canvas element with a block element -->
<div class="container">
<img src="picture.jpg">
</div>
```
```js
$('.container > img').cropper({
aspectRatio: 16 / 9,
crop: function(e) {
// Output the result data for cropping image.
console.log(e.x);
console.log(e.y);
console.log(e.width);
console.log(e.height);
console.log(e.rotate);
console.log(e.scaleX);
console.log(e.scaleY);
}
});
```
#### FAQ
See the [FAQ](FAQ.md) documentation.
#### Notes
- 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.
- The outputted cropped data bases on the original image size, so you can use them to crop the image directly.
- If you try to start cropper on a cross-origin image, please make sure that your browser supports HTML5 [CORS settings attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes), and your image server supports the `Access-Control-Allow-Origin` option.
#### Known issues
- About `getCroppedCanvas` method: The `canvas.drawImage` API in some Mac OS / iOS browsers will rotate an image with EXIF Orientation automatically, so the output cropped canvas may be incorrect. To fix this, you may upload the cropped data and crop the image in the server-side, see the example: [Crop Avatar](examples/crop-avatar). Or you may handle the EXIF Orientation in server first before to use cropper.
- [Known iOS resource limits](https://developer.apple.com/library/mac/documentation/AppleApplications/Reference/SafariWebContent/CreatingContentforSafarioniPhone/CreatingContentforSafarioniPhone.html): As iOS devices limit memory, the browser may crash when you are cropping a large image (iPhone camera resolution). To avoid this, you may resize the image first (below 1024px) before start a cropper.
## Options
You may set cropper options with `$().cropper(options)`.
If you want to change the global default options, You may use `$.fn.cropper.setDefaults(options)`.
### aspectRatio
- Type: `Number`
- Default: `NaN`
Set the aspect ratio of the crop box. By default, the crop box is free ratio.
### data
- Type: `Object`
- Default: `null`
The previous cropped data if you had stored, will be passed to `setData` method automatically.
### preview
- Type: `String` (**jQuery selector**)
- Default: `''`
Add extra elements (containers) for previewing.
**Notes:**
- The maximum width is the initial width of preview container.
- The maximum height is the initial height of preview container.
- If you set an `aspectRatio` option, be sure to set the preview container with the same aspect ratio.
- If preview is not getting properly displayed, set `overflow:hidden` to the preview container.
### strict
- Type: `Boolean`
- Default: `true`
In strict mode, the crop box cannot be outside of the canvas (image wrapper).
### responsive
- Type: `Boolean`
- Default: `true`
Rebuild the cropper when resize the window.
### checkImageOrigin
- Type: `Boolean`
- Default: `true`
By default, the plugin will check the image origin, and if it is a cross-origin image, a `crossOrigin` attribute will be added to the image element and a timestamp will be added to the image url to reload the image for "getCroppedCanvas".
By adding `crossOrigin` attribute to image will stop adding timestamp to image url, and stop reload of image.
### modal
- Type: `Boolean`
- Default: `true`
Show the black modal above the image and under the crop box.
### guides
- Type: `Boolean`
- Default: `true`
Show the dashed lines above the crop box.
### center
- Type: `Boolean`
- Default: `true`
Show the center indicator above the crop box.
### highlight
- Type: `Boolean`
- Default: `true`
Show the white modal above the crop box (highlight the crop box).
### background
- Type: `Boolean`
- Default: `true`
Show the grid background of the container.
### autoCrop
- Type: `Boolean`
- Default: `true`
Enable to crop the image automatically when initialize.
### autoCropArea
- Type: `Number`
- Default: `0.8` (80% of the image)
A number between 0 and 1. Define the automatic cropping area size (percentage).
### dragCrop
- Type: `Boolean`
- Default: `true`
Enable to remove the current crop box and create a new one by dragging over the image.
### movable
- Type: `Boolean`
- Default: `true`
Enable to move the image.
### rotatable
- Type: `Boolean`
- Default: `true`
Enable to rotate the image.
### scalable
- Type: `Boolean`
- Default: `true`
Enable to scale the image.
### zoomable
- Type: `Boolean`
- Default: `true`
Enable to zoom the image.
### mouseWheelZoom
- Type: `Boolean`
- Default: `true`
Enable to zoom the image by wheeling mouse.
### wheelZoomRatio
- Type: `Number`
- Default: `0.1`
Define zoom ratio when zoom the image by wheeling mouse.
### touchDragZoom
- Type: `Boolean`
- Default: `true`
Enable to zoom the image by dragging touch.
### cropBoxMovable
- Type: `Boolean`
- Default: `true`
Enable to move the crop box.
### cropBoxResizable
- Type: `Boolean`
- Default: `true`
Enable to resize the crop box.
### doubleClickToggle
- Type: `Boolean`
- Default: `true`
Enable to toggle drag mode between "crop" and "move" when double click on the cropper.
### minContainerWidth
- Type: `Number`
- Default: `200`
The minimum width of the container.
### minContainerHeight
- Type: `Number`
- Default: `100`
The minimum height of the container.
### minCanvasWidth
- Type: `Number`
- Default: `0`
The minimum width of the canvas (image wrapper).
### minCanvasHeight
- Type: `Number`
- Default: `0`
The minimum height of the canvas (image wrapper).
### minCropBoxWidth
- Type: `Number`
- Default: `0`
The minimum width of the crop box.
### minCropBoxHeight
- Type: `Number`
- Default: `0`
The minimum height of the crop box.
### build
- Type: `Function`
- Default: `null`
A shortcut of the "build.cropper" event.
### built
- Type: `Function`
- Default: `null`
A shortcut of the "built.cropper" event.
### cropstart
- Type: `Function`
- Default: `null`
A shortcut of the "cropstart.cropper" event.
### cropmove
- Type: `Function`
- Default: `null`
A shortcut of the "cropmove.cropper" event.
### cropend
- Type: `Function`
- Default: `null`
A shortcut of the "cropend.cropper" event.
### crop
- Type: `Function`
- Default: `null`
A shortcut of the "crop.cropper" event.
### zoom
- Type: `Function`
- Default: `null`
A shortcut of the "zoom.cropper" event.
## Methods
As there is an **asynchronous** process when load the image, you **should call most of the methods after built**, except "setAspectRatio", "replace" and "destroy".
```js
$().cropper({
built: function () {
$().cropper('method', argument1, , argument2, ..., argumentN);
}
});
```
### crop()
Show the crop box manually.
```js
$().cropper({
autoCrop: false,
built: function () {
// Do something here
// ...
// And then
$(this).cropper('crop');
}
});
```
### reset()
Reset the image and crop box to their initial states.
### clear()
Clear the crop box.
### replace(url)
- **url**:
- Type: `String`
- A new image url.
Replace the image's src and rebuild the cropper.
### enable()
Enable (unfreeze) the cropper.
### disable()
Disable (freeze) the cropper.
### destroy()
Destroy the cropper and remove the instance from the image.
### move(offsetX[, offsetY])
- **offsetX**:
- Type: `Number`
- Default: `0`
- Moving size (px) in the horizontal direction.
- **offsetY** (optional):
- Type: `Number`
- Moving size (px) in the vertical direction.
- If not present, its default value is `offsetX`.
Move the canvas (image wrapper).
```js
$().cropper('move', 1);
$().cropper('move', 1, 0);
$().cropper('move', 0, -1);
```
### zoom(ratio)
- **ratio**:
- Type: `Number`
- Zoom in: requires a positive number (ratio > 0)
- Zoom out: requires a negative number (ratio < 0)
Zoom the canvas (image wrapper).
```js
$().cropper('zoom', 0.1);
$().cropper('zoom', -0.1);
```
### rotate(degree)
- **degree**:
- Type: `Number`
- Rotate right: requires a positive number (degree > 0)
- Rotate left: requires a negative number (degree < 0)
Rotate the canvas (image wrapper).
> Requires [CSS3 2D Transforms](http://caniuse.com/transforms2d) support (IE 9+).
```js
$().cropper('rotate', 90);
$().cropper('rotate', -90);
```
### scale(scaleX[, scaleY])
- **scaleX**:
- Type: `Number`
- Default: `1`
- The scaling factor to apply on the abscissa of the image.
- When equal to `1` it does nothing.
- **scaleY** (optional):
- Type: `Number`
- The scaling factor to apply on the ordinate of the image.
- If not present, its default value is `scaleX`.
Scale the image.
> Requires [CSS3 2D Transforms](http://caniuse.com/transforms2d) support (IE 9+).
```js
$().cropper('scale', -1); // Flip both horizontal and vertical
$().cropper('scale', -1, 1); // Flip horizontal
$().cropper('scale', 1, -1); // Flip vertical
```
### getData([rounded])
- **rounded** (optional):
- Type: `Boolean`
- Default: `false`
- Set `true` to get rounded values.
- (return value):
- Type: `Object`
- Properties:
- `x`: the offset left of the cropped area
- `y`: the offset top of the cropped area
- `width`: the width of the cropped area
- `height`: the height of the cropped area
- `rotate`: the rotated degrees of the image
- `scaleX`: the scaling factor to apply on the abscissa of the image
- `scaleY`: the scaling factor to apply on the ordinate of the image
Output the cropped area position and size data (base on the original image).
![a schematic diagram of data's properties](assets/img/data.jpg)
### setData(data)
- **data**:
- Type: `Object`
- Properties: See the [`getData`](#getdatarounded) method.
Change the cropped area position and size with new data (base on the original image).
**Note:** Only available in strict mode.
### getContainerData()
- (return value):
- Type: `Object`
- Properties:
- `width`: the current width of the container
- `height`: the current height of the container
Output the container size data.
![a schematic diagram of cropper's layers](assets/img/layers.jpg)
### getImageData()
- (return value):
- Type: `Object`
- Properties:
- `left`: the offset left of the image
- `top`: the offset top of the image
- `width`: the width of the image
- `height`: the height of the image
- `naturalWidth`: the natural width of the image
- `naturalHeight`: the natural height of the image
- `aspectRatio`: the aspect ratio of the image
- `rotate`: the rotated degrees of the image if rotated
- `scaleX`: the scaling factor to apply on the abscissa of the image if scaled
- `scaleY`: the scaling factor to apply on the ordinate of the image if scaled
Output the image position, size and other related data.
### getCanvasData()
- (return value):
- Type: `Object`
- Properties:
- `left`: the offset left of the canvas
- `top`: the offset top of the canvas
- `width`: the width of the canvas
- `height`: the height of the canvas
Output the canvas (image wrapper) position and size data.
### setCanvasData(data)
- **data**:
- Type: `Object`
- Properties:
- `left`: the new offset left of the canvas
- `top`: the new offset top of the canvas
- `width`: the new width of the canvas
- `height`: the new height of the canvas
Change the canvas (image wrapper) position and size with new data.
### getCropBoxData()
- (return value):
- Type: `Object`
- Properties:
- `left`: the offset left of the crop box
- `top`: the offset top of the crop box
- `width`: the width of the crop box
- `height`: the height of the crop box
Output the crop box position and size data.
### setCropBoxData(data)
- **data**:
- Type: `Object`
- Properties:
- `left`: the new offset left of the crop box
- `top`: the new offset top of the crop box
- `width`: the new width of the crop box
- `height`: the new height of the crop box
Change the crop box position and size with new data.
### getCroppedCanvas([options])
- **options** (optional):
- Type: `Object`
- Properties:
- `width`: the destination width of the output canvas
- `height`: the destination height of the output canvas
- `fillColor`: a color to fill any alpha values in the output canvas
- (return value):
- Type: `HTMLCanvasElement`
- A canvas drawn the cropped image.
- Browser support:
- Basic image: requires [Canvas](http://caniuse.com/canvas) support (IE 9+).
- Rotated image: requires [CSS3 2D Transforms](http://caniuse.com/transforms2d) support (IE 9+).
- Cross-origin image: requires HTML5 [CORS settings attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) support (IE 11+).
Get a canvas drawn the cropped image.
> After then, you can display the canvas as an image directly, or use [canvas.toDataURL](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL) to get a Data URL, or use [canvas.toBlob](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob) to get a blob and upload it to server with [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) if the browser supports these APIs.
```js
$().cropper('getCroppedCanvas');
$().cropper('getCroppedCanvas', {
width: 160,
height: 90
});
// Upload cropped image to server
$().cropper('getCroppedCanvas').toBlob(function (blob) {
var formData = new FormData();
formData.append('croppedImage', blob);
$.ajax('/path/to/upload', {
method: "POST",
data: formData,
processData: false,
contentType: false,
success: function () {
console.log('Upload success');
},
error: function () {
console.log('Upload error');
}
});
});
```
### setAspectRatio(aspectRatio)
- **aspectRatio**:
- Type: `Number`
- Requires a positive number.
Change the aspect ratio of the crop box.
### setDragMode([mode])
- **mode** (optional):
- Type: `String`
- Default: `'none'`
- Options: `'none'`, `'crop'`, `'move'`
Change the drag mode.
**Tips:** You can toggle the "crop" and "move" mode by double click on the cropper.
## Events
### build.cropper
This event fires when a cropper instance starts to load an image.
### built.cropper
This event fires when a cropper instance has built completely.
### cropstart.cropper
- **event.originalEvent**:
- Type: `Event`
- Options: `mousedown`, `touchstart` and `pointerdown`
- **event.action**:
- Type: `String`
- Options:
- `'crop'`: create a new crop box
- `'move'`: move the canvas (image wrapper)
- `'zoom'`: zoom in / out the canvas (image wrapper) by touch.
- `'e'`: resize the east side of the crop box
- `'w'`: resize the west side of the crop box
- `'s'`: resize the south side of the crop box
- `'n'`: resize the north side of the crop box
- `'se'`: resize the southeast side of the crop box
- `'sw'`: resize the southwest side of the crop box
- `'ne'`: resize the northeast side of the crop box
- `'nw'`: resize the northwest side of the crop box
- `'all'`: move the crop box (all directions)
This event fires when the canvas (image wrapper) or the crop box starts to change.
```js
$().on('cropstart.cropper', function (e) {
console.log(e.type); // cropstart
console.log(e.namespace); // cropper
console.log(e.action); // ...
console.log(e.originalEvent.pageX);
// Prevent to start cropping, moving, etc if necessary
if (e.action === 'crop') {
e.preventDefault();
}
});
```
### cropmove.cropper
- **event.originalEvent**:
- Type: `Event`
- Options: `mousemove`, `touchmove` and `pointermove`.
- **event.action**: the same as "cropstart.cropper".
This event fires when the canvas (image wrapper) or the crop box is changing.
### cropend.cropper
- **event.originalEvent**:
- Type: `Event`
- Options: `mouseup`, `touchend`, `touchcancel`, `pointerup` and `pointercancel`.
- **event.action**: the same as "cropstart.cropper".
This event fires when the canvas (image wrapper) or the crop box stops to change.
### crop.cropper
- **event.x**
- **event.y**
- **event.width**
- **event.height**
- **event.rotate**
- **event.scaleX**
- **event.scaleY**
> About these properties, see the [`getData`](#getdatarounded) method.
This event fires when the canvas (image wrapper) or the crop box changed.
### zoom.cropper
- **event.originalEvent**:
- Type: `Event`
- Options: `wheel`, `touchmove`.
- **event.ratio**:
- Type: `Number`
- The current zoom ratio (`ratio > 0` means zoom in, `ratio < 0` means zoom out)
This event fires when a cropper instance starts to zoom in or zoom out its canvas (image wrapper).
```js
$().on('zoom.cropper', function (e) {
var maxRatio = 10;
var imageData = $(this).cropper('getImageData');
var currentRatio = imageData.width / imageData.naturalWidth;
// Zoom in
if (e.ratio > 0 && currentRatio > maxRatio) {
// Prevent zoom in
e.preventDefault();
// Fit the max zoom ratio
$(this).cropper('setCanvasData', {
width: imageData.naturalWidth * maxRatio
});
}
// Zoom out
// ...
});
```
## No conflict
If you have to use other plugin with the same namespace, just call the `$.fn.cropper.noConflict` method to revert to it.
```html
<script src="other-plugin.js"></script>
<script src="cropper.js"></script>
<script>
$.fn.cropper.noConflict();
// Code that uses other plugin's "$().cropper" can follow here.
</script>
```
## Browser support
- Chrome (latest 2)
- Firefox (latest 2)
- Internet Explorer 8+
- Opera (latest 2)
- Safari (latest 2)
As a jQuery plugin, you also need to see the [jQuery Browser Support](http://jquery.com/browser-support/).
## Contributing
Please read through our [contributing guidelines](CONTRIBUTING.md).
## Versioning
Maintained under the [Semantic Versioning guidelines](http://semver.org/).
## [License](LICENSE.md)
[MIT](http://opensource.org/licenses/MIT) © [Fengyuan Chen](http://github.com/fengyuanchen)
## Related projects
- [ngCropper](https://github.com/koorgoo/ngCropper) - AngularJS wrapper for Cropper.
- [react-cropper](https://github.com/roadmanfong/react-cropper) - Cropper as React components.
- [meteor-cropper](https://github.com/jonblum/meteor-cropper) - Cropper for Meteor.
- [ember-cli-cropper](https://github.com/anilmaurya/ember-cli-cropper) - Ember cli addon for Cropper.
- [ember-cli-image-cropper](https://github.com/mhretab/ember-cli-image-cropper) - Ember-cli addon for cropping/resizing images based on the jQuery Cropper plugin.

View File

@@ -0,0 +1,53 @@
{
"name": "cropper",
"description": "A simple jQuery image cropping plugin.",
"main": [
"dist/cropper.js",
"dist/cropper.css"
],
"keywords": [
"image",
"crop",
"cropping",
"move",
"zoom",
"rotate",
"scale",
"jquery",
"plugin",
"html",
"css",
"javascript",
"front-end",
"web",
"development"
],
"homepage": "https://github.com/fengyuanchen/cropper",
"authors": [
"Fengyuan Chen"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"tests",
"test",
"examples",
"assets",
"demo",
"docs",
"gulpfile.js",
"CONTRIBUTING.md",
"FAQ.md"
],
"dependencies": {
"jquery": ">= 1.9.1"
},
"devDependencies": {
"bootstrap": "~3.3.5",
"fontawesome": "~4.4.0",
"jquery": "~1.11.3",
"qunit": "~1.18.0"
}
}

View File

@@ -0,0 +1,290 @@
/*!
* Cropper v1.0.0
* https://github.com/fengyuanchen/cropper
*
* Copyright (c) 2014-2015 Fengyuan Chen and contributors
* Released under the MIT license
*
* Date: 2015-10-10T02:10:06.999Z
*/
.cropper-container {
position: relative;
overflow: hidden;
font-size: 0;
line-height: 0;
-ms-touch-action: none;
touch-action: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
direction: ltr !important;
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
}
.cropper-container img {
display: block;
width: 100%;
min-width: 0 !important;
max-width: none !important;
height: 100%;
min-height: 0 !important;
max-height: none !important;
image-orientation: 0deg !important;
}
.cropper-canvas,
.cropper-drag-box,
.cropper-crop-box,
.cropper-modal {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.cropper-drag-box {
background-color: #fff;
filter: alpha(opacity=0);
opacity: 0;
}
.cropper-modal {
background-color: #000;
filter: alpha(opacity=50);
opacity: .5;
}
.cropper-view-box {
display: block;
width: 100%;
height: 100%;
overflow: hidden;
outline: 1px solid #39f;
outline-color: rgba(51, 153, 255, .75);
}
.cropper-dashed {
position: absolute;
display: block;
filter: alpha(opacity=50);
border: 0 dashed #eee;
opacity: .5;
}
.cropper-dashed.dashed-h {
top: 33.33333%;
left: 0;
width: 100%;
height: 33.33333%;
border-top-width: 1px;
border-bottom-width: 1px;
}
.cropper-dashed.dashed-v {
top: 0;
left: 33.33333%;
width: 33.33333%;
height: 100%;
border-right-width: 1px;
border-left-width: 1px;
}
.cropper-center {
position: absolute;
top: 50%;
left: 50%;
display: block;
width: 0;
height: 0;
filter: alpha(opacity=75);
opacity: .75;
}
.cropper-center:before,
.cropper-center:after {
position: absolute;
display: block;
content: " ";
background-color: #eee;
}
.cropper-center:before {
top: 0;
left: -3px;
width: 7px;
height: 1px;
}
.cropper-center:after {
top: -3px;
left: 0;
width: 1px;
height: 7px;
}
.cropper-face,
.cropper-line,
.cropper-point {
position: absolute;
display: block;
width: 100%;
height: 100%;
filter: alpha(opacity=10);
opacity: .1;
}
.cropper-face {
top: 0;
left: 0;
background-color: #fff;
}
.cropper-line {
background-color: #39f;
}
.cropper-line.line-e {
top: 0;
right: -3px;
width: 5px;
cursor: e-resize;
}
.cropper-line.line-n {
top: -3px;
left: 0;
height: 5px;
cursor: n-resize;
}
.cropper-line.line-w {
top: 0;
left: -3px;
width: 5px;
cursor: w-resize;
}
.cropper-line.line-s {
bottom: -3px;
left: 0;
height: 5px;
cursor: s-resize;
}
.cropper-point {
width: 5px;
height: 5px;
background-color: #39f;
filter: alpha(opacity=75);
opacity: .75;
}
.cropper-point.point-e {
top: 50%;
right: -3px;
margin-top: -3px;
cursor: e-resize;
}
.cropper-point.point-n {
top: -3px;
left: 50%;
margin-left: -3px;
cursor: n-resize;
}
.cropper-point.point-w {
top: 50%;
left: -3px;
margin-top: -3px;
cursor: w-resize;
}
.cropper-point.point-s {
bottom: -3px;
left: 50%;
margin-left: -3px;
cursor: s-resize;
}
.cropper-point.point-ne {
top: -3px;
right: -3px;
cursor: ne-resize;
}
.cropper-point.point-nw {
top: -3px;
left: -3px;
cursor: nw-resize;
}
.cropper-point.point-sw {
bottom: -3px;
left: -3px;
cursor: sw-resize;
}
.cropper-point.point-se {
right: -3px;
bottom: -3px;
width: 20px;
height: 20px;
cursor: se-resize;
filter: alpha(opacity=100);
opacity: 1;
}
.cropper-point.point-se:before {
position: absolute;
right: -50%;
bottom: -50%;
display: block;
width: 200%;
height: 200%;
content: " ";
background-color: #39f;
filter: alpha(opacity=0);
opacity: 0;
}
@media (min-width: 768px) {
.cropper-point.point-se {
width: 15px;
height: 15px;
}
}
@media (min-width: 992px) {
.cropper-point.point-se {
width: 10px;
height: 10px;
}
}
@media (min-width: 1200px) {
.cropper-point.point-se {
width: 5px;
height: 5px;
filter: alpha(opacity=75);
opacity: .75;
}
}
.cropper-bg {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC");
}
.cropper-invisible {
filter: alpha(opacity=0);
opacity: 0;
}
.cropper-hide {
position: absolute;
display: block;
width: 0;
height: 0;
}
.cropper-hidden {
display: none !important;
}
.cropper-move {
cursor: move;
}
.cropper-crop {
cursor: crosshair;
}
.cropper-disabled .cropper-drag-box,
.cropper-disabled .cropper-face,
.cropper-disabled .cropper-line,
.cropper-disabled .cropper-point {
cursor: not-allowed;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
/*!
* Cropper v1.0.0
* https://github.com/fengyuanchen/cropper
*
* Copyright (c) 2014-2015 Fengyuan Chen and contributors
* Released under the MIT license
*
* Date: 2015-10-10T02:10:06.999Z
*/.cropper-container{position:relative;overflow:hidden;font-size:0;line-height:0;-ms-touch-action:none;touch-action:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;direction:ltr!important;-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none}.cropper-container img{display:block;width:100%;min-width:0!important;max-width:none!important;height:100%;min-height:0!important;max-height:none!important;image-orientation:0deg!important}.cropper-canvas,.cropper-crop-box,.cropper-drag-box,.cropper-modal{position:absolute;top:0;right:0;bottom:0;left:0}.cropper-drag-box{background-color:#fff;filter:alpha(opacity=0);opacity:0}.cropper-dashed,.cropper-modal{filter:alpha(opacity=50);opacity:.5}.cropper-modal{background-color:#000}.cropper-view-box{display:block;width:100%;height:100%;overflow:hidden;outline:#39f solid 1px;outline-color:rgba(51,153,255,.75)}.cropper-dashed{position:absolute;display:block;border:0 dashed #eee}.cropper-dashed.dashed-h{top:33.33333%;left:0;width:100%;height:33.33333%;border-top-width:1px;border-bottom-width:1px}.cropper-dashed.dashed-v{top:0;left:33.33333%;width:33.33333%;height:100%;border-right-width:1px;border-left-width:1px}.cropper-center{position:absolute;top:50%;left:50%;display:block;width:0;height:0;filter:alpha(opacity=75);opacity:.75}.cropper-center:after,.cropper-center:before{position:absolute;display:block;content:" ";background-color:#eee}.cropper-center:before{top:0;left:-3px;width:7px;height:1px}.cropper-center:after{top:-3px;left:0;width:1px;height:7px}.cropper-face,.cropper-line,.cropper-point{position:absolute;display:block;width:100%;height:100%;filter:alpha(opacity=10);opacity:.1}.cropper-face{top:0;left:0;background-color:#fff}.cropper-line,.cropper-point{background-color:#39f}.cropper-line.line-e{top:0;right:-3px;width:5px;cursor:e-resize}.cropper-line.line-n{top:-3px;left:0;height:5px;cursor:n-resize}.cropper-line.line-w{top:0;left:-3px;width:5px;cursor:w-resize}.cropper-line.line-s{bottom:-3px;left:0;height:5px;cursor:s-resize}.cropper-point{width:5px;height:5px;filter:alpha(opacity=75);opacity:.75}.cropper-point.point-e{top:50%;right:-3px;margin-top:-3px;cursor:e-resize}.cropper-point.point-n{top:-3px;left:50%;margin-left:-3px;cursor:n-resize}.cropper-point.point-w{top:50%;left:-3px;margin-top:-3px;cursor:w-resize}.cropper-point.point-s{bottom:-3px;left:50%;margin-left:-3px;cursor:s-resize}.cropper-point.point-ne{top:-3px;right:-3px;cursor:ne-resize}.cropper-point.point-nw{top:-3px;left:-3px;cursor:nw-resize}.cropper-point.point-sw{bottom:-3px;left:-3px;cursor:sw-resize}.cropper-point.point-se{right:-3px;bottom:-3px;width:20px;height:20px;cursor:se-resize;filter:alpha(opacity=100);opacity:1}.cropper-point.point-se:before{position:absolute;right:-50%;bottom:-50%;display:block;width:200%;height:200%;content:" ";background-color:#39f;filter:alpha(opacity=0);opacity:0}@media (min-width:768px){.cropper-point.point-se{width:15px;height:15px}}@media (min-width:992px){.cropper-point.point-se{width:10px;height:10px}}@media (min-width:1200px){.cropper-point.point-se{width:5px;height:5px;filter:alpha(opacity=75);opacity:.75}}.cropper-bg{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC)}.cropper-invisible{filter:alpha(opacity=0);opacity:0}.cropper-hide{position:absolute;display:block;width:0;height:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-drag-box,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,58 @@
{
"name": "cropper",
"description": "A simple jQuery image cropping plugin.",
"version": "1.0.0",
"main": "dist/cropper.js",
"keywords": [
"image",
"crop",
"cropping",
"move",
"zoom",
"rotate",
"scale",
"jquery",
"plugin",
"html",
"css",
"javascript",
"front-end",
"web",
"development"
],
"author": {
"name": "Fengyuan Chen",
"url": "https://github.com/fengyuanchen"
},
"homepage": "https://github.com/fengyuanchen/cropper",
"repository": {
"type": "git",
"url": "https://github.com/fengyuanchen/cropper.git"
},
"bugs": {
"url": "https://github.com/fengyuanchen/cropper/issues"
},
"license": "MIT",
"dependencies": {
"@fengyuanchen/tooltip": "0.0.2",
"jquery": ">= 1.9.1"
},
"devDependencies": {
"gulp": "^3.9.0",
"gulp-autoprefixer": "^3.0.2",
"gulp-concat": "~2.6.0",
"gulp-csscomb": "^3.0.6",
"gulp-csslint": "^0.2.0",
"gulp-htmlcomb": "0.1.0",
"gulp-jscs": "^3.0.1",
"gulp-jshint": "^1.11.0",
"gulp-load-plugins": "^1.0.0-rc.1",
"gulp-minify-css": "^1.2.1",
"gulp-qunit": "^1.2.1",
"gulp-rename": "^1.2.0",
"gulp-replace": "^0.5.3",
"gulp-sass": "^2.0.4",
"gulp-sourcemaps": "~1.6.0",
"gulp-uglify": "^1.4.1"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

View File

@@ -0,0 +1,89 @@
$.extend(prototype, {
bind: function () {
var options = this.options;
var $this = this.$element;
var $cropper = this.$cropper;
if ($.isFunction(options.cropstart)) {
$this.on(EVENT_CROP_START, options.cropstart);
}
if ($.isFunction(options.cropmove)) {
$this.on(EVENT_CROP_MOVE, options.cropmove);
}
if ($.isFunction(options.cropend)) {
$this.on(EVENT_CROP_END, options.cropend);
}
if ($.isFunction(options.crop)) {
$this.on(EVENT_CROP, options.crop);
}
if ($.isFunction(options.zoom)) {
$this.on(EVENT_ZOOM, options.zoom);
}
$cropper.on(EVENT_MOUSE_DOWN, $.proxy(this.cropStart, this));
if (options.zoomable && options.mouseWheelZoom) {
$cropper.on(EVENT_WHEEL, $.proxy(this.wheel, this));
}
if (options.doubleClickToggle) {
$cropper.on(EVENT_DBLCLICK, $.proxy(this.dblclick, this));
}
$document.
on(EVENT_MOUSE_MOVE, (this._cropMove = proxy(this.cropMove, this))).
on(EVENT_MOUSE_UP, (this._cropEnd = proxy(this.cropEnd, this)));
if (options.responsive) {
$window.on(EVENT_RESIZE, (this._resize = proxy(this.resize, this)));
}
},
unbind: function () {
var options = this.options;
var $this = this.$element;
var $cropper = this.$cropper;
if ($.isFunction(options.cropstart)) {
$this.off(EVENT_CROP_START, options.cropstart);
}
if ($.isFunction(options.cropmove)) {
$this.off(EVENT_CROP_MOVE, options.cropmove);
}
if ($.isFunction(options.cropend)) {
$this.off(EVENT_CROP_END, options.cropend);
}
if ($.isFunction(options.crop)) {
$this.off(EVENT_CROP, options.crop);
}
if ($.isFunction(options.zoom)) {
$this.off(EVENT_ZOOM, options.zoom);
}
$cropper.off(EVENT_MOUSE_DOWN, this.cropStart);
if (options.zoomable && options.mouseWheelZoom) {
$cropper.off(EVENT_WHEEL, this.wheel);
}
if (options.doubleClickToggle) {
$cropper.off(EVENT_DBLCLICK, this.dblclick);
}
$document.
off(EVENT_MOUSE_MOVE, this._cropMove).
off(EVENT_MOUSE_UP, this._cropEnd);
if (options.responsive) {
$window.off(EVENT_RESIZE, this._resize);
}
}
});

View File

@@ -0,0 +1,120 @@
$.extend(prototype, {
build: function () {
var options = this.options;
var $this = this.$element;
var $clone = this.$clone;
var $cropper;
var $cropBox;
var $face;
if (!this.ready) {
return;
}
// Unbuild first when replace
if (this.built) {
this.unbuild();
}
// Create cropper elements
this.$container = $this.parent();
this.$cropper = $cropper = $(Cropper.TEMPLATE);
this.$canvas = $cropper.find('.cropper-canvas').append($clone);
this.$dragBox = $cropper.find('.cropper-drag-box');
this.$cropBox = $cropBox = $cropper.find('.cropper-crop-box');
this.$viewBox = $cropper.find('.cropper-view-box');
this.$face = $face = $cropBox.find('.cropper-face');
// Hide the original image
$this.addClass(CLASS_HIDDEN).after($cropper);
// Show the clone image if is hidden
if (!this.isImg) {
$clone.removeClass(CLASS_HIDE);
}
this.initPreview();
this.bind();
// Format aspect ratio (0 -> NaN)
options.aspectRatio = num(options.aspectRatio) || NaN;
if (options.autoCrop) {
this.cropped = true;
if (options.modal) {
this.$dragBox.addClass(CLASS_MODAL);
}
} else {
$cropBox.addClass(CLASS_HIDDEN);
}
if (!options.guides) {
$cropBox.find('.cropper-dashed').addClass(CLASS_HIDDEN);
}
if (!options.center) {
$cropBox.find('.cropper-center').addClass(CLASS_HIDDEN);
}
if (options.cropBoxMovable) {
$face.addClass(CLASS_MOVE).data(DATA_ACTION, ACTION_ALL);
}
if (!options.highlight) {
$face.addClass(CLASS_INVISIBLE);
}
if (options.background) {
$cropper.addClass(CLASS_BG);
}
if (!options.cropBoxResizable) {
$cropBox.find('.cropper-line, .cropper-point').addClass(CLASS_HIDDEN);
}
this.setDragMode(options.dragCrop ? ACTION_CROP : (options.movable ? ACTION_MOVE : ACTION_NONE));
this.render();
this.built = true;
this.setData(options.data);
$this.one(EVENT_BUILT, options.built);
// Trigger the built event asynchronously to keep `data('cropper')` is defined
setTimeout($.proxy(function () {
this.trigger(EVENT_BUILT);
this.complete = true;
}, this), 0);
},
unbuild: function () {
if (!this.built) {
return;
}
this.built = false;
this.initialImage = null;
// Clear `initialCanvas` is necessary when replace
this.initialCanvas = null;
this.initialCropBox = null;
this.container = null;
this.canvas = null;
// Clear `cropBox` is necessary when replace
this.cropBox = null;
this.unbind();
this.resetPreview();
this.$preview = null;
this.$viewBox = null;
this.$cropBox = null;
this.$dragBox = null;
this.$canvas = null;
this.$container = null;
this.$cropper.remove();
this.$cropper = null;
}
});

View File

@@ -0,0 +1,404 @@
$.extend(prototype, {
change: function (shiftKey, originalEvent) {
var options = this.options;
var aspectRatio = options.aspectRatio;
var action = this.action;
var container = this.container;
var canvas = this.canvas;
var cropBox = this.cropBox;
var width = cropBox.width;
var height = cropBox.height;
var left = cropBox.left;
var top = cropBox.top;
var right = left + width;
var bottom = top + height;
var minLeft = 0;
var minTop = 0;
var maxWidth = container.width;
var maxHeight = container.height;
var renderable = true;
var offset;
var range;
// Locking aspect ratio in "free mode" by holding shift key (#259)
if (!aspectRatio && shiftKey) {
aspectRatio = width && height ? width / height : 1;
}
if (options.strict) {
minLeft = cropBox.minLeft;
minTop = cropBox.minTop;
maxWidth = minLeft + min(container.width, canvas.width);
maxHeight = minTop + min(container.height, canvas.height);
}
range = {
x: this.endX - this.startX,
y: this.endY - this.startY
};
if (aspectRatio) {
range.X = range.y * aspectRatio;
range.Y = range.x / aspectRatio;
}
switch (action) {
// Move crop box
case ACTION_ALL:
left += range.x;
top += range.y;
break;
// Resize crop box
case ACTION_EAST:
if (range.x >= 0 && (right >= maxWidth || aspectRatio &&
(top <= minTop || bottom >= maxHeight))) {
renderable = false;
break;
}
width += range.x;
if (aspectRatio) {
height = width / aspectRatio;
top -= range.Y / 2;
}
if (width < 0) {
action = ACTION_WEST;
width = 0;
}
break;
case ACTION_NORTH:
if (range.y <= 0 && (top <= minTop || aspectRatio &&
(left <= minLeft || right >= maxWidth))) {
renderable = false;
break;
}
height -= range.y;
top += range.y;
if (aspectRatio) {
width = height * aspectRatio;
left += range.X / 2;
}
if (height < 0) {
action = ACTION_SOUTH;
height = 0;
}
break;
case ACTION_WEST:
if (range.x <= 0 && (left <= minLeft || aspectRatio &&
(top <= minTop || bottom >= maxHeight))) {
renderable = false;
break;
}
width -= range.x;
left += range.x;
if (aspectRatio) {
height = width / aspectRatio;
top += range.Y / 2;
}
if (width < 0) {
action = ACTION_EAST;
width = 0;
}
break;
case ACTION_SOUTH:
if (range.y >= 0 && (bottom >= maxHeight || aspectRatio &&
(left <= minLeft || right >= maxWidth))) {
renderable = false;
break;
}
height += range.y;
if (aspectRatio) {
width = height * aspectRatio;
left -= range.X / 2;
}
if (height < 0) {
action = ACTION_NORTH;
height = 0;
}
break;
case ACTION_NORTH_EAST:
if (aspectRatio) {
if (range.y <= 0 && (top <= minTop || right >= maxWidth)) {
renderable = false;
break;
}
height -= range.y;
top += range.y;
width = height * aspectRatio;
} else {
if (range.x >= 0) {
if (right < maxWidth) {
width += range.x;
} else if (range.y <= 0 && top <= minTop) {
renderable = false;
}
} else {
width += range.x;
}
if (range.y <= 0) {
if (top > minTop) {
height -= range.y;
top += range.y;
}
} else {
height -= range.y;
top += range.y;
}
}
if (width < 0 && height < 0) {
action = ACTION_SOUTH_WEST;
height = 0;
width = 0;
} else if (width < 0) {
action = ACTION_NORTH_WEST;
width = 0;
} else if (height < 0) {
action = ACTION_SOUTH_EAST;
height = 0;
}
break;
case ACTION_NORTH_WEST:
if (aspectRatio) {
if (range.y <= 0 && (top <= minTop || left <= minLeft)) {
renderable = false;
break;
}
height -= range.y;
top += range.y;
width = height * aspectRatio;
left += range.X;
} else {
if (range.x <= 0) {
if (left > minLeft) {
width -= range.x;
left += range.x;
} else if (range.y <= 0 && top <= minTop) {
renderable = false;
}
} else {
width -= range.x;
left += range.x;
}
if (range.y <= 0) {
if (top > minTop) {
height -= range.y;
top += range.y;
}
} else {
height -= range.y;
top += range.y;
}
}
if (width < 0 && height < 0) {
action = ACTION_SOUTH_EAST;
height = 0;
width = 0;
} else if (width < 0) {
action = ACTION_NORTH_EAST;
width = 0;
} else if (height < 0) {
action = ACTION_SOUTH_WEST;
height = 0;
}
break;
case ACTION_SOUTH_WEST:
if (aspectRatio) {
if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) {
renderable = false;
break;
}
width -= range.x;
left += range.x;
height = width / aspectRatio;
} else {
if (range.x <= 0) {
if (left > minLeft) {
width -= range.x;
left += range.x;
} else if (range.y >= 0 && bottom >= maxHeight) {
renderable = false;
}
} else {
width -= range.x;
left += range.x;
}
if (range.y >= 0) {
if (bottom < maxHeight) {
height += range.y;
}
} else {
height += range.y;
}
}
if (width < 0 && height < 0) {
action = ACTION_NORTH_EAST;
height = 0;
width = 0;
} else if (width < 0) {
action = ACTION_SOUTH_EAST;
width = 0;
} else if (height < 0) {
action = ACTION_NORTH_WEST;
height = 0;
}
break;
case ACTION_SOUTH_EAST:
if (aspectRatio) {
if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {
renderable = false;
break;
}
width += range.x;
height = width / aspectRatio;
} else {
if (range.x >= 0) {
if (right < maxWidth) {
width += range.x;
} else if (range.y >= 0 && bottom >= maxHeight) {
renderable = false;
}
} else {
width += range.x;
}
if (range.y >= 0) {
if (bottom < maxHeight) {
height += range.y;
}
} else {
height += range.y;
}
}
if (width < 0 && height < 0) {
action = ACTION_NORTH_WEST;
height = 0;
width = 0;
} else if (width < 0) {
action = ACTION_SOUTH_WEST;
width = 0;
} else if (height < 0) {
action = ACTION_NORTH_EAST;
height = 0;
}
break;
// Move canvas
case ACTION_MOVE:
this.move(range.x, range.y);
renderable = false;
break;
// Zoom canvas
case ACTION_ZOOM:
this.zoom((function (x1, y1, x2, y2) {
var z1 = sqrt(x1 * x1 + y1 * y1);
var z2 = sqrt(x2 * x2 + y2 * y2);
return (z2 - z1) / z1;
})(
abs(this.startX - this.startX2),
abs(this.startY - this.startY2),
abs(this.endX - this.endX2),
abs(this.endY - this.endY2)
), originalEvent);
this.startX2 = this.endX2;
this.startY2 = this.endY2;
renderable = false;
break;
// Create crop box
case ACTION_CROP:
if (range.x && range.y) {
offset = this.$cropper.offset();
left = this.startX - offset.left;
top = this.startY - offset.top;
width = cropBox.minWidth;
height = cropBox.minHeight;
if (range.x > 0) {
if (range.y > 0) {
action = ACTION_SOUTH_EAST;
} else {
action = ACTION_NORTH_EAST;
top -= height;
}
} else {
if (range.y > 0) {
action = ACTION_SOUTH_WEST;
left -= width;
} else {
action = ACTION_NORTH_WEST;
left -= width;
top -= height;
}
}
// Show the crop box if is hidden
if (!this.cropped) {
this.cropped = true;
this.$cropBox.removeClass(CLASS_HIDDEN);
}
}
break;
// No default
}
if (renderable) {
cropBox.width = width;
cropBox.height = height;
cropBox.left = left;
cropBox.top = top;
this.action = action;
this.renderCropBox();
}
// Override
this.startX = this.endX;
this.startY = this.endY;
}
});

View File

@@ -0,0 +1,17 @@
function Cropper(element, options) {
this.$element = $(element);
this.options = $.extend({}, Cropper.DEFAULTS, $.isPlainObject(options) && options);
this.ready = false;
this.built = false;
this.complete = false;
this.rotated = false;
this.cropped = false;
this.disabled = false;
this.replaced = false;
this.isImg = false;
this.originalUrl = '';
this.crossOrigin = '';
this.canvas = null;
this.cropBox = null;
this.init();
}

View File

@@ -0,0 +1,95 @@
Cropper.DEFAULTS = {
// Define the aspect ratio of the crop box
aspectRatio: NaN,
// An object with the previous cropping result data
data: null,
// A jQuery selector for adding extra containers to preview
preview: '',
// Strict mode, the image cannot zoom out less than the container
strict: true,
// Rebuild when resize the window
responsive: true,
// Check if the target image is cross origin
checkImageOrigin: true,
// Show the black modal
modal: true,
// Show the dashed lines for guiding
guides: true,
// Show the center indicator for guiding
center: true,
// Show the white modal to highlight the crop box
highlight: true,
// Show the grid background
background: true,
// Enable to crop the image automatically when initialize
autoCrop: true,
// Define the percentage of automatic cropping area when initializes
autoCropArea: 0.8,
// Enable to create new crop box by dragging over the image
dragCrop: true,
// Enable to move the image
movable: true,
// Enable to rotate the image
rotatable: true,
// Enable to scale the image
scalable: true,
// Enable to zoom the image
zoomable: true,
// Enable to zoom the image by wheeling mouse
mouseWheelZoom: true,
// Define zoom ratio when zoom the image by wheeling mouse
wheelZoomRatio: 0.1,
// Enable to zoom the image by dragging touch
touchDragZoom: true,
// Enable to move the crop box
cropBoxMovable: true,
// Enable to resize the crop box
cropBoxResizable: true,
// Toggle drag mode between "crop" and "move" when double click on the cropper
doubleClickToggle: true,
// Size limitation
minCanvasWidth: 0,
minCanvasHeight: 0,
minCropBoxWidth: 0,
minCropBoxHeight: 0,
minContainerWidth: 200,
minContainerHeight: 100,
// Shortcuts of events
build: null,
built: null,
cropstart: null,
cropmove: null,
cropend: null,
crop: null,
zoom: null
};
Cropper.setDefaults = function (options) {
$.extend(Cropper.DEFAULTS, options);
};

View File

@@ -0,0 +1,191 @@
$.extend(prototype, {
resize: function () {
var $container = this.$container;
var container = this.container;
var canvasData;
var cropBoxData;
var ratio;
// Check `container` is necessary for IE8
if (this.disabled || !container) {
return;
}
ratio = $container.width() / container.width;
// Resize when width changed or height changed
if (ratio !== 1 || $container.height() !== container.height) {
canvasData = this.getCanvasData();
cropBoxData = this.getCropBoxData();
this.render();
this.setCanvasData($.each(canvasData, function (i, n) {
canvasData[i] = n * ratio;
}));
this.setCropBoxData($.each(cropBoxData, function (i, n) {
cropBoxData[i] = n * ratio;
}));
}
},
dblclick: function () {
if (this.disabled) {
return;
}
if (this.$dragBox.hasClass(CLASS_CROP)) {
this.setDragMode(ACTION_MOVE);
} else {
this.setDragMode(ACTION_CROP);
}
},
wheel: function (event) {
var originalEvent = event.originalEvent;
var e = originalEvent;
var ratio = num(this.options.wheelZoomRatio) || 0.1;
var delta = 1;
if (this.disabled) {
return;
}
event.preventDefault();
if (e.deltaY) {
delta = e.deltaY > 0 ? 1 : -1;
} else if (e.wheelDelta) {
delta = -e.wheelDelta / 120;
} else if (e.detail) {
delta = e.detail > 0 ? 1 : -1;
}
this.zoom(-delta * ratio, originalEvent);
},
cropStart: function (event) {
var options = this.options;
var originalEvent = event.originalEvent;
var touches = originalEvent && originalEvent.touches;
var e = event;
var touchesLength;
var action;
if (this.disabled) {
return;
}
if (touches) {
touchesLength = touches.length;
if (touchesLength > 1) {
if (options.zoomable && options.touchDragZoom && touchesLength === 2) {
e = touches[1];
this.startX2 = e.pageX;
this.startY2 = e.pageY;
action = ACTION_ZOOM;
} else {
return;
}
}
e = touches[0];
}
action = action || $(e.target).data(DATA_ACTION);
if (REGEXP_ACTIONS.test(action)) {
if (this.trigger(EVENT_CROP_START, {
originalEvent: originalEvent,
action: action
}).isDefaultPrevented()) {
return;
}
event.preventDefault();
this.action = action;
this.cropping = false;
// IE8 has `event.pageX/Y`, but not `event.originalEvent.pageX/Y`
// IE10 has `event.originalEvent.pageX/Y`, but not `event.pageX/Y`
this.startX = e.pageX || originalEvent && originalEvent.pageX;
this.startY = e.pageY || originalEvent && originalEvent.pageY;
if (action === ACTION_CROP) {
this.cropping = true;
this.$dragBox.addClass(CLASS_MODAL);
}
}
},
cropMove: function (event) {
var options = this.options;
var originalEvent = event.originalEvent;
var touches = originalEvent && originalEvent.touches;
var e = event;
var action = this.action;
var touchesLength;
if (this.disabled) {
return;
}
if (touches) {
touchesLength = touches.length;
if (touchesLength > 1) {
if (options.zoomable && options.touchDragZoom && touchesLength === 2) {
e = touches[1];
this.endX2 = e.pageX;
this.endY2 = e.pageY;
} else {
return;
}
}
e = touches[0];
}
if (action) {
if (this.trigger(EVENT_CROP_MOVE, {
originalEvent: originalEvent,
action: action
}).isDefaultPrevented()) {
return;
}
event.preventDefault();
this.endX = e.pageX || originalEvent && originalEvent.pageX;
this.endY = e.pageY || originalEvent && originalEvent.pageY;
this.change(e.shiftKey, action === ACTION_ZOOM ? originalEvent : null);
}
},
cropEnd: function (event) {
var originalEvent = event.originalEvent;
var action = this.action;
if (this.disabled) {
return;
}
if (action) {
event.preventDefault();
if (this.cropping) {
this.cropping = false;
this.$dragBox.toggleClass(CLASS_MODAL, this.cropped && this.options.modal);
}
this.action = '';
this.trigger(EVENT_CROP_END, {
originalEvent: originalEvent,
action: action
});
}
}
});

View File

@@ -0,0 +1,24 @@
/*!
* Cropper v@VERSION
* https://github.com/fengyuanchen/cropper
*
* Copyright (c) 2014-@YEAR Fengyuan Chen and contributors
* Released under the MIT license
*
* Date: @DATE
*/
(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';

View File

@@ -0,0 +1,109 @@
$.extend(prototype, {
init: function () {
var $this = this.$element;
var url;
if ($this.is('img')) {
this.isImg = true;
// Should use `$.fn.attr` here. e.g.: "img/picture.jpg"
this.originalUrl = url = $this.attr('src');
// Stop when it's a blank image
if (!url) {
return;
}
// Should use `$.fn.prop` here. e.g.: "http://example.com/img/picture.jpg"
url = $this.prop('src');
} else if ($this.is('canvas') && SUPPORT_CANVAS) {
url = $this[0].toDataURL();
}
this.load(url);
},
// A shortcut for triggering custom events
trigger: function (type, data) {
var e = $.Event(type, data);
this.$element.trigger(e);
return e;
},
load: function (url) {
var options = this.options;
var $this = this.$element;
var crossOrigin = '';
var bustCacheUrl;
var $clone;
if (!url) {
return;
}
this.url = url;
// Trigger build event first
$this.one(EVENT_BUILD, options.build);
if (this.trigger(EVENT_BUILD).isDefaultPrevented()) {
return;
}
if (options.checkImageOrigin && isCrossOriginURL(url)) {
crossOrigin = $this.prop('crossOrigin');
// Bust cache (#148), only when there was not a "crossOrigin" property
if (!crossOrigin) {
crossOrigin = 'anonymous';
bustCacheUrl = addTimestamp(url);
}
this.crossOrigin = crossOrigin;
}
this.$clone = $clone = $('<img' + getCrossOrigin(crossOrigin) + ' src="' + (bustCacheUrl || url) + '">');
if (this.isImg) {
if ($this[0].complete) {
this.start();
} else {
$this.one(EVENT_LOAD, $.proxy(this.start, this));
}
} else {
$clone.
one(EVENT_LOAD, $.proxy(this.start, this)).
one(EVENT_ERROR, $.proxy(this.stop, this)).
addClass(CLASS_HIDE).
insertAfter($this);
}
},
start: function () {
var $image = this.$element;
var $clone = this.$clone;
if (!this.isImg) {
$clone.off(EVENT_ERROR, this.stop);
$image = $clone;
}
getImageSize($image[0], $.proxy(function (naturalWidth, naturalHeight) {
this.image = {
naturalWidth: naturalWidth,
naturalHeight: naturalHeight,
aspectRatio: naturalWidth / naturalHeight
};
this.ready = true;
this.build();
}, this));
},
stop: function () {
this.$clone.remove();
this.$clone = null;
}
});

View File

@@ -0,0 +1,659 @@
$.extend(prototype, {
// Show the crop box manually
crop: function () {
if (!this.built || this.disabled) {
return;
}
if (!this.cropped) {
this.cropped = true;
this.limitCropBox(true, true);
if (this.options.modal) {
this.$dragBox.addClass(CLASS_MODAL);
}
this.$cropBox.removeClass(CLASS_HIDDEN);
}
this.setCropBoxData(this.initialCropBox);
},
// Reset the image and crop box to their initial states
reset: function () {
if (!this.built || this.disabled) {
return;
}
this.image = $.extend({}, this.initialImage);
this.canvas = $.extend({}, this.initialCanvas);
// Required for strict mode
this.cropBox = $.extend({}, this.initialCropBox);
this.renderCanvas();
if (this.cropped) {
this.renderCropBox();
}
},
// Clear the crop box
clear: function () {
if (!this.cropped || this.disabled) {
return;
}
$.extend(this.cropBox, {
left: 0,
top: 0,
width: 0,
height: 0
});
this.cropped = false;
this.renderCropBox();
this.limitCanvas(true, true);
// Render canvas after crop box rendered
this.renderCanvas();
this.$dragBox.removeClass(CLASS_MODAL);
this.$cropBox.addClass(CLASS_HIDDEN);
},
/**
* Replace the image's src and rebuild the cropper
*
* @param {String} url
*/
replace: function (url) {
if (!this.disabled && url) {
if (this.isImg) {
this.replaced = true;
this.$element.attr('src', url);
}
// Clear previous data
this.options.data = null;
this.load(url);
}
},
// Enable (unfreeze) the cropper
enable: function () {
if (this.built) {
this.disabled = false;
this.$cropper.removeClass(CLASS_DISABLED);
}
},
// Disable (freeze) the cropper
disable: function () {
if (this.built) {
this.disabled = true;
this.$cropper.addClass(CLASS_DISABLED);
}
},
// Destroy the cropper and remove the instance from the image
destroy: function () {
var $this = this.$element;
if (this.ready) {
if (this.isImg && this.replaced) {
$this.attr('src', this.originalUrl);
}
this.unbuild();
$this.removeClass(CLASS_HIDDEN);
} else {
if (this.isImg) {
$this.off(EVENT_LOAD, this.start);
} else if (this.$clone) {
this.$clone.remove();
}
}
$this.removeData(NAMESPACE);
},
/**
* Move the canvas
*
* @param {Number} offsetX
* @param {Number} offsetY (optional)
*/
move: function (offsetX, offsetY) {
var canvas = this.canvas;
// If "offsetY" is not present, its default value is "offsetX"
if (isUndefined(offsetY)) {
offsetY = offsetX;
}
offsetX = num(offsetX);
offsetY = num(offsetY);
if (this.built && !this.disabled && this.options.movable) {
canvas.left += isNumber(offsetX) ? offsetX : 0;
canvas.top += isNumber(offsetY) ? offsetY : 0;
this.renderCanvas(true);
}
},
/**
* Zoom the canvas
*
* @param {Number} ratio
* @param {Event} _originalEvent (private)
*/
zoom: function (ratio, _originalEvent) {
var canvas = this.canvas;
var width;
var height;
ratio = num(ratio);
if (ratio && this.built && !this.disabled && this.options.zoomable) {
if (this.trigger(EVENT_ZOOM, {
originalEvent: _originalEvent,
ratio: ratio
}).isDefaultPrevented()) {
return;
}
if (ratio < 0) {
ratio = 1 / (1 - ratio);
} else {
ratio = 1 + ratio;
}
width = canvas.width * ratio;
height = canvas.height * ratio;
canvas.left -= (width - canvas.width) / 2;
canvas.top -= (height - canvas.height) / 2;
canvas.width = width;
canvas.height = height;
this.renderCanvas(true);
this.setDragMode(ACTION_MOVE);
}
},
/**
* Rotate the canvas
* https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#rotate()
*
* @param {Number} degree
*/
rotate: function (degree) {
var image = this.image;
var rotate = image.rotate || 0;
degree = num(degree) || 0;
if (this.built && !this.disabled && this.options.rotatable) {
image.rotate = (rotate + degree) % 360;
this.rotated = true;
this.renderCanvas(true);
}
},
/**
* Scale the image
* https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#scale()
*
* @param {Number} scaleX
* @param {Number} scaleY (optional)
*/
scale: function (scaleX, scaleY) {
var image = this.image;
// If "scaleY" is not present, its default value is "scaleX"
if (isUndefined(scaleY)) {
scaleY = scaleX;
}
scaleX = num(scaleX);
scaleY = num(scaleY);
if (this.built && !this.disabled && this.options.scalable) {
image.scaleX = isNumber(scaleX) ? scaleX : 1;
image.scaleY = isNumber(scaleY) ? scaleY : 1;
this.renderImage(true);
}
},
/**
* Get the cropped area position and size data (base on the original image)
*
* @param {Boolean} rounded (optional)
* @return {Object} data
*/
getData: function (rounded) {
var options = this.options;
var image = this.image;
var canvas = this.canvas;
var cropBox = this.cropBox;
var ratio;
var data;
if (this.built && this.cropped) {
data = {
x: cropBox.left - canvas.left,
y: cropBox.top - canvas.top,
width: cropBox.width,
height: cropBox.height
};
ratio = image.width / image.naturalWidth;
$.each(data, function (i, n) {
n = n / ratio;
data[i] = rounded ? Math.round(n) : n;
});
} else {
data = {
x: 0,
y: 0,
width: 0,
height: 0
};
}
if (options.rotatable) {
data.rotate = image.rotate || 0;
}
if (options.scalable) {
data.scaleX = image.scaleX || 1;
data.scaleY = image.scaleY || 1;
}
return data;
},
/**
* Set the cropped area position and size with new data
*
* @param {Object} data
*/
setData: function (data) {
var options = this.options;
var image = this.image;
var canvas = this.canvas;
var cropBoxData = {};
var rotated;
var scaled;
var ratio;
if ($.isFunction(data)) {
data = data.call(this.element);
}
if (this.built && !this.disabled && $.isPlainObject(data)) {
if (options.rotatable) {
if (isNumber(data.rotate) && data.rotate !== image.rotate) {
image.rotate = data.rotate;
this.rotated = rotated = true;
}
}
if (options.scalable) {
if (isNumber(data.scaleX) && data.scaleX !== image.scaleX) {
image.scaleX = data.scaleX;
scaled = true;
}
if (isNumber(data.scaleY) && data.scaleY !== image.scaleY) {
image.scaleY = data.scaleY;
scaled = true;
}
}
if (rotated) {
this.renderCanvas();
} else if (scaled) {
this.renderImage();
}
ratio = image.width / image.naturalWidth;
if (isNumber(data.x)) {
cropBoxData.left = data.x * ratio + canvas.left;
}
if (isNumber(data.y)) {
cropBoxData.top = data.y * ratio + canvas.top;
}
if (isNumber(data.width)) {
cropBoxData.width = data.width * ratio;
}
if (isNumber(data.height)) {
cropBoxData.height = data.height * ratio;
}
this.setCropBoxData(cropBoxData);
}
},
/**
* Get the container size data
*
* @return {Object} data
*/
getContainerData: function () {
return this.built ? this.container : {};
},
/**
* Get the image position and size data
*
* @return {Object} data
*/
getImageData: function () {
return this.ready ? this.image : {};
},
/**
* Get the canvas position and size data
*
* @return {Object} data
*/
getCanvasData: function () {
var canvas = this.canvas;
var data;
if (this.built) {
data = {
left: canvas.left,
top: canvas.top,
width: canvas.width,
height: canvas.height
};
}
return data || {};
},
/**
* Set the canvas position and size with new data
*
* @param {Object} data
*/
setCanvasData: function (data) {
var canvas = this.canvas;
var aspectRatio = canvas.aspectRatio;
if ($.isFunction(data)) {
data = data.call(this.$element);
}
if (this.built && !this.disabled && $.isPlainObject(data)) {
if (isNumber(data.left)) {
canvas.left = data.left;
}
if (isNumber(data.top)) {
canvas.top = data.top;
}
if (isNumber(data.width)) {
canvas.width = data.width;
canvas.height = data.width / aspectRatio;
} else if (isNumber(data.height)) {
canvas.height = data.height;
canvas.width = data.height * aspectRatio;
}
this.renderCanvas(true);
}
},
/**
* Get the crop box position and size data
*
* @return {Object} data
*/
getCropBoxData: function () {
var cropBox = this.cropBox;
var data;
if (this.built && this.cropped) {
data = {
left: cropBox.left,
top: cropBox.top,
width: cropBox.width,
height: cropBox.height
};
}
return data || {};
},
/**
* Set the crop box position and size with new data
*
* @param {Object} data
*/
setCropBoxData: function (data) {
var cropBox = this.cropBox;
var aspectRatio = this.options.aspectRatio;
var widthChanged;
var heightChanged;
if ($.isFunction(data)) {
data = data.call(this.$element);
}
if (this.built && this.cropped && !this.disabled && $.isPlainObject(data)) {
if (isNumber(data.left)) {
cropBox.left = data.left;
}
if (isNumber(data.top)) {
cropBox.top = data.top;
}
if (isNumber(data.width) && data.width !== cropBox.width) {
widthChanged = true;
cropBox.width = data.width;
}
if (isNumber(data.height) && data.height !== cropBox.height) {
heightChanged = true;
cropBox.height = data.height;
}
if (aspectRatio) {
if (widthChanged) {
cropBox.height = cropBox.width / aspectRatio;
} else if (heightChanged) {
cropBox.width = cropBox.height * aspectRatio;
}
}
this.renderCropBox();
}
},
/**
* Get a canvas drawn the cropped image
*
* @param {Object} options (optional)
* @return {HTMLCanvasElement} canvas
*/
getCroppedCanvas: function (options) {
var originalWidth;
var originalHeight;
var canvasWidth;
var canvasHeight;
var scaledWidth;
var scaledHeight;
var scaledRatio;
var aspectRatio;
var canvas;
var context;
var data;
if (!this.built || !this.cropped || !SUPPORT_CANVAS) {
return;
}
if (!$.isPlainObject(options)) {
options = {};
}
data = this.getData();
originalWidth = data.width;
originalHeight = data.height;
aspectRatio = originalWidth / originalHeight;
if ($.isPlainObject(options)) {
scaledWidth = options.width;
scaledHeight = options.height;
if (scaledWidth) {
scaledHeight = scaledWidth / aspectRatio;
scaledRatio = scaledWidth / originalWidth;
} else if (scaledHeight) {
scaledWidth = scaledHeight * aspectRatio;
scaledRatio = scaledHeight / originalHeight;
}
}
canvasWidth = scaledWidth || originalWidth;
canvasHeight = scaledHeight || originalHeight;
canvas = $('<canvas>')[0];
canvas.width = canvasWidth;
canvas.height = canvasHeight;
context = canvas.getContext('2d');
if (options.fillColor) {
context.fillStyle = options.fillColor;
context.fillRect(0, 0, canvasWidth, canvasHeight);
}
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage
context.drawImage.apply(context, (function () {
var source = getSourceCanvas(this.$clone[0], this.image);
var sourceWidth = source.width;
var sourceHeight = source.height;
var args = [source];
// Source canvas
var srcX = data.x;
var srcY = data.y;
var srcWidth;
var srcHeight;
// Destination canvas
var dstX;
var dstY;
var dstWidth;
var dstHeight;
if (srcX <= -originalWidth || srcX > sourceWidth) {
srcX = srcWidth = dstX = dstWidth = 0;
} else if (srcX <= 0) {
dstX = -srcX;
srcX = 0;
srcWidth = dstWidth = min(sourceWidth, originalWidth + srcX);
} else if (srcX <= sourceWidth) {
dstX = 0;
srcWidth = dstWidth = min(originalWidth, sourceWidth - srcX);
}
if (srcWidth <= 0 || srcY <= -originalHeight || srcY > sourceHeight) {
srcY = srcHeight = dstY = dstHeight = 0;
} else if (srcY <= 0) {
dstY = -srcY;
srcY = 0;
srcHeight = dstHeight = min(sourceHeight, originalHeight + srcY);
} else if (srcY <= sourceHeight) {
dstY = 0;
srcHeight = dstHeight = min(originalHeight, sourceHeight - srcY);
}
args.push(srcX, srcY, srcWidth, srcHeight);
// Scale destination sizes
if (scaledRatio) {
dstX *= scaledRatio;
dstY *= scaledRatio;
dstWidth *= scaledRatio;
dstHeight *= scaledRatio;
}
// Avoid "IndexSizeError" in IE and Firefox
if (dstWidth > 0 && dstHeight > 0) {
args.push(dstX, dstY, dstWidth, dstHeight);
}
return args;
}).call(this));
return canvas;
},
/**
* Change the aspect ratio of the crop box
*
* @param {Number} aspectRatio
*/
setAspectRatio: function (aspectRatio) {
var options = this.options;
if (!this.disabled && !isUndefined(aspectRatio)) {
// 0 -> NaN
options.aspectRatio = num(aspectRatio) || NaN;
if (this.built) {
this.initCropBox();
if (this.cropped) {
this.renderCropBox();
}
}
}
},
/**
* Change the drag mode
*
* @param {String} mode (optional)
*/
setDragMode: function (mode) {
var options = this.options;
var croppable;
var movable;
if (this.ready && !this.disabled) {
croppable = options.dragCrop && mode === ACTION_CROP;
movable = options.movable && mode === ACTION_MOVE;
mode = (croppable || movable) ? mode : ACTION_NONE;
this.$dragBox.
data(DATA_ACTION, mode).
toggleClass(CLASS_CROP, croppable).
toggleClass(CLASS_MOVE, movable);
if (!options.cropBoxMovable) {
// Sync drag mode to crop box when it is not movable(#300)
this.$face.
data(DATA_ACTION, mode).
toggleClass(CLASS_CROP, croppable).
toggleClass(CLASS_MOVE, movable);
}
}
}
});

View File

@@ -0,0 +1 @@
});

View File

@@ -0,0 +1,37 @@
// Save the other cropper
Cropper.other = $.fn.cropper;
// Register as jQuery plugin
$.fn.cropper = function (options) {
var args = toArray(arguments, 1);
var result;
this.each(function () {
var $this = $(this);
var data = $this.data(NAMESPACE);
var fn;
if (!data) {
if (/destroy/.test(options)) {
return;
}
$this.data(NAMESPACE, (data = new Cropper(this, options)));
}
if (typeof options === 'string' && $.isFunction(fn = data[options])) {
result = fn.apply(data, args);
}
});
return isUndefined(result) ? this : result;
};
$.fn.cropper.Constructor = Cropper;
$.fn.cropper.setDefaults = Cropper.setDefaults;
// No conflict
$.fn.cropper.noConflict = function () {
$.fn.cropper = Cropper.other;
return this;
};

View File

@@ -0,0 +1,93 @@
$.extend(prototype, {
initPreview: function () {
var crossOrigin = getCrossOrigin(this.crossOrigin);
var url = this.url;
this.$preview = $(this.options.preview);
this.$viewBox.html('<img' + crossOrigin + ' src="' + url + '">');
this.$preview.each(function () {
var $this = $(this);
// Save the original size for recover
$this.data(DATA_PREVIEW, {
width: $this.width(),
height: $this.height(),
original: $this.html()
});
/**
* Override img element styles
* Add `display:block` to avoid margin top issue
* (Occur only when margin-top <= -height)
*/
$this.html(
'<img' + crossOrigin + ' src="' + url + '" style="' +
'display:block;width:100%;height:auto;' +
'min-width:0!important;min-height:0!important;' +
'max-width:none!important;max-height:none!important;' +
'image-orientation:0deg!important">'
);
});
},
resetPreview: function () {
this.$preview.each(function () {
var $this = $(this);
$this.html($this.data(DATA_PREVIEW).original).removeData(DATA_PREVIEW);
});
},
preview: function () {
var image = this.image;
var canvas = this.canvas;
var cropBox = this.cropBox;
var cropBoxWidth = cropBox.width;
var cropBoxHeight = cropBox.height;
var width = image.width;
var height = image.height;
var left = cropBox.left - canvas.left - image.left;
var top = cropBox.top - canvas.top - image.top;
if (!this.cropped || this.disabled) {
return;
}
this.$viewBox.find('img').css({
width: width,
height: height,
marginLeft: -left,
marginTop: -top,
transform: getTransform(image)
});
this.$preview.each(function () {
var $this = $(this);
var data = $this.data(DATA_PREVIEW);
var originalWidth = data.width;
var originalHeight = data.height;
var newWidth = originalWidth;
var newHeight = originalHeight;
var ratio = 1;
if (cropBoxWidth) {
ratio = originalWidth / cropBoxWidth;
newHeight = cropBoxHeight * ratio;
}
if (cropBoxHeight && newHeight > originalHeight) {
ratio = originalHeight / cropBoxHeight;
newWidth = cropBoxWidth * ratio;
newHeight = originalHeight;
}
$this.width(newWidth).height(newHeight).find('img').css({
width: width * ratio,
height: height * ratio,
marginLeft: -left * ratio,
marginTop: -top * ratio,
transform: getTransform(image)
});
});
}
});

View File

@@ -0,0 +1 @@
$.extend(Cropper.prototype, prototype);

View File

@@ -0,0 +1,392 @@
$.extend(prototype, {
render: function () {
this.initContainer();
this.initCanvas();
this.initCropBox();
this.renderCanvas();
if (this.cropped) {
this.renderCropBox();
}
},
initContainer: function () {
var options = this.options;
var $this = this.$element;
var $container = this.$container;
var $cropper = this.$cropper;
$cropper.addClass(CLASS_HIDDEN);
$this.removeClass(CLASS_HIDDEN);
$cropper.css((this.container = {
width: max($container.width(), num(options.minContainerWidth) || 200),
height: max($container.height(), num(options.minContainerHeight) || 100)
}));
$this.addClass(CLASS_HIDDEN);
$cropper.removeClass(CLASS_HIDDEN);
},
// Canvas (image wrapper)
initCanvas: function () {
var container = this.container;
var containerWidth = container.width;
var containerHeight = container.height;
var image = this.image;
var aspectRatio = image.aspectRatio;
var canvas = {
aspectRatio: aspectRatio,
width: containerWidth,
height: containerHeight
};
if (containerHeight * aspectRatio > containerWidth) {
canvas.height = containerWidth / aspectRatio;
} else {
canvas.width = containerHeight * aspectRatio;
}
canvas.oldLeft = canvas.left = (containerWidth - canvas.width) / 2;
canvas.oldTop = canvas.top = (containerHeight - canvas.height) / 2;
this.canvas = canvas;
this.limitCanvas(true, true);
this.initialImage = $.extend({}, image);
this.initialCanvas = $.extend({}, canvas);
},
limitCanvas: function (size, position) {
var options = this.options;
var strict = options.strict;
var container = this.container;
var containerWidth = container.width;
var containerHeight = container.height;
var canvas = this.canvas;
var aspectRatio = canvas.aspectRatio;
var cropBox = this.cropBox;
var cropped = this.cropped && cropBox;
var initialCanvas = this.initialCanvas || canvas;
var minCanvasWidth;
var minCanvasHeight;
if (size) {
minCanvasWidth = num(options.minCanvasWidth) || 0;
minCanvasHeight = num(options.minCanvasHeight) || 0;
if (strict) {
if (minCanvasWidth) {
minCanvasWidth = max(minCanvasWidth, cropped ? cropBox.width : initialCanvas.width);
} else if (minCanvasHeight) {
minCanvasHeight = max(minCanvasHeight, cropped ? cropBox.height : initialCanvas.height);
} else if (cropped) {
minCanvasWidth = cropBox.width;
minCanvasHeight = cropBox.height;
if (minCanvasHeight * aspectRatio > minCanvasWidth) {
minCanvasWidth = minCanvasHeight * aspectRatio;
} else {
minCanvasHeight = minCanvasWidth / aspectRatio;
}
}
}
if (minCanvasWidth && minCanvasHeight) {
if (minCanvasHeight * aspectRatio > minCanvasWidth) {
minCanvasHeight = minCanvasWidth / aspectRatio;
} else {
minCanvasWidth = minCanvasHeight * aspectRatio;
}
} else if (minCanvasWidth) {
minCanvasHeight = minCanvasWidth / aspectRatio;
} else if (minCanvasHeight) {
minCanvasWidth = minCanvasHeight * aspectRatio;
}
canvas.minWidth = minCanvasWidth;
canvas.minHeight = minCanvasHeight;
canvas.maxWidth = Infinity;
canvas.maxHeight = Infinity;
}
if (position) {
if (strict) {
canvas.minLeft = cropped ?
min(cropBox.left, (cropBox.left + cropBox.width) - canvas.width) :
min(0, containerWidth - canvas.width);
canvas.minTop = cropped ?
min(cropBox.top, (cropBox.top + cropBox.height) - canvas.height) :
min(0, containerHeight - canvas.height);
canvas.maxLeft = cropped ? cropBox.left : max(0, containerWidth - canvas.width);
canvas.maxTop = cropped ? cropBox.top : max(0, containerHeight - canvas.height);
} else {
canvas.minLeft = -canvas.width;
canvas.minTop = -canvas.height;
canvas.maxLeft = containerWidth;
canvas.maxTop = containerHeight;
}
}
},
renderCanvas: function (changed) {
var options = this.options;
var canvas = this.canvas;
var image = this.image;
var aspectRatio;
var rotated;
if (this.rotated) {
this.rotated = false;
// Computes rotated sizes with image sizes
rotated = getRotatedSizes({
width: image.width,
height: image.height,
degree: image.rotate
});
aspectRatio = rotated.width / rotated.height;
if (aspectRatio !== canvas.aspectRatio) {
canvas.left -= (rotated.width - canvas.width) / 2;
canvas.top -= (rotated.height - canvas.height) / 2;
canvas.width = rotated.width;
canvas.height = rotated.height;
canvas.aspectRatio = aspectRatio;
this.limitCanvas(true, false);
}
}
if (canvas.width > canvas.maxWidth || canvas.width < canvas.minWidth) {
canvas.left = canvas.oldLeft;
}
if (canvas.height > canvas.maxHeight || canvas.height < canvas.minHeight) {
canvas.top = canvas.oldTop;
}
canvas.width = min(max(canvas.width, canvas.minWidth), canvas.maxWidth);
canvas.height = min(max(canvas.height, canvas.minHeight), canvas.maxHeight);
this.limitCanvas(false, true);
canvas.oldLeft = canvas.left = min(max(canvas.left, canvas.minLeft), canvas.maxLeft);
canvas.oldTop = canvas.top = min(max(canvas.top, canvas.minTop), canvas.maxTop);
this.$canvas.css({
width: canvas.width,
height: canvas.height,
left: canvas.left,
top: canvas.top
});
this.renderImage();
if (this.cropped && options.strict) {
this.limitCropBox(true, true);
}
if (changed) {
this.output();
}
},
renderImage: function (changed) {
var canvas = this.canvas;
var image = this.image;
var reversed;
if (image.rotate) {
reversed = getRotatedSizes({
width: canvas.width,
height: canvas.height,
degree: image.rotate,
aspectRatio: image.aspectRatio
}, true);
}
$.extend(image, reversed ? {
width: reversed.width,
height: reversed.height,
left: (canvas.width - reversed.width) / 2,
top: (canvas.height - reversed.height) / 2
} : {
width: canvas.width,
height: canvas.height,
left: 0,
top: 0
});
this.$clone.css({
width: image.width,
height: image.height,
marginLeft: image.left,
marginTop: image.top,
transform: getTransform(image)
});
if (changed) {
this.output();
}
},
initCropBox: function () {
var options = this.options;
var canvas = this.canvas;
var aspectRatio = options.aspectRatio;
var autoCropArea = num(options.autoCropArea) || 0.8;
var cropBox = {
width: canvas.width,
height: canvas.height
};
if (aspectRatio) {
if (canvas.height * aspectRatio > canvas.width) {
cropBox.height = cropBox.width / aspectRatio;
} else {
cropBox.width = cropBox.height * aspectRatio;
}
}
this.cropBox = cropBox;
this.limitCropBox(true, true);
// Initialize auto crop area
cropBox.width = min(max(cropBox.width, cropBox.minWidth), cropBox.maxWidth);
cropBox.height = min(max(cropBox.height, cropBox.minHeight), cropBox.maxHeight);
// The width of auto crop area must large than "minWidth", and the height too. (#164)
cropBox.width = max(cropBox.minWidth, cropBox.width * autoCropArea);
cropBox.height = max(cropBox.minHeight, cropBox.height * autoCropArea);
cropBox.oldLeft = cropBox.left = canvas.left + (canvas.width - cropBox.width) / 2;
cropBox.oldTop = cropBox.top = canvas.top + (canvas.height - cropBox.height) / 2;
this.initialCropBox = $.extend({}, cropBox);
},
limitCropBox: function (size, position) {
var options = this.options;
var strict = options.strict;
var container = this.container;
var containerWidth = container.width;
var containerHeight = container.height;
var canvas = this.canvas;
var cropBox = this.cropBox;
var aspectRatio = options.aspectRatio;
var minCropBoxWidth;
var minCropBoxHeight;
var maxCropBoxWidth;
var maxCropBoxHeight;
if (size) {
minCropBoxWidth = num(options.minCropBoxWidth) || 0;
minCropBoxHeight = num(options.minCropBoxHeight) || 0;
// The min/maxCropBoxWidth/Height must be less than containerWidth/Height
minCropBoxWidth = min(minCropBoxWidth, containerWidth);
minCropBoxHeight = min(minCropBoxHeight, containerHeight);
maxCropBoxWidth = min(containerWidth, strict ? canvas.width : containerWidth);
maxCropBoxHeight = min(containerHeight, strict ? canvas.height : containerHeight);
if (aspectRatio) {
if (minCropBoxWidth && minCropBoxHeight) {
if (minCropBoxHeight * aspectRatio > minCropBoxWidth) {
minCropBoxHeight = minCropBoxWidth / aspectRatio;
} else {
minCropBoxWidth = minCropBoxHeight * aspectRatio;
}
} else if (minCropBoxWidth) {
minCropBoxHeight = minCropBoxWidth / aspectRatio;
} else if (minCropBoxHeight) {
minCropBoxWidth = minCropBoxHeight * aspectRatio;
}
if (maxCropBoxHeight * aspectRatio > maxCropBoxWidth) {
maxCropBoxHeight = maxCropBoxWidth / aspectRatio;
} else {
maxCropBoxWidth = maxCropBoxHeight * aspectRatio;
}
}
// The minWidth/Height must be less than maxWidth/Height
cropBox.minWidth = min(minCropBoxWidth, maxCropBoxWidth);
cropBox.minHeight = min(minCropBoxHeight, maxCropBoxHeight);
cropBox.maxWidth = maxCropBoxWidth;
cropBox.maxHeight = maxCropBoxHeight;
}
if (position) {
if (strict) {
cropBox.minLeft = max(0, canvas.left);
cropBox.minTop = max(0, canvas.top);
cropBox.maxLeft = min(containerWidth, canvas.left + canvas.width) - cropBox.width;
cropBox.maxTop = min(containerHeight, canvas.top + canvas.height) - cropBox.height;
} else {
cropBox.minLeft = 0;
cropBox.minTop = 0;
cropBox.maxLeft = containerWidth - cropBox.width;
cropBox.maxTop = containerHeight - cropBox.height;
}
}
},
renderCropBox: function () {
var options = this.options;
var container = this.container;
var containerWidth = container.width;
var containerHeight = container.height;
var cropBox = this.cropBox;
if (cropBox.width > cropBox.maxWidth || cropBox.width < cropBox.minWidth) {
cropBox.left = cropBox.oldLeft;
}
if (cropBox.height > cropBox.maxHeight || cropBox.height < cropBox.minHeight) {
cropBox.top = cropBox.oldTop;
}
cropBox.width = min(max(cropBox.width, cropBox.minWidth), cropBox.maxWidth);
cropBox.height = min(max(cropBox.height, cropBox.minHeight), cropBox.maxHeight);
this.limitCropBox(false, true);
cropBox.oldLeft = cropBox.left = min(max(cropBox.left, cropBox.minLeft), cropBox.maxLeft);
cropBox.oldTop = cropBox.top = min(max(cropBox.top, cropBox.minTop), cropBox.maxTop);
if (options.movable && options.cropBoxMovable) {
// Turn to move the canvas when the crop box is equal to the container
this.$face.data(DATA_ACTION, (cropBox.width === containerWidth && cropBox.height === containerHeight) ? ACTION_MOVE : ACTION_ALL);
}
this.$cropBox.css({
width: cropBox.width,
height: cropBox.height,
left: cropBox.left,
top: cropBox.top
});
if (this.cropped && options.strict) {
this.limitCanvas(true, true);
}
if (!this.disabled) {
this.output();
}
},
output: function () {
this.preview();
if (this.complete) {
this.trigger(EVENT_CROP, this.getData());
} else if (!this.built) {
// Only trigger one crop event before complete
this.$element.one(EVENT_BUILT, $.proxy(function () {
this.trigger(EVENT_CROP, this.getData());
}, this));
}
}
});

View File

@@ -0,0 +1,25 @@
Cropper.TEMPLATE = (
'<div class="cropper-container">' +
'<div class="cropper-canvas"></div>' +
'<div class="cropper-drag-box"></div>' +
'<div class="cropper-crop-box">' +
'<span class="cropper-view-box"></span>' +
'<span class="cropper-dashed dashed-h"></span>' +
'<span class="cropper-dashed dashed-v"></span>' +
'<span class="cropper-center"></span>' +
'<span class="cropper-face"></span>' +
'<span class="cropper-line line-e" data-action="e"></span>' +
'<span class="cropper-line line-n" data-action="n"></span>' +
'<span class="cropper-line line-w" data-action="w"></span>' +
'<span class="cropper-line line-s" data-action="s"></span>' +
'<span class="cropper-point point-e" data-action="e"></span>' +
'<span class="cropper-point point-n" data-action="n"></span>' +
'<span class="cropper-point point-w" data-action="w"></span>' +
'<span class="cropper-point point-s" data-action="s"></span>' +
'<span class="cropper-point point-ne" data-action="ne"></span>' +
'<span class="cropper-point point-nw" data-action="nw"></span>' +
'<span class="cropper-point point-sw" data-action="sw"></span>' +
'<span class="cropper-point point-se" data-action="se"></span>' +
'</div>' +
'</div>'
);

View File

@@ -0,0 +1,173 @@
function isNumber(n) {
return typeof n === 'number' && !isNaN(n);
}
function isUndefined(n) {
return typeof n === 'undefined';
}
function toArray(obj, offset) {
var args = [];
// This is necessary for IE8
if (isNumber(offset)) {
args.push(offset);
}
return args.slice.apply(obj, args);
}
// Custom proxy to avoid jQuery's guid
function proxy(fn, context) {
var args = toArray(arguments, 2);
return function () {
return fn.apply(context, args.concat(toArray(arguments)));
};
}
function isCrossOriginURL(url) {
var parts = url.match(/^(https?:)\/\/([^\:\/\?#]+):?(\d*)/i);
return parts && (
parts[1] !== location.protocol ||
parts[2] !== location.hostname ||
parts[3] !== location.port
);
}
function addTimestamp(url) {
var timestamp = 'timestamp=' + (new Date()).getTime();
return (url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp);
}
function getCrossOrigin(crossOrigin) {
return crossOrigin ? ' crossOrigin="' + crossOrigin + '"' : '';
}
function getImageSize(image, callback) {
var newImage;
// Modern browsers
if (image.naturalWidth) {
return callback(image.naturalWidth, image.naturalHeight);
}
// IE8: Don't use `new Image()` here (#319)
newImage = document.createElement('img');
newImage.onload = function () {
callback(this.width, this.height);
};
newImage.src = image.src;
}
function getTransform(options) {
var transforms = [];
var rotate = options.rotate;
var scaleX = options.scaleX;
var scaleY = options.scaleY;
if (isNumber(rotate)) {
transforms.push('rotate(' + rotate + 'deg)');
}
if (isNumber(scaleX) && isNumber(scaleY)) {
transforms.push('scale(' + scaleX + ',' + scaleY + ')');
}
return transforms.length ? transforms.join(' ') : 'none';
}
function getRotatedSizes(data, reverse) {
var deg = abs(data.degree) % 180;
var arc = (deg > 90 ? (180 - deg) : deg) * Math.PI / 180;
var sinArc = sin(arc);
var cosArc = cos(arc);
var width = data.width;
var height = data.height;
var aspectRatio = data.aspectRatio;
var newWidth;
var newHeight;
if (!reverse) {
newWidth = width * cosArc + height * sinArc;
newHeight = width * sinArc + height * cosArc;
} else {
newWidth = width / (cosArc + sinArc / aspectRatio);
newHeight = newWidth / aspectRatio;
}
return {
width: newWidth,
height: newHeight
};
}
function getSourceCanvas(image, data) {
var canvas = $('<canvas>')[0];
var context = canvas.getContext('2d');
var x = 0;
var y = 0;
var width = data.naturalWidth;
var height = data.naturalHeight;
var rotate = data.rotate;
var scaleX = data.scaleX;
var scaleY = data.scaleY;
var scalable = isNumber(scaleX) && isNumber(scaleY) && (scaleX !== 1 || scaleY !== 1);
var rotatable = isNumber(rotate) && rotate !== 0;
var advanced = rotatable || scalable;
var canvasWidth = width;
var canvasHeight = height;
var translateX;
var translateY;
var rotated;
if (scalable) {
translateX = width / 2;
translateY = height / 2;
}
if (rotatable) {
rotated = getRotatedSizes({
width: width,
height: height,
degree: rotate
});
canvasWidth = rotated.width;
canvasHeight = rotated.height;
translateX = rotated.width / 2;
translateY = rotated.height / 2;
}
canvas.width = canvasWidth;
canvas.height = canvasHeight;
if (advanced) {
x = -width / 2;
y = -height / 2;
context.save();
context.translate(translateX, translateY);
}
if (rotatable) {
context.rotate(rotate * Math.PI / 180);
}
// Should call `scale` after rotated
if (scalable) {
context.scale(scaleX, scaleY);
}
context.drawImage(image, x, y, width, height);
if (advanced) {
context.restore();
}
return canvas;
}

View File

@@ -0,0 +1,73 @@
// Globals
var $window = $(window);
var $document = $(document);
var location = window.location;
// Constants
var NAMESPACE = 'cropper';
// Classes
var CLASS_MODAL = 'cropper-modal';
var CLASS_HIDE = 'cropper-hide';
var CLASS_HIDDEN = 'cropper-hidden';
var CLASS_INVISIBLE = 'cropper-invisible';
var CLASS_MOVE = 'cropper-move';
var CLASS_CROP = 'cropper-crop';
var CLASS_DISABLED = 'cropper-disabled';
var CLASS_BG = 'cropper-bg';
// Events
var EVENT_MOUSE_DOWN = 'mousedown touchstart pointerdown MSPointerDown';
var EVENT_MOUSE_MOVE = 'mousemove touchmove pointermove MSPointerMove';
var EVENT_MOUSE_UP = 'mouseup touchend touchcancel pointerup pointercancel MSPointerUp MSPointerCancel';
var EVENT_WHEEL = 'wheel mousewheel DOMMouseScroll';
var EVENT_DBLCLICK = 'dblclick';
var EVENT_LOAD = 'load.' + NAMESPACE;
var EVENT_ERROR = 'error.' + NAMESPACE;
var EVENT_RESIZE = 'resize.' + NAMESPACE; // Bind to window with namespace
var EVENT_BUILD = 'build.' + NAMESPACE;
var EVENT_BUILT = 'built.' + NAMESPACE;
var EVENT_CROP_START = 'cropstart.' + NAMESPACE;
var EVENT_CROP_MOVE = 'cropmove.' + NAMESPACE;
var EVENT_CROP_END = 'cropend.' + NAMESPACE;
var EVENT_CROP = 'crop.' + NAMESPACE;
var EVENT_ZOOM = 'zoom.' + NAMESPACE;
// RegExps
var REGEXP_ACTIONS = /^(e|w|s|n|se|sw|ne|nw|all|crop|move|zoom)$/;
// Data keys
var DATA_PREVIEW = 'preview';
var DATA_ACTION = 'action';
// Actions
var ACTION_EAST = 'e';
var ACTION_WEST = 'w';
var ACTION_SOUTH = 's';
var ACTION_NORTH = 'n';
var ACTION_SOUTH_EAST = 'se';
var ACTION_SOUTH_WEST = 'sw';
var ACTION_NORTH_EAST = 'ne';
var ACTION_NORTH_WEST = 'nw';
var ACTION_ALL = 'all';
var ACTION_CROP = 'crop';
var ACTION_MOVE = 'move';
var ACTION_ZOOM = 'zoom';
var ACTION_NONE = 'none';
// Supports
var SUPPORT_CANVAS = $.isFunction($('<canvas>')[0].getContext);
// Maths
var sqrt = Math.sqrt;
var min = Math.min;
var max = Math.max;
var abs = Math.abs;
var sin = Math.sin;
var cos = Math.cos;
var num = parseFloat;
// Prototype
var prototype = {
version: '@VERSION'
};

View File

@@ -0,0 +1,249 @@
// Cropper
// =========================
.cropper-container {
position: relative;
font-size: 0;
line-height: 0;
overflow: hidden;
direction: ltr !important; // Ignore global rtl
touch-action: none;
user-select: none;
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
img {
// Avoid margin top issue (Occur only when margin-top <= -height)
display: block;
// https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation
image-orientation: 0deg !important;
min-width: 0 !important;
min-height: 0 !important;
max-width: none !important;
max-height: none !important;
@include square(100%);
}
}
.cropper-canvas,
.cropper-drag-box,
.cropper-crop-box,
.cropper-modal {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.cropper-drag-box {
background-color: #fff;
@include opacity(0);
}
.cropper-modal {
background-color: #000;
@include opacity(.5);
}
.cropper-view-box {
display: block;
overflow: hidden;
outline: 1px solid $color-brand;
outline-color: rgba($color-brand, .75);
@include square(100%);
}
.cropper-dashed {
position: absolute;
display: block;
border: 0 dashed #eee;
@include opacity(.5);
&.dashed-h {
top: percentage(1 / 3);
left: 0;
border-top-width: 1px;
border-bottom-width: 1px;
@include size(100%, percentage(1 / 3));
}
&.dashed-v {
top: 0;
left: percentage(1 / 3);
border-right-width: 1px;
border-left-width: 1px;
@include size(percentage(1 / 3), 100%);
}
}
.cropper-center {
position: absolute;
top: 50%;
left: 50%;
display: block;
width: 0;
height: 0;
@include opacity(.75);
&:before,
&:after {
position: absolute;
display: block;
background-color: #eee;
content: " ";
}
&:before {
top: 0;
left: -3px;
width: 7px;
height: 1px;
}
&:after {
top: -3px;
left: 0;
width: 1px;
height: 7px;
}
}
.cropper-face,
.cropper-line,
.cropper-point {
position: absolute;
display: block;
@include square(100%);
@include opacity(.1);
}
.cropper-face {
top: 0;
left: 0;
background-color: #fff;
}
.cropper-line {
background-color: $color-brand;
&.line-e {
top: 0;
right: -3px;
width: 5px;
cursor: e-resize;
}
&.line-n {
top: -3px;
left: 0;
height: 5px;
cursor: n-resize;
}
&.line-w {
top: 0;
left: -3px;
width: 5px;
cursor: w-resize;
}
&.line-s {
bottom: -3px;
left: 0;
height: 5px;
cursor: s-resize;
}
}
.cropper-point {
background-color: $color-brand;
@include square(5px);
@include opacity(.75);
&.point-e {
top: 50%;
right: -3px;
margin-top: -3px;
cursor: e-resize;
}
&.point-n {
top: -3px;
left: 50%;
margin-left: -3px;
cursor: n-resize;
}
&.point-w {
top: 50%;
left: -3px;
margin-top: -3px;
cursor: w-resize;
}
&.point-s {
bottom: -3px;
left: 50%;
margin-left: -3px;
cursor: s-resize;
}
&.point-ne {
top: -3px;
right: -3px;
cursor: ne-resize;
}
&.point-nw {
top: -3px;
left: -3px;
cursor: nw-resize;
}
&.point-sw {
bottom: -3px;
left: -3px;
cursor: sw-resize;
}
&.point-se {
right: -3px;
bottom: -3px;
cursor: se-resize;
@include square(20px);
@include opacity(1);
}
&.point-se:before {
position: absolute;
right: -50%;
bottom: -50%;
display: block;
content: " ";
background-color: $color-brand;
@include square(200%);
@include opacity(0);
}
@media (min-width: $screen-sm) {
&.point-se {
@include square(15px);
}
}
@media (min-width: $screen-md) {
&.point-se {
@include square(10px);
}
}
@media (min-width: $screen-lg) {
&.point-se {
@include square(5px);
@include opacity(.75);
}
}
}

View File

@@ -0,0 +1,23 @@
// Mixins
// =========================
// Opacity
// -------------------------
@mixin opacity($opacity) {
opacity: $opacity;
filter: alpha(opacity=#{$opacity * 100}); // IE8
}
// Sizing shortcuts
// -------------------------
@mixin size($width, $height) {
width: $width;
height: $height;
}
@mixin square($size) {
@include size($size, $size);
}

View File

@@ -0,0 +1,49 @@
// Helper classes for JavaScript
// =========================
// Visual
// -------------------------
.cropper-bg {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC");
}
.cropper-invisible {
@include opacity(0);
}
// Visibility
// -------------------------
.cropper-hide {
position: absolute;
display: block;
width: 0;
height: 0;
}
.cropper-hidden {
display: none !important;
}
// Cursors
// -------------------------
.cropper-move {
cursor: move;
}
.cropper-crop {
cursor: crosshair;
}
.cropper-disabled {
.cropper-drag-box,
.cropper-face,
.cropper-line,
.cropper-point {
cursor: not-allowed;
}
}

View File

@@ -0,0 +1,19 @@
// Variables
// =========================
// Colors
// -------------------------
$color-brand: #39f;
$color-black: #000;
$color-white: #fff;
// Media queries breakpoints
// -------------------------
$screen-xs: 480px; // Extra small screen / phone
$screen-sm: 768px; // Small screen / tablet
$screen-md: 992px; // Medium screen / desktop
$screen-lg: 1200px; // Large screen / wide desktop

View File

@@ -0,0 +1,14 @@
/*!
* Cropper v@VERSION
* https://github.com/fengyuanchen/cropper
*
* Copyright (c) 2014-@YEAR Fengyuan Chen and contributors
* Released under the MIT license
*
* Date: @DATE
*/
@import "variables.scss";
@import "mixins.scss";
@import "main.scss";
@import "utilities.scss";