├── .gitignore
├── images
├── demo.gif
└── logo.png
├── tests
├── fixtures
│ ├── 250x350.gif
│ └── 350x250.gif
├── index.html
└── runner.js
├── .travis.yml
├── bower.json
├── drag-n-crop.jquery.json
├── LICENSE
├── package.json
├── karma.conf.js
├── jquery.drag-n-crop.css
├── README.md
├── jquery.drag-n-crop.js
└── jquery.drag-n-crop.amd.js
/.gitignore:
--------------------------------------------------------------------------------
1 | bower_components/
2 | node_modules/
3 | _site
--------------------------------------------------------------------------------
/images/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lukaszfiszer/drag-n-crop/HEAD/images/demo.gif
--------------------------------------------------------------------------------
/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lukaszfiszer/drag-n-crop/HEAD/images/logo.png
--------------------------------------------------------------------------------
/tests/fixtures/250x350.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lukaszfiszer/drag-n-crop/HEAD/tests/fixtures/250x350.gif
--------------------------------------------------------------------------------
/tests/fixtures/350x250.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lukaszfiszer/drag-n-crop/HEAD/tests/fixtures/350x250.gif
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "0.10"
4 | before_install: # make "bower install" availabke
5 | - npm install bower -g
6 | before_script: # start xvfb to use GUI browsers
7 | - export DISPLAY=:99.0
8 | - sh -e /etc/init.d/xvfb start
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jquery.drag-n-crop",
3 | "version": "0.1.1",
4 | "main": ["jquery.drag-n-crop.js", "jquery.drag-n-crop.css"],
5 | "keywords": [
6 | "jquery",
7 | "drag",
8 | "crop",
9 | "cropping",
10 | "facebook"
11 | ],
12 | "ignore": [
13 | "**/.*",
14 | "*.json",
15 | "karma.conf.js",
16 | "node_modules",
17 | "bower_components",
18 | "images",
19 | "tests"
20 | ],
21 | "dependencies": {
22 | "jquery": "~1.9.1",
23 | "jquery-ui": "~1.10.1",
24 | "imagesloaded": "~3.1.4"
25 | },
26 | "devDependencies": {
27 | "mocha": "~1.8.1",
28 | "chai": "~1.5.0"
29 | }
30 | }
31 |
32 |
33 |
--------------------------------------------------------------------------------
/drag-n-crop.jquery.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "drag-n-crop",
3 | "title": "jQuery Drag-n-Crop",
4 | "description": "jQuery plugin for cropping images like Facebook cover photo",
5 | "keywords": [
6 | "drag",
7 | "crop",
8 | "cropping",
9 | "facebook"
10 | ],
11 | "version": "0.1.1",
12 | "author": {
13 | "name": "Lukasz Fiszer",
14 | "url": "http://github.com/lukaszfiszer"
15 | },
16 | "licenses": [
17 | {
18 | "type": "MIT",
19 | "url": "https://raw2.github.com/lukaszfiszer/drag-n-crop/master/LICENSE"
20 | }
21 | ],
22 | "bugs": "https://github.com/lukaszfiszer/drag-n-crop/issues",
23 | "homepage": "http://lukaszfiszer.github.io/drag-n-crop/",
24 | "docs": "http://lukaszfiszer.github.io/drag-n-crop/",
25 | "download": "https://github.com/lukaszfiszer/drag-n-crop/",
26 | "dependencies": {
27 | "jquery": ">=1.9",
28 | "jquery-ui": ">=1.9"
29 | }
30 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012 Lukasz Fiszer
2 |
3 | Permission is hereby granted, free of charge, to any person
4 | obtaining a copy of this software and associated documentation
5 | files (the "Software"), to deal in the Software without
6 | restriction, including without limitation the rights to use,
7 | copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the
9 | Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jquery.drag-n-crop",
3 | "version": "0.1.1",
4 | "description": "jQuery drag'n'crop plugin",
5 | "main": "jquery.drag-n-crop.js",
6 | "directories": {
7 | "example": "examples",
8 | "test": "tests"
9 | },
10 | "keywords": [
11 | "jquery",
12 | "drag",
13 | "crop",
14 | "cropping",
15 | "facebook"
16 | ],
17 | "dependencies": {},
18 | "devDependencies": {
19 | "karma-mocha": "~0.1.1",
20 | "karma": "~0.10.9"
21 | },
22 | "scripts": {
23 | "postinstall": "bower install",
24 | "test": "./node_modules/.bin/karma start --single-run"
25 | },
26 | "author": {
27 | "name": "Lukasz Fiszer",
28 | "url": "http://github.com/lukaszfiszer"
29 | },
30 | "contributors": [],
31 | "repository": {
32 | "type": "git",
33 | "url": "git://github.com/lukaszfiszer/drag-n-crop.git"
34 | },
35 | "homepage": "http://lukaszfiszer.github.io/drag-n-crop/",
36 | "bugs": {
37 | "url": "https://github.com/lukaszfiszer/drag-n-crop/issues"
38 | },
39 | "licenses": [
40 | {
41 | "type": "MIT",
42 | "url": "https://raw2.github.com/lukaszfiszer/drag-n-crop/master/LICENSE"
43 | }
44 | ]
45 | }
--------------------------------------------------------------------------------
/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Mocha Tests
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
24 |
25 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Sat Feb 15 2014 14:29:17 GMT+0100 (CET)
3 |
4 | module.exports = function(config) {
5 | config.set({
6 |
7 | // base path, that will be used to resolve files and exclude
8 | basePath: '',
9 |
10 |
11 | // frameworks to use
12 | frameworks: ['mocha'],
13 |
14 |
15 | // list of files / patterns to load in the browser
16 | files: [
17 | 'bower_components/jquery/jquery.js',
18 | 'bower_components/jquery-ui/ui/jquery.ui.core.js',
19 | 'bower_components/jquery-ui/ui/jquery.ui.widget.js',
20 | 'bower_components/jquery-ui/ui/jquery.ui.mouse.js',
21 | 'bower_components/jquery-ui/ui/jquery.ui.draggable.js',
22 | 'bower_components/eventEmitter/EventEmitter.js',
23 | 'bower_components/eventie/eventie.js',
24 | 'bower_components/imagesloaded/imagesloaded.js',
25 | 'bower_components/chai/chai.js',
26 | 'jquery.drag-n-crop.js',
27 | 'jquery.drag-n-crop.css',
28 | 'tests/*.js',
29 | {
30 | pattern: 'tests/fixtures/*',
31 | included: false,
32 | served: true
33 | }
34 | ],
35 |
36 |
37 | // list of files to exclude
38 | exclude: [
39 |
40 | ],
41 |
42 |
43 | // test results reporter to use
44 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
45 | reporters: ['progress'],
46 |
47 |
48 | // web server port
49 | port: 9876,
50 |
51 |
52 | // enable / disable colors in the output (reporters and logs)
53 | colors: true,
54 |
55 |
56 | // level of logging
57 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
58 | logLevel: config.LOG_INFO,
59 |
60 |
61 | // enable / disable watching file and executing tests whenever any file changes
62 | autoWatch: true,
63 |
64 |
65 | // Start these browsers, currently available:
66 | // - Chrome
67 | // - ChromeCanary
68 | // - Firefox
69 | // - Opera (has to be installed with `npm install karma-opera-launcher`)
70 | // - Safari (only Mac; has to be installed with `npm install karma-safari-launcher`)
71 | // - PhantomJS
72 | // - IE (only Windows; has to be installed with `npm install karma-ie-launcher`)
73 | browsers: ['PhantomJS'],
74 |
75 |
76 | // If browser does not capture in given timeout [ms], kill it
77 | captureTimeout: 60000,
78 |
79 |
80 | // Continuous Integration mode
81 | // if true, it capture browsers, run tests and exit
82 | singleRun: false
83 | });
84 | };
85 |
--------------------------------------------------------------------------------
/jquery.drag-n-crop.css:
--------------------------------------------------------------------------------
1 | /*
2 | DEFAULT CLASSES
3 | */
4 |
5 | .dragncrop {
6 | position: relative;
7 | overflow: hidden;
8 | }
9 |
10 | .dragncrop img {
11 | display: block;
12 | }
13 |
14 | /* TODO: cross-browser cursors */
15 | .ui-draggable,
16 | .dragncrop-overlay {
17 | cursor:grab;
18 | cursor:-moz-grab;
19 | cursor:-webkit-grab;
20 | }
21 |
22 | .ui-draggable-dragging {
23 | cursor:grabbing;
24 | cursor:-moz-grabbing;
25 | cursor:-webkit-grabbing;
26 | }
27 |
28 | .dragncrop-horizontal {
29 | max-width: none;
30 | height: 100%;
31 | }
32 |
33 | .dragncrop-vertical {
34 | width: 100%;
35 | max-height: none;
36 | }
37 |
38 | .dragncrop-containment {
39 | position: absolute;
40 | }
41 |
42 | /*
43 | OPTIONAL CLASSES
44 | */
45 |
46 | /* Overlow */
47 | .dragncrop-dragging.dragncrop-overflow {
48 | overflow: visible;
49 | }
50 |
51 | /* Overlay */
52 | .dragncrop-overlay {
53 | position: absolute;
54 | opacity: 0.5;
55 | top: 0;
56 | left: 0;
57 | bottom: 0;
58 | right: 0;
59 | border-style: solid;
60 | border-color: black;
61 | border-width: 0;
62 | }
63 |
64 | .dragncrop-dragging .dragncrop-overlay {
65 | z-index: 1;
66 | }
67 |
68 | /* Instruction */
69 | .dragncrop-instruction {
70 | position: absolute;
71 | top: 40%;
72 | opacity: 0.7;
73 | background: black;
74 | color: #DDD;
75 | text-align: center;
76 | border-radius: 6px;
77 | line-height: 1;
78 | box-sizing: border-box;
79 | font-family: sans-serif;
80 | z-index: 1;
81 | font-size: 0.875em;
82 | right: 15%;
83 | left: 15%;
84 | padding: 10px 12px;
85 | }
86 |
87 | .dragncrop-instruction-text {
88 | color: #DDD;
89 | text-align: center;
90 | line-height: 1;
91 | font-family: sans-serif;
92 | font-size: 0.875em;
93 | background-repeat: no-repeat;
94 | background-position: 0 50%;
95 | display: block;
96 | padding-left: 20px
97 | }
98 |
99 | .dragncrop-horizontal + .dragncrop-instruction .dragncrop-instruction-text {
100 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAqklEQVQ4T2NkoDJgpLJ5DKMGUh6i8DD8//+/ANC4XCCezMjI+IEYo7HpARsIldgNZJoAsSPQwANEGugAVLcfiM8BsTPIIYxohoHMWQDED4gxEKhGAYgToGohhgINBLnMhUgDCCnbCzLwFFCVKSGVRMrvg3l5D1CDMVTTQhK9HA/Vdx5IOyFHCsxQciIFYhgoUmBegUZOHpA/icRkg6JnNC8TmTjwKBv8YQgAitFBotB3lzcAAAAASUVORK5CYII=');
101 | }
102 |
103 | .dragncrop-vertical + .dragncrop-instruction .dragncrop-instruction-text {
104 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAv0lEQVQ4T2NkQAL////vAXKDgDiQkZHxIrIcsWxGmEKgYXOA7GQo/z2QdiTHULCBaIbB7CDLUEagYQZAE84CMRMWb+0FutKFWO+C1MFcaAdkGwFxP1TzOSBdDMT3gQY+JNlAqLcdgPR+qGaSXQazFDlSRg0kLXZHwxCc/QZxsgG6DpT1DIF4AjS2QHkblPUekJz1oIUDKO/Ccw1S3iU5C+Irvj4ADXYgtUzEVcCSZRi8+IJ5EVoFgEptkl0GMwMA7BJsFXeMXW4AAAAASUVORK5CYII=');
105 | }
106 |
107 | .dragncrop:hover .dragncrop-instruction-autohide,
108 | .dragncrop-dragging .dragncrop-instruction-autohide {
109 | display: none;
110 | }
111 |
--------------------------------------------------------------------------------
/tests/runner.js:
--------------------------------------------------------------------------------
1 | /*global describe:false, it:false, assert:false,
2 | beforeEach:false, afterEach:false, before:false, after:false */
3 |
4 | describe('jquery.dragncrop.js', function() {
5 |
6 | var sandbox, imgv, imgh, assert = chai.assert;
7 |
8 | before(function() {
9 | sandbox = $('').appendTo($('body'))
10 | })
11 |
12 | beforeEach(function() {
13 | sandbox.append($('
'));
14 | sandbox.append($('
'));
15 | });
16 |
17 | afterEach(function() {
18 | sandbox.html('');
19 | });
20 |
21 | it('is a jquery plugin', function() {
22 | assert.isFunction($.fn.dragncrop);
23 | });
24 |
25 | it('returns the object', function() {
26 | var el = $('#imgv');
27 | assert.equal(el, el.dragncrop());
28 | });
29 |
30 | describe('after initialization', function() {
31 |
32 | it('is an instance of jquery.ui.draggable', function(done) {
33 | var el = $('#imgv').dragncrop();
34 | setTimeout(function() {
35 | // if is not an instance, will throw error
36 | el.draggable('widget');
37 | done()
38 | }, 50);
39 | });
40 |
41 | it('adds class to the parent container if it is not present', function() {
42 | var el = $('#imgv').dragncrop();
43 | assert.isTrue(el.parent().hasClass('dragncrop'));
44 | });
45 |
46 | it('applies "dragncrop-horizontal" class on the element if horizontal', function(done) {
47 | $('#imgh').dragncrop();
48 | setTimeout(function() {
49 | assert.isTrue($('#imgh').hasClass('dragncrop-horizontal'));
50 | done();
51 | }, 50);
52 | });
53 |
54 | it('applies "dragncrop-horizontal" class on the element if vertical', function(done) {
55 | $('#imgv').dragncrop();
56 | setTimeout(function() {
57 | assert.isTrue($('#imgv').hasClass('dragncrop-vertical'));
58 | done();
59 | }, 50);
60 | });
61 |
62 | it('applies "dragncrop-vertical" class on the element in function of proportion', function(done) {
63 | $('#imgv').dragncrop();
64 | $('#sandbox img').imagesLoaded(function() {
65 | assert.isTrue($('#imgv').hasClass('dragncrop-vertical'));
66 | done();
67 | });
68 | });
69 |
70 | it('resizes vertical img to container', function(done) {
71 | $('#imgv').dragncrop();
72 | $('#sandbox img').imagesLoaded(function() {
73 | assert.equal($('#imgv').width(), 200);
74 | done();
75 | });
76 | });
77 |
78 | it('resizes horizontal img to container', function(done) {
79 | $('#imgh').dragncrop();
80 | $('#sandbox img').imagesLoaded(function() {
81 | assert.equal($('#imgh').height(), 200);
82 | done();
83 | });
84 | });
85 |
86 | });
87 |
88 |
89 | describe('getPosition method', function() {
90 |
91 | it('returns a dictionary with two types of coordinates for horizontal images', function(done) {
92 | var el = $('#imgh').dragncrop({centered: true});
93 | setTimeout(function() {
94 | var pos = el.dragncrop('getPosition');
95 | assert.equal(pos.offset[0], '0.5');
96 | assert.equal(pos.dimension[0].toPrecision(2), '0.14');
97 | done();
98 | }, 50);
99 | });
100 |
101 | it('returns a dictionary with two types of coordinates for vertical images', function(done) {
102 | var el = $('#imgv').dragncrop({centered: true});
103 | el.imagesLoaded(function() {
104 | var pos = el.dragncrop('getPosition');
105 | assert.equal(pos.offset[1], '0.5');
106 | assert.equal(pos.dimension[1].toPrecision(2), '0.14');
107 | done();
108 | });
109 | });
110 |
111 | });
112 |
113 | describe('destroy method', function() {
114 |
115 | it('removes draggable widget', function(done) {
116 | var err = null;
117 | var el = $('#imgv').dragncrop();
118 |
119 | el.imagesLoaded(function() {
120 | el.dragncrop('destroy');
121 | try {
122 | el.draggable('widget');
123 | }
124 | catch (e){
125 | err = e;
126 | }
127 | assert.isNotNull(err, 'Catched error');
128 | done();
129 | });
130 | });
131 |
132 | it('returns element inner html to the initial state', function(done) {
133 | var err = null;
134 | var el = $('#imgv').dragncrop();
135 | var before = el.parent().children();
136 | el.imagesLoaded(function() {
137 | var after = el.dragncrop('destroy').parent().children();
138 | assert.equal(before.length, after.length);
139 | done();
140 | });
141 | });
142 |
143 | it('cleans up classes from container', function(done) {
144 | var err = null;
145 | var el = $('#imgv').dragncrop();
146 | var before = el.parent().attr('class');
147 | el.imagesLoaded(function() {
148 | var after = el.dragncrop('destroy').parent().attr('class');
149 | assert.equal(before, after);
150 | done();
151 | });
152 | });
153 |
154 | it('cleans up classes from element', function(done) {
155 | var err = null;
156 | var el = $('#imgv').dragncrop();
157 | var before = el.attr('class') || '';
158 | el.imagesLoaded(function() {
159 | var after = el.dragncrop('destroy').attr('class');
160 | assert.equal(before, after);
161 | done();
162 | });
163 | });
164 |
165 | });
166 |
167 | });
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](https://travis-ci.org/lukaszfiszer/drag-n-crop)
4 |
5 | A jQuery plugin for croping images by dragging, inspired by Facebook cover photo.
6 |
7 | It aims to be minimalistic and very easy to use for the end-user. It allows to crop the image only in one dimension (no zooming!). A typical usecase would be to crop rectangular images into squares. If you search for a more advanced croping plugin, there are some [other](https://github.com/tapmodo/Jcrop) [plugins](http://odyniec.net/projects/imgareaselect/) available.
8 |
9 |
10 | Quick demo!
11 | -------------
12 | 
13 |
14 |
15 | Dependencies
16 | -------------
17 |
18 | * jQuery
19 | * jQuery UI draggable
20 | * [imagesloaded](/desandro/imagesloaded)
21 |
22 |
23 | Installation
24 | -----
25 |
26 | 1.Install the plugin with bower `bower install jquery.drag-n-crop` or download it from this repo.
27 |
28 | 2.Include drag'n'crop JS and CSS files (+ and its dependencies, if you dont have them already) on your site.
29 | ```
30 |
31 | (...)
32 |
33 | ```
34 | 3.Wrap the photo you want to crop in a element with target width and height
35 |
36 | ```
37 | 
38 | ```
39 |
40 | 4.Initialize the plugin
41 | ```
42 | $('#photo').dragncrop();
43 | ```
44 |
45 |
46 | API
47 | ---
48 |
49 | *jQuery.drag'n'crop* is build using [jQuery widget factory](http://api.jqueryui.com/jQuery.widget/), providing a standard way of interacting with the plugin. It inherits all default [options, events and methods](http://api.jqueryui.com/jQuery.widget/#jQuery-Widget2) but also provides some custom ones, described below:
50 |
51 | ### Init options
52 |
53 | You can customize behaviour of the plugin by passing an option object on initialization, example:
54 |
55 | ```
56 | $('#photo').dragncrop({
57 | centered: true,
58 | overflow: true
59 | });
60 | ```
61 |
62 | Here's the complete list of available options
63 |
64 | | Options | Description | Default |
65 | | -----------------------|---------------------------------------------------|-------|
66 | | position | set initial position of the image (see [position](#position-object)) | undefined |
67 | | centered | center image in the container on init | false |
68 | | overflow | show image oveflow when dragging | false |
69 | | overlay | show oveflow with a semi-transparent overlay when dragging | false |
70 | | instruction | show text instruction on image | false |
71 | | instructionText | customize instruction text | 'Drag to crop' |
72 | | instructionHideOnHover | hide instruction when hovering over image | true |
73 |
74 | ### Position object
75 |
76 | *drag'n'crop* provides a *position* object describing the coordinates of the image inside the container with the following structure
77 |
78 | ```
79 | {
80 | offset : [x, y],
81 | dimension : [x, y]
82 | }
83 | ```
84 |
85 | * **offset** represents the relative position of the image inside the container and ranges from 0-1. Example `offset: [1,0]` with an image 400x200px and container 200x200: the image is dragged to its right edge.
86 | * **dimension** represents the offset proportional to the image dimensions. Example: `dimension: [0.5, 0]` with an image of 600x200px and container 200x200: image is dragged by 300px (=600x0.5) pixels to the right.
87 |
88 | Because *jquery.drag'n'crop* crops only in one dimension, only `x` or `y` changes for a given image - the second is always expected to be 0.
89 |
90 | If you set the position (either via `position` option on init, or with `move` method), you must provide the coordinates in one of two formats. Example:
91 |
92 | ```
93 | $('#horizontal-image').dragncrop({
94 | position: {offset: [1,0]} // position image on the right
95 | });
96 | ```
97 |
98 | ### Events
99 |
100 | Event listener function are passed to the constructor in the same way as options. They are invoke every time an event is triggered. Example:
101 |
102 | ```
103 | $('#photo').dragncrop({
104 | start: function() {
105 | console.log('Dragging started!!')
106 | }
107 | });
108 | ```
109 |
110 | Here's the complete list of events emitted by the plugin:
111 |
112 | | Event | Description | Arguments |
113 | | -----------------------|---------------------------------------------------------|-----------------|
114 | | start | when dragging of the image started | event, position |
115 | | drag | when dragging occures | event, position |
116 | | stop | when dragging finished (user released the mouse button) | event, position |
117 |
118 |
119 | ### Instance methods
120 |
121 | Instance methods are invoked by calling the plugin function with the method name provided as a first argument followed by other arguments:
122 |
123 | ```
124 | $("#photo").dragncrop('move', {offset:{[0,0.2]});
125 | ```
126 |
127 | Available methods for drag'n'crop plugin:
128 |
129 | | Method Name | Description | Arguments | Returns |
130 | | ----------------|---------------------------------------------------------|-----------|-----------
131 | | move | Move image programatically to the given position | position | undefined
132 | | getPosition | retrieves the current position of the image | none | position
133 | | destroy | Destroy and cleanup the plugin. | none | undefined
134 |
135 |
136 |
137 | Examples
138 | -------------
139 | See [http://lukaszfiszer.github.io/drag-n-crop/examples.html](http://lukaszfiszer.github.io/drag-n-crop/examples.html)
140 |
141 |
142 | Release History
143 | ---------------
144 | 0.1.1 - fixing manifest files
145 | 0.1.0 - initial release
146 |
147 |
--------------------------------------------------------------------------------
/jquery.drag-n-crop.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jquery.drag-n-crop
3 | * https://github.com/lukaszfiszer/drag-n-crop
4 | *
5 | * Copyright (c) 2013 Lukasz Fiszer
6 | * Licensed under the MIT license.
7 | */
8 |
9 | ;(function ( $, window, document, undefined ) {
10 |
11 | $.widget( "lukaszfiszer.dragncrop" , {
12 |
13 | classes: {
14 | // Basic classes
15 | container: 'dragncrop',
16 | containerActive: 'dragncrop-dragging',
17 | containment: 'dragncrop-containment',
18 | horizontal: 'dragncrop-horizontal',
19 | vertical: 'dragncrop-vertical',
20 |
21 | // Options' classes
22 | overflow: 'dragncrop-overflow',
23 | overlay: 'dragncrop-overlay',
24 | instruction: 'dragncrop-instruction',
25 | instructionHide: 'dragncrop-instruction-autohide',
26 | instructionText: 'dragncrop-instruction-text'
27 | },
28 |
29 | options: {
30 | // Initial position
31 | position: {},
32 | centered: false,
33 |
34 | // Simple overflow:
35 | overflow: false,
36 |
37 | // Overflaid overflow
38 | overlay: false,
39 |
40 | // Drag instruction
41 | instruction: false,
42 | instructionText: 'Drag to crop',
43 | instructionHideOnHover: true,
44 | },
45 |
46 | move: function ( position ) {
47 |
48 | var left, top, x, y;
49 |
50 | if( !position ){
51 | throw new Error('position object must be provided');
52 | }
53 |
54 | if (position.offset === undefined && position.dimension === undefined ) {
55 | throw new Error('position object must contain "left" or "top" props');
56 | }
57 |
58 | if( this.axis === 'x' && position.offset ){
59 | left = -position.offset[0] * this.offsetX;
60 | this.element.css('left', left);
61 | this.element.css('top', 0);
62 | } else
63 | if( this.axis === 'x' && position.dimension ){
64 | left = -position.dimension[0] * this.width;
65 | this.element.css('left', left);
66 | this.element.css('top', 0);
67 | } else
68 |
69 | if( this.axis === 'y' && position.offset ){
70 | top = -position.offset[1] * this.offsetY;
71 | this.element.css('left', 0);
72 | this.element.css('top', top);
73 | } else
74 | if( this.axis === 'y' && position.dimension ){
75 | top = -position.dimension[1] * this.height;
76 | this.element.css('left', 0);
77 | this.element.css('top', top);
78 | }
79 |
80 | this._setPosition( { left: left, top: top });
81 |
82 | },
83 |
84 | _create: function () {
85 |
86 | this.container = $(this.element.parent());
87 | this.container.addClass(this.classes.container);
88 |
89 | if( this.options.overflow || this.options.overlay){
90 | $(this.container).addClass(this.classes.overflow);
91 | }
92 |
93 | var dfd = this.element.imagesLoaded();
94 | var self = this;
95 |
96 | dfd.done(function(){
97 | if(self._setAxis.call(self)){
98 | self._getDimensions.call(self);
99 | self._makeDraggable.call(self);
100 | if (self.options.loaded) {
101 | self.options.loaded();
102 | }
103 | }
104 | } );
105 |
106 | },
107 |
108 | _destroy: function() {
109 | this.draggable && this.draggable.draggable('destroy');
110 | this.container.find('.' + this.classes.containment + ',' +
111 | '.' + this.classes.overlay + ',' +
112 | '.' + this.classes.instruction).remove();
113 | this.element.removeClass(this.classes.horizontal)
114 | .removeClass(this.classes.vertical);
115 | },
116 |
117 | getPosition: function() {
118 | return {
119 | offset : [
120 | ( -this.position.left / this.offsetX) || 0,
121 | ( -this.position.top / this.offsetY) || 0
122 | ],
123 | dimension : [
124 | ( -this.position.left / this.width) || 0,
125 | ( -this.position.top / this.height) || 0
126 | ]
127 | };
128 |
129 | },
130 |
131 | _getDimensions: function() {
132 |
133 | this.width = this.element.width();
134 | this.height = this.element.height();
135 |
136 | this.containerWidth = this.container.width();
137 | this.containerHeight = this.container.height();
138 |
139 | this.offsetX = this.width - this.containerWidth;
140 | this.offsetY = this.height - this.containerHeight;
141 |
142 | },
143 |
144 | _setAxis: function() {
145 |
146 | this.photoRatio = this.element.width() / this.element.height();
147 | this.containerRatio = this.container.width() / this.container.height();
148 |
149 | if (this.photoRatio > this.containerRatio) {
150 |
151 | this.axis = 'x';
152 | $(this.element).addClass(this.classes.horizontal);
153 | return true;
154 |
155 | } else if (this.photoRatio < this.containerRatio) {
156 |
157 | this.axis = 'y';
158 | $(this.element).addClass(this.classes.vertical);
159 | return true;
160 |
161 | }else{
162 |
163 | return false;
164 |
165 | }
166 |
167 | },
168 |
169 | _setPosition: function( obj ) {
170 | this.position = obj;
171 | },
172 |
173 | _makeDraggable : function () {
174 |
175 | var axis = this.axis;
176 | var position = this.options.position;
177 | var containement = this._insertContainment();
178 |
179 | var draggable = this.draggable = this.element.draggable({
180 | axis: axis,
181 | containment: containement
182 | });
183 |
184 | this._on({
185 | dragstart: function (event, ui) {
186 | this._dragStart( event , ui );
187 | this.container.addClass( this.classes.containerActive );
188 | },
189 | drag: function( event, ui ){
190 | this._dragging( event , ui );
191 | if (this.options.overlay) {
192 | this._adaptOverlay( ui );
193 | }
194 | },
195 | dragstop: function( event, ui ){
196 | this._dragStop( event , ui );
197 | this.container.removeClass( this.classes.containerActive );
198 | }
199 | });
200 |
201 | if(this.options.overlay){
202 | this._insertOverlay();
203 | }
204 |
205 | if(this.options.instruction){
206 | this._insertInstruction();
207 | }
208 |
209 | if(this.options.centered){
210 | position = { offset : [0.5, 0.5] };
211 | }
212 |
213 | if( position && ( position.offset || position.coordinates)) {
214 | this.move(position);
215 | }else{
216 | this._setPosition({ left: 0, top: 0 });
217 | }
218 |
219 | },
220 |
221 | _dragStart: function( event, ui ) {
222 | this._setPosition(ui.position);
223 | this._trigger('start', event, this.getPosition() );
224 | },
225 |
226 | _dragging: function( event, ui ) {
227 | this._setPosition(ui.position);
228 | this._trigger('drag', event, this.getPosition() );
229 | },
230 |
231 | _dragStop: function( event, ui ) {
232 | this._setPosition(ui.position);
233 | this._trigger('stop', event, this.getPosition() );
234 | },
235 |
236 |
237 | _insertOverlay: function(){
238 |
239 | var overlay = $('').addClass(this.classes.overlay);
240 | this.overlay = overlay.insertBefore(this.element);
241 | return this.overlay;
242 |
243 | },
244 |
245 | _adaptOverlay: function( ui ) {
246 |
247 | if ( this.axis === 'x' ) {
248 |
249 | this.overlay.css('left', ui.position.left)
250 | .css('border-left-width', -ui.position.left)
251 | .css('right', -ui.position.left - this.offsetX)
252 | .css('border-right-width', ui.position.left + this.offsetX);
253 |
254 | } else if ( this.axis === 'y' ){
255 |
256 | this.overlay.css('top', ui.position.top)
257 | .css('border-top-width', -ui.position.top)
258 | .css('bottom', -ui.position.top - this.offsetY )
259 | .css('border-bottom-width', ui.position.top + this.offsetY );
260 | }
261 |
262 | },
263 |
264 | _insertContainment: function() {
265 |
266 | var top = - this.offsetY;
267 | var bottom = - this.offsetY;
268 | var left = - this.offsetX;
269 | var right = - this.offsetX;
270 |
271 | var containment = $('
').addClass(this.classes.containment)
272 | .css('top',top).css('bottom', bottom)
273 | .css('left',left).css('right',right)
274 | .css('position','absolute');
275 |
276 | this.containment = containment.insertBefore(this.element);
277 | return this.containment;
278 |
279 | },
280 |
281 | _insertInstruction: function() {
282 | this.instruction = $('
').addClass(this.classes.instruction);
283 | if(this.options.instructionHideOnHover){
284 | this.instruction.addClass(this.classes.instructionHide);
285 | }
286 | this.instruction.append(
287 | $('
').text(this.options.instructionText)
288 | .addClass(this.classes.instructionText)
289 | );
290 | this.instruction.insertAfter(this.element);
291 | return this.instruction;
292 | },
293 |
294 | });
295 |
296 | })( jQuery, window, document );
297 |
--------------------------------------------------------------------------------
/jquery.drag-n-crop.amd.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jquery.drag-n-crop
3 | * https://github.com/lukaszfiszer/drag-n-crop
4 | *
5 | * Copyright (c) 2013 Lukasz Fiszer
6 | * Licensed under the MIT license.
7 | */
8 |
9 | define(['jquery',
10 | 'jqueryui/draggable',
11 | 'jqueryui/core',
12 | 'jqueryui/mouse',
13 | 'jqueryui/widget'
14 | ], function ($) {
15 |
16 | $.widget( "lukaszfiszer.dragncrop" , {
17 |
18 | classes: {
19 | // Basic classes
20 | container: 'dragncrop',
21 | containerActive: 'dragncrop-dragging',
22 | containment: 'dragncrop-containment',
23 | horizontal: 'dragncrop-horizontal',
24 | vertical: 'dragncrop-vertical',
25 |
26 | // Options' classes
27 | overflow: 'dragncrop-overflow',
28 | overlay: 'dragncrop-overlay',
29 | instruction: 'dragncrop-instruction',
30 | instructionHide: 'dragncrop-instruction-autohide',
31 | instructionText: 'dragncrop-instruction-text'
32 | },
33 |
34 | options: {
35 | // Initial position
36 | position: {},
37 | centered: false,
38 |
39 | // Simple overflow:
40 | overflow: false,
41 |
42 | // Overflaid overflow
43 | overlay: false,
44 |
45 | // Drag instruction
46 | instruction: false,
47 | instructionText: 'Drag to crop',
48 | instructionHideOnHover: true,
49 | },
50 |
51 | move: function ( position ) {
52 |
53 | var left, top, x, y;
54 |
55 | if( !position ){
56 | throw new Error('position object must be provided');
57 | }
58 |
59 | if (position.offset === undefined && position.dimension === undefined ) {
60 | throw new Error('position object must contain "left" or "top" props');
61 | }
62 |
63 | if( this.axis === 'x' && position.offset ){
64 | left = -position.offset[0] * this.offsetX;
65 | this.element.css('left', left);
66 | this.element.css('top', 0);
67 | } else
68 | if( this.axis === 'x' && position.dimension ){
69 | left = -position.dimension[0] * this.width;
70 | this.element.css('left', left);
71 | this.element.css('top', 0);
72 | } else
73 |
74 | if( this.axis === 'y' && position.offset ){
75 | top = -position.offset[1] * this.offsetY;
76 | this.element.css('left', 0);
77 | this.element.css('top', top);
78 | } else
79 | if( this.axis === 'y' && position.dimension ){
80 | top = -position.dimension[1] * this.height;
81 | this.element.css('left', 0);
82 | this.element.css('top', top);
83 | }
84 |
85 | this._setPosition( { left: left, top: top });
86 |
87 | },
88 |
89 | _create: function () {
90 |
91 | this.container = $(this.element.parent());
92 | this.container.addClass(this.classes.container);
93 |
94 | if( this.options.overflow || this.options.overlay){
95 | $(this.container).addClass(this.classes.overflow);
96 | }
97 |
98 | var dfd = this.element.imagesLoaded();
99 | var self = this;
100 |
101 | dfd.done(function(){
102 | if(self._setAxis.call(self)){
103 | self._getDimensions.call(self);
104 | self._makeDraggable.call(self);
105 | if (self.options.loaded) {
106 | self.options.loaded();
107 | }
108 | }
109 | });
110 |
111 | },
112 |
113 | _destroy: function() {
114 | this.draggable.draggable('destroy');
115 | this.container.find('.' + this.classes.containment + ',' +
116 | '.' + this.classes.overlay + ',' +
117 | '.' + this.classes.instruction).remove();
118 | this.element.removeClass(this.classes.horizontal)
119 | .removeClass(this.classes.vertical);
120 | },
121 |
122 | getPosition: function() {
123 | return {
124 | offset : [
125 | ( -this.position.left / this.offsetX) || 0,
126 | ( -this.position.top / this.offsetY) || 0
127 | ],
128 | dimension : [
129 | ( -this.position.left / this.width) || 0,
130 | ( -this.position.top / this.height) || 0
131 | ]
132 | };
133 |
134 | },
135 |
136 | _getDimensions: function() {
137 |
138 | this.width = this.element.width();
139 | this.height = this.element.height();
140 |
141 | this.containerWidth = this.container.width();
142 | this.containerHeight = this.container.height();
143 |
144 | this.offsetX = this.width - this.containerWidth;
145 | this.offsetY = this.height - this.containerHeight;
146 |
147 | },
148 |
149 | _setAxis: function() {
150 |
151 | this.photoRatio = this.element.width() / this.element.height();
152 | this.containerRatio = this.container.width() / this.container.height();
153 |
154 | if (this.photoRatio > this.containerRatio) {
155 |
156 | this.axis = 'x';
157 | $(this.element).addClass(this.classes.horizontal);
158 | return true;
159 |
160 | } else if (this.photoRatio < this.containerRatio) {
161 |
162 | this.axis = 'y';
163 | $(this.element).addClass(this.classes.vertical);
164 | return true;
165 |
166 | }else{
167 |
168 | return false;
169 |
170 | }
171 |
172 | },
173 |
174 | _setPosition: function( obj ) {
175 | this.position = obj;
176 | },
177 |
178 | _makeDraggable : function () {
179 |
180 | var axis = this.axis;
181 | var position = this.options.position;
182 | var containement = this._insertContainment();
183 |
184 | var draggable = this.draggable = this.element.draggable({
185 | axis: axis,
186 | containment: containement
187 | });
188 |
189 | this._on({
190 | dragstart: function (event, ui) {
191 | this._dragStart( event , ui );
192 | this.container.addClass( this.classes.containerActive );
193 | },
194 | drag: function( event, ui ){
195 | this._dragging( event , ui );
196 | if (this.options.overlay) {
197 | this._adaptOverlay( ui );
198 | }
199 | },
200 | dragstop: function( event, ui ){
201 | this._dragStop( event , ui );
202 | this.container.removeClass( this.classes.containerActive );
203 | }
204 | });
205 |
206 | if(this.options.overlay){
207 | this._insertOverlay();
208 | }
209 |
210 | if(this.options.instruction){
211 | this._insertInstruction();
212 | }
213 |
214 | if(this.options.centered){
215 | position = { offset : [0.5, 0.5] };
216 | }
217 |
218 | if( position && ( position.offset || position.coordinates)) {
219 | this.move(position);
220 | }else{
221 | this._setPosition({ left: 0, top: 0 });
222 | }
223 |
224 | },
225 |
226 | _dragStart: function( event, ui ) {
227 | this._setPosition(ui.position);
228 | this._trigger('start', event, this.getPosition() );
229 | },
230 |
231 | _dragging: function( event, ui ) {
232 | this._setPosition(ui.position);
233 | this._trigger('drag', event, this.getPosition() );
234 | },
235 |
236 | _dragStop: function( event, ui ) {
237 | this._setPosition(ui.position);
238 | this._trigger('stop', event, this.getPosition() );
239 | },
240 |
241 |
242 | _insertOverlay: function(){
243 |
244 | var overlay = $('
').addClass(this.classes.overlay);
245 | this.overlay = overlay.insertBefore(this.element);
246 | return this.overlay;
247 |
248 | },
249 |
250 | _adaptOverlay: function( ui ) {
251 |
252 | if ( this.axis === 'x' ) {
253 |
254 | this.overlay.css('left', ui.position.left)
255 | .css('border-left-width', -ui.position.left)
256 | .css('right', -ui.position.left - this.offsetX)
257 | .css('border-right-width', ui.position.left + this.offsetX);
258 |
259 | } else if ( this.axis === 'y' ){
260 |
261 | this.overlay.css('top', ui.position.top)
262 | .css('border-top-width', -ui.position.top)
263 | .css('bottom', -ui.position.top - this.offsetY )
264 | .css('border-bottom-width', ui.position.top + this.offsetY );
265 | }
266 |
267 | },
268 |
269 | _insertContainment: function() {
270 |
271 | var top = - this.offsetY;
272 | var bottom = - this.offsetY;
273 | var left = - this.offsetX;
274 | var right = - this.offsetX;
275 |
276 | var containment = $('
').addClass(this.classes.containment)
277 | .css('top',top).css('bottom', bottom)
278 | .css('left',left).css('right',right)
279 | .css('position','absolute');
280 |
281 | this.containment = containment.insertBefore(this.element);
282 | return this.containment;
283 |
284 | },
285 |
286 | _insertInstruction: function() {
287 | this.instruction = $('
').addClass(this.classes.instruction);
288 | if(this.options.instructionHideOnHover){
289 | this.instruction.addClass(this.classes.instructionHide);
290 | }
291 | this.instruction.append(
292 | $('').text(this.options.instructionText)
293 | .addClass(this.classes.instructionText)
294 | );
295 | this.instruction.insertAfter(this.element);
296 | return this.instruction;
297 | },
298 |
299 | });
300 |
301 | });
302 |
--------------------------------------------------------------------------------