如何创建类似于 polyvore 提供的裁剪功能

How to create a cropping functionality similar to the one provided by polyvore

如何在图像上提供与 polyvore.com 提供的功能类似的裁剪功能,我在下面附上了屏幕截图。

这不是 100%,但从外观上看,它正在使用 FabricJS

React.js 和 React 扩展可能就是您想要的。从您的照片来看,这可以解决您想要的问题。

用于拖动事件(拖动部分) http://jsfiddle.net/mattpodwysocki/pfCqq/

鼠标事件(剪裁部分) http://jsfiddle.net/mattpodwysocki/gJtjx/

<script src="http://cdnjs.cloudflare.com/ajax/libs/rxjs/2.1.18/rx.js"></script>

包含脚本后,您可以破解代码...

React.js:用于构建用户界面的 JAVASCRIPT 库 https://facebook.github.io/react/

React 扩展:让您轻松处理点击事件、键盘事件、拖放事件等事件 https://github.com/Reactive-Extensions/RxJS/tree/master/examples

  • 这不是插件。事实上,似乎根本就没有JQuery

  • 开发者好像复制了FB Toolkit(图片裁剪)未公开的部分,复制了Handle-[=48的部分=]

  • 动画是用 SVG 的

  • 完成的

主要的 JS worker 文件(未缩小的 30289 行)是:

http://akwww.polyvorecdn.com/rsrc/montage-4d3d377ea56d496ebf373692dc2cfe53.js

您最感兴趣的部分是:

ImageItem.prototype.startCrop = function() {
    LassoDialog.showExpanded(this)
};

开发者从FB的源代码中抓取并稍作修改:

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

然后扩展方法:

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 + ".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)})
};

确定必要的特征

我一直在寻找的功能:

  1. 它应该允许叠加图像
  2. 应该允许编辑各个点
  3. 并且应该可以生成裁剪后的图像
  4. 理想情况下,它应该允许将图像的未裁剪区域变灰

遗憾的是(有点令人惊讶)我找不到为您做这件事的图书馆,但我得到的最接近的是 netplayer crop 图书馆。在前面的 4 点中,它只能执行第 1 点和第 3 点。换句话说,有必要旋转你自己的解决方案,因为我认为第 2 点是一个很大的要求。接下来的两节将为您完成一些工作,但它绝对不同于现成的插件。


绘制可编辑多边形

我能找到的用于绘制多边形的最佳库是 PolyK.js。对我们来说遗憾的是,它远非完美,但它确实极大地简化了过程。

您可以在下面找到一个基本的可编辑多边形,摘自 PolyK.js 文档。

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>

为此我们需要添加添加点和删除点的功能。我实现了这一点,任何地方的任何点击都会在多边形的最近段附近添加点,并且通过点击它们而不拖动来删除点。此外,我在 <canvas> 下添加了一张图片,只是为了了解结果(以整页模式打开代码段以获得一个好主意)。

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="http://i.stack.imgur.com/eNvlM.png">
<canvas id="c"></canvas>

裁剪

现在我们有了一个多边形(poly 中的点数组),我们可以使用您已经在使用的任何服务器端语言非常简单地裁剪掉多边形之外的任何区域, 这里 是一些关于如何使用 PHP 来完成它的文档(或者它可以在客户端上完成,就像我之前提到的 netplayer crop 库中所做的那样)。无论哪种方式,这应该是比较简单的。或者你可以完全跳过它并使用像 polyClip.js 这样的库,它会在你加载图像时在 运行 上执行它(仅当它是 JPG 时才可取谈论)。