-2

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

enter image description here

enter image description here

David Mulder
  • 26,123
  • 9
  • 51
  • 114
supersaiyan
  • 1,670
  • 5
  • 16
  • 36
  • Have you inspected their source code? – Bergi May 15 '15 at 06:07
  • i did but js is minified so not able to find which plugin they are using – supersaiyan May 15 '15 at 06:42
  • Check this: http://www.elevateweb.co.uk/image-zoom/examples – Ivan Sivak May 28 '15 at 13:23
  • I couldn't find an example of what you are looking..Can you provide a particular url? – laaposto May 28 '15 at 13:27
  • @laaposto i added the screenshot.. this kind of croping i am looking for – supersaiyan May 28 '15 at 13:31
  • 1
    This question in its original form should have been closed (it was asking for external resources) long before you could have asked the bounty. As it's a pity to lose 400 reputation over such a thing I have edited the question to make it more or less on topic (though it might still be too broad). – David Mulder May 28 '15 at 16:14
  • @DavidMulder, that edit changed the question entirely. – Dave Alperovich May 28 '15 at 17:18
  • @DaveAlperovich That's what often happens when questions are off topic in their original form. – David Mulder May 28 '15 at 17:22
  • @DavidMulder, you re-edit them to suit your needs and down vote competition? – Dave Alperovich May 28 '15 at 17:23
  • @DaveAlperovich I edit them to make them *on topic* if I am capable of doing that within the intentions of the author. In this case the obvious intention of the author was to recreate the functionality of polyvore, so that's what I edited it to. Asking for plugins or "what code did this site use to achieve this?" are both questions which are explicitly offtopic on StackOverflow. With your reputation you should have been able to see that this question already had 3 close votes, so it's not like you didn't know this when you were answering. – David Mulder May 28 '15 at 17:30
  • @DavidMulder, I agree with your logic. But counter that you have "fixed" the problem with interpretation of what makes it a good question which is NOT what editing is for. Even moderators don't do that. If they don't like the question, they close it and cancel the bounty. You are acting like a self-appointed moderator. – Dave Alperovich May 28 '15 at 17:34
  • 1
    @DaveAlperovich The two main criteria for editing is that 1) you improve the post and 2) you stay within the intent of the author. I believe I sincerely did both. According to a lot of posts on meta editing is not only for fixing small mistakes. And btw, I honestly think nobody would have been glad to see 300 reputation go up in smoke. – David Mulder May 28 '15 at 17:37
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/79045/discussion-between-david-mulder-and-dave-alperovich). – David Mulder May 28 '15 at 17:39

4 Answers4

4
  • 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)})
};
Dave Alperovich
  • 32,320
  • 8
  • 79
  • 101
  • 1
    @supersaiyan, Implementing this into a solution could be a Month of work. That's not reasonable. your original Q was "crop functionalities or just wanted to know its a plugin or a custom creation." I believe I answered that fairly well. Has the requirement changed? – Dave Alperovich May 28 '15 at 17:19
  • how would i know if it works or not ?. atleast provide the working snippet.. i wanted to know how it is working – supersaiyan May 28 '15 at 17:24
  • 1
    @supersaiyan, original question was "crop functionalities ... just wanted to know its a plugin or a custom creation." I looked into the source code and found what seems like conclusive proof that it was a custom creation and explained how. Are you saying that reqirement has changed into implementing the functionality? – Dave Alperovich May 28 '15 at 17:31
  • i can understand your point but i already dig into this but not able to achieve it somehow..so here my need is to get the plugin and how its working basically.. the flow of code .. – supersaiyan May 28 '15 at 17:36
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/79046/discussion-between-supersaiyan-and-dave-alperovich). – supersaiyan May 28 '15 at 17:39
2

Determining the necessary features

Functionality that I was looking for:

  1. It should allow overlaying an image
  2. It should allow editing of the individual points
  3. And it should be possible to generate the cropped image
  4. And ideally it should allow gray out the non-cropped area of the image

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.


Drawing the editable polygon

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>

Cropping

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).

Community
  • 1
  • 1
David Mulder
  • 26,123
  • 9
  • 51
  • 114
0

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

Shih-Min Lee
  • 9,350
  • 7
  • 37
  • 67
  • did u check screenshot what kind of cropping i am looking for ?? – supersaiyan May 28 '15 at 13:36
  • It says click to draw a path. Which means you need to create mousedown, mouseup and mousemove events to track what you want to crop. – Shih-Min Lee May 28 '15 at 13:38
  • please look at the nodes of images. ( which i added ) . I need quite a few nodes on my product – supersaiyan May 28 '15 at 13:39
  • could you check your network and see if this guy is doing cropping on the frontend or send the points back to server to calculate and rerender a cropped image? It is also one possibility – Shih-Min Lee May 28 '15 at 13:50
  • i want to know how we can do cropping ( just like the yellow image i attached ) i am not worrying about rest of the stuff..that is manageable... – supersaiyan May 28 '15 at 17:27
-1

Not 100% on this but by the looks of it, it's using FabricJS

Dan Moldovan
  • 3,576
  • 2
  • 13
  • 24