How can I provide cropping functionality on an image similar to the functionality provided by polyvore.com of which I included screenshots below.


How can I provide cropping functionality on an image similar to the functionality provided by polyvore.com of which I included screenshots below.


This is not a plug-in. In fact, there seems to be no JQuery at all.
The developers seem to have copied unexposed parts of the FB Toolkit (Picture Cropping) and copied parts of Handle-Bars.JS
The animations are done with SVG's
The main JS worker file (30289 lines un-minified) is:
http://akwww.polyvorecdn.com/rsrc/montage-4d3d377ea56d496ebf373692dc2cfe53.js
The parts you are most interested in are:
ImageItem.prototype.startCrop = function() {
LassoDialog.showExpanded(this)
};
The developers grabbed this out of FB's source code and modified it slightly:
function FBPhoto(b) {
this.fbImgUrl = b.imgurl;
this.pid = b.pid;
FBPhoto.superclass.constructor.call(this, b);
var a = 3;
Event.addListener(this.img, "error", function() {
a--;
if (a) {
this.img.setSrc("");
window.setTimeout(Event.wrapper(function() {
this.updateImage()
}, this), 200)
}
}, this);
if (!this.rect.width()) {
getNaturalWidthHeight(b.imgurl, Event.wrapper(function(c, d) {
if (c && d) {
b.w = c;
b.h = d
} else {
b.w = 200;
b.h = 200
}
b.x = 0;
b.y = 0;
this.rect = new Rect(-b.w / 2, -b.h / 2, b.w / 2, b.h / 2);
this.translation = new Point(b.x + b.w / 2, b.y + b.h / 2);
Event.trigger(this, "change");
Event.trigger(this, "sized", this)
}, this))
}
}
And then Extended the method:
extend(FBPhoto, ImageItem);
FBPhoto.mapImgUrl = function(a, b) {
var d = UI.sizeMap[b].dim;
var c;
if (d < 100) {
c = "t"
} else {
if (d < 150) {
c = "s"
} else {
}
}
if (c) {
a = a.replace(/_n\.jpg/, "_" + c + ".jpg").replace(/\/n([^\/]*)\.jpg$/, "/" + c + "$1.jpg")
}
return a
};
FBPhoto.prototype.startCrop = function() {
LassoDialog.showSimple(imgUrl, this, {header: loc("Crop your friend's face by drawing a path around it..."),onSuccess: Event.wrapper(function(b, a) {
this.mask_spec = b || [];
this.updateImage();
Event.trigger(this, "updateactions", this);
this.setDimensions(a);
Event.trigger(this, "change")
}, this)})
};
Functionality that I was looking for:
Sadly (and somewhat suprisingly) I was not able to find a library that did this for you, but the closest I got was the netplayer crop library. Out of the previous 4 points it's only able to do point 1 and 3. In other words, it's necessary to spin your own solution as point 2 is quite a big requirement I suppose. The next two sections will do some of the work for you, but it's definitely not the same as a ready made plugin.
The best library I was able to find for drawing the polygon was PolyK.js . Sadly for us it's far far from perfect, however it does simplify the process enormously.
Below you can find a basic editable polygon, taken from the PolyK.js documentation.
var stage, s, dragged;
var poly = [93, 195, 129, 92, 280, 81, 402, 134, 477, 70, 619, 61, 759, 97, 758, 247, 662, 347, 665, 230, 721, 140, 607, 117, 472, 171, 580, 178, 603, 257, 605, 377, 690, 404, 787, 328, 786, 480, 617, 510, 611, 439, 544, 400, 529, 291, 509, 218, 400, 358, 489, 402, 425, 479, 268, 464, 341, 338, 393, 427, 373, 284, 429, 197, 301, 150, 296, 245, 252, 384, 118, 360, 190, 272, 244, 165, 81, 259, 40, 216];
var dots = [];
function Go() {
stage = new Stage("c");
s = new Sprite();
stage.addChild(s);
for (var i = 0; i < poly.length / 2; i++) {
var dot = new Dot();
dot.x = poly[2 * i];
dot.y = poly[2 * i + 1];
dots.push(dot);
dot.addEventListener(MouseEvent.MOUSE_DOWN, onMD);
stage.addChild(dot);
}
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMM);
stage.addEventListener(MouseEvent.MOUSE_UP, onMU);
redraw();
}
function onMD(e) {
dragged = e.target;
}
function onMU(e) {
dragged = null;
}
function onMM(e) {
if (dragged != null) {
dragged.x = stage.mouseX;
dragged.y = stage.mouseY;
var i = dots.indexOf(dragged);
poly[2 * i] = stage.mouseX;
poly[2 * i + 1] = stage.mouseY;
redraw();
}
}
function redraw() {
s.graphics.clear();
fillPoly(poly, s, 0x00bbff);
}
function strokePoly(poly, s) {
var n = poly.length >> 1;
s.graphics.lineStyle(6, 0xff0000);
s.graphics.moveTo(poly[0], poly[1]);
for (var i = 1; i < n; i++) s.graphics.lineTo(poly[2 * i], poly[2 * i + 1]);
s.graphics.lineTo(poly[0], poly[1]);
}
function fillPoly(poly, s, color) {
var tgs = PolyK.Triangulate(poly);
s.graphics.beginFill(color);
s.graphics.drawTriangles(poly, tgs);
}
function drawTgs(vrt, ind, s) {
var n = ind.length / 3;
s.graphics.lineStyle(1, 0x000000);
for (var i = 0; i < n; i++) {
var i0 = ind[3 * i];
var i1 = ind[3 * i + 1];
var i2 = ind[3 * i + 2]
s.graphics.moveTo(vrt[2 * i0], vrt[2 * i0 + 1]);
s.graphics.lineTo(vrt[2 * i1], vrt[2 * i1 + 1]);
s.graphics.lineTo(vrt[2 * i2], vrt[2 * i2 + 1]);
s.graphics.lineTo(vrt[2 * i0], vrt[2 * i0 + 1]);
}
}
function Dot() {
Sprite.apply(this); // inherits from Sprite
this.graphics.beginFill(0x000000, 0.15);
this.graphics.drawCircle(0, 0, 13);
this.graphics.beginFill(0xffffff, 1.0);
this.graphics.drawCircle(0, 0, 6);
this.buttonMode = true;
}
Dot.prototype = new Sprite();
document.addEventListener("DOMContentLoaded", Go);
<script src="http://lib.ivank.net/ivank.js"></script>
<script src="http://polyk.ivank.net/polyk.js"></script>
<canvas id="c"></canvas>
To this we need to add the functionality to add points and remove points. I implemented this that any click anywhere will add the point near the closest segment of the polygon and removing point is done by clicking on them without dragging. Additionally I added in an image under the <canvas>, just to give an idea of the result (open the snippet in full page mode to get a good idea).
var stage, s, dragged;
var poly = [77, 222, 81, 187, 112, 195, 143, 183, 150, 222, 133, 262, 95, 258];
var dots = [];
var originalPosition = {
x: 0,
y: 0
};
var Go = function() {
stage = new Stage("c");
s = new Sprite();
stage.addChild(s);
stage.addEventListener(MouseEvent.MOUSE_UP, removeOrAddDots);
for (var i = 0; i < poly.length / 2; i++) {
var dot = new Dot();
dot.x = poly[2 * i];
dot.y = poly[2 * i + 1];
dots.push(dot);
dot.addEventListener(MouseEvent.MOUSE_DOWN, clickDot);
stage.addChild(dot);
}
stage.addEventListener(MouseEvent.MOUSE_MOVE, dragDot);
stage.addEventListener(MouseEvent.MOUSE_UP, releaseDot);
redraw();
}
var clickDot = function(e) {
dragged = e.target;
originalPosition.x = dragged.x;
originalPosition.y = dragged.y;
}
var releaseDot = function(e) {
if (dragged && Math.abs(originalPosition.x - dragged.x) < 2 && Math.abs(originalPosition.y - dragged.y) < 2) {
stage.removeChild(dragged);
var index = dots.indexOf(dragged);
poly.splice(index * 2, 2);
dots.splice(index, 1);
redraw();
}
dragged = null;
}
var dragDot = function(e) {
if (dragged != null) {
dragged.x = stage.mouseX;
dragged.y = stage.mouseY;
var i = dots.indexOf(dragged);
poly[2 * i] = stage.mouseX;
poly[2 * i + 1] = stage.mouseY;
redraw();
}
}
var removeOrAddDots = function(ev) {
if (dragged) {
//ignore, because we're dragging a point
} else {
var isc = PolyK.ClosestEdge(poly, stage.mouseX, stage.mouseY);
var dot = new Dot();
dot.x = stage.mouseX;
dot.y = stage.mouseY;
dots.splice((isc.edge + 1), 0, dot);
poly.splice((isc.edge + 1) * 2, 0, dot.x);
poly.splice((isc.edge + 1) * 2 + 1, 0, dot.y);
stage.addChild(dot);
dot.addEventListener(MouseEvent.MOUSE_DOWN, clickDot);
redraw();
}
}
var redraw = function() {
s.graphics.clear();
fillPoly(poly, s, 0x00bbff);
}
var strokePoly = function(poly, s) {
var n = poly.length >> 1;
s.graphics.lineStyle(6, 0xff0000);
s.graphics.moveTo(poly[0], poly[1]);
for (var i = 1; i < n; i++) s.graphics.lineTo(poly[2 * i], poly[2 * i + 1]);
s.graphics.lineTo(poly[0], poly[1]);
}
var fillPoly = function(poly, s, color) {
var tgs = PolyK.Triangulate(poly);
s.graphics.beginFill(color, .7);
s.graphics.drawTriangles(poly, tgs);
}
function Dot() {
Sprite.apply(this); // inherits from Sprite
this.graphics.beginFill(0x000000, 0.15);
this.graphics.drawCircle(0, 0, 13);
this.graphics.beginFill(0xffffff, 1.0);
this.graphics.drawCircle(0, 0, 6);
this.buttonMode = true;
}
Dot.prototype = new Sprite();
document.addEventListener("DOMContentLoaded", Go);
canvas {
position: absolute;
left: 0px;
top: 0px;
}
<script src="http://lib.ivank.net/ivank.js"></script>
<script src="http://polyk.ivank.net/polyk.js"></script>
<img src="https://i.stack.imgur.com/eNvlM.png">
<canvas id="c"></canvas>
Now that we have a polygon (the array of points in poly), we can quite simply crop away any area outside of the polygon using whatever server side language you're already using, here is some documentation on how to do it with PHP for example (or it can be done on the client, as done inthe netplayer crop library I mentioned earlier). Either way, that should be comparatively simple. Or you can skip it entirely and use a library like polyClip.js which does it on the run whenever you load the image (only advisable if it's JPGs we're talking about).
React.js with React extension might be what you're after. Judging from your photo this can solve what you want.
for drag event (dragging part) http://jsfiddle.net/mattpodwysocki/pfCqq/
for mouse event (cropping part) http://jsfiddle.net/mattpodwysocki/gJtjx/
<script src="http://cdnjs.cloudflare.com/ajax/libs/rxjs/2.1.18/rx.js"></script>
Once you include the script you can hack into the code...
React.js: A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES https://facebook.github.io/react/
React extensions: Let you work on events easily such as click events, keyboard events, dragdrop events... etc https://github.com/Reactive-Extensions/RxJS/tree/master/examples