【问题标题】:Kinect JS cropping rectangleKinect JS 裁剪矩形
【发布时间】:2013-09-13 20:25:21
【问题描述】:

我已经在使用名为 jCropjQuery 插件,但最近我发现了 KinectJs,它确实为我解决了很多问题。然后我偶然发现了这个例子:

http://www.html5canvastutorials.com/labs/html5-canvas-drag-and-drop-resize-and-invert-images/

我决定根据KinectJs 及以上示例编写自己的裁剪矩形。

    function update(activeAnchor) {
var group = activeAnchor.getParent();

var topLeft = group.get('.topLeft')[0];
var topRight = group.get('.topRight')[0];
var bottomRight = group.get('.bottomRight')[0];
var bottomLeft = group.get('.bottomLeft')[0];
var cropper = group.get('.cropper')[0];

var leftMask = group.getParent().get('.leftMask')[0];
var rightMask = group.getParent().get('.rightMask')[0];
var topMask = group.getParent().get('.topMask')[0];
var bottomMask = group.getParent().get('.bottomMask')[0];

var anchorX = activeAnchor.getX();
var anchorY = activeAnchor.getY();

// update anchor positions
switch (activeAnchor.getName()) {
    case 'topLeft':
        topRight.setY(anchorY);
        bottomLeft.setX(anchorX);
        updateLeftMaskWidth(leftMask,activeAnchor);
        updateTopMaskHeight(topMask,cropper,activeAnchor);
        break;
    case 'topRight':
        topLeft.setY(anchorY);
        bottomRight.setX(anchorX);
        updateRightMaskWidthAndPos(rightMask,activeAnchor);
        updateTopMaskHeight(topMask,cropper,activeAnchor);
        break;
    case 'bottomRight':
        bottomLeft.setY(anchorY);
        topRight.setX(anchorX);
        updateRightMaskWidthAndPos(rightMask,activeAnchor);
        updateBottomMaskHeightAndPos(bottomMask,cropper,activeAnchor);
        break;
    case 'bottomLeft':
        bottomRight.setY(anchorY);
        topLeft.setX(anchorX);
        updateLeftMaskWidth(leftMask,activeAnchor);
        updateBottomMaskHeightAndPos(bottomMask,cropper,activeAnchor);
        break;
    }

    cropper.setPosition(topLeft.getPosition().x,topLeft.getPosition().y);

    var width = topRight.getX() - topLeft.getX();
    var height = bottomLeft.getY() - topLeft.getY();
    if(width && height) {
        cropper.setSize(width, height);
    }
}

function updateLeftMaskWidth(mask,leftAnchor) {
    mask.setWidth(leftAnchor.getAbsolutePosition().x - 100);
}

function updateRightMaskWidthAndPos(mask,rightAnchor) {
    mask.setAbsolutePosition(rightAnchor.getAbsolutePosition().x,mask.getAbsolutePosition().y);
    mask.setWidth(213 - (rightAnchor.getAbsolutePosition().x - 100));
}

function updateTopMaskHeight(mask,cropper,topAnchor) {
    mask.setAbsolutePosition(topAnchor.getAbsolutePosition().x,mask.getAbsolutePosition().y);
    mask.setHeight(topAnchor.getAbsolutePosition().y - 110);
    mask.setWidth(cropper.getWidth());
}

function updateBottomMaskHeightAndPos(mask,cropper,bottomAnchor) {
    mask.setAbsolutePosition(bottomAnchor.getAbsolutePosition().x, bottomAnchor.getAbsolutePosition().y);
    mask.setHeight(236 - (bottomAnchor.getAbsolutePosition().y - 110));
    mask.setWidth(cropper.getWidth());
}

function addAnchor(group, x, y, name) {
var stage = group.getStage();
var layer = group.getLayer();

var anchor = new Kinetic.Circle({
    x: x,
    y: y,
    stroke: '#666',
    fill: '#ddd',
    strokeWidth: 1,
    radius: 5,
    name: name,
    draggable: true,
    dragBoundFunc: function(pos) {
        var newX = pos.x;
        var newY = pos.y;
        var image = this.getParent().getParent().get('.image')[0];
        var cropper = this.getParent();

        // Bound horizontally
        if(newX < 100) {
            newX = 100;
        }
        else if(newX > image.getWidth() + 100 - cropper.getWidth()) {
            newX = image.getWidth() + 100 - cropper.getWidth();
        }

        if(newY < 110) {
            newY = 110;
        }
        else if(newY > image.getHeight() + 110 - cropper.getHeight()) {
            newY = image.getHeight() + 110 - cropper.getHeight();
        }

        return {
            x: newX,
            y: newY
        }
    }
});

anchor.on('dragmove', function() {
    update(this);
    layer.draw();
});
// add hover styling
anchor.on('mouseover', function() {
    var layer = this.getLayer();
    document.body.style.cursor = 'pointer';
    this.setStrokeWidth(2);
    layer.draw();
});
anchor.on('mouseout', function() {
    var layer = this.getLayer();
    document.body.style.cursor = 'default';
    this.setStrokeWidth(2);
    layer.draw();
});

group.add(anchor);
}

function initStage(img) {
    var stage = new Kinetic.Stage({
        container: 'container',
        width: 578,
        height: 400
    });
    var imageGroup = new Kinetic.Group({
        x: 100,
        y: 110
    });
var leftMaskGroup = new Kinetic.Group({
    x: 100,
    y: 110
});

var rightMaskGroup = new Kinetic.Group({
    x: 270,
    y: 110
});

var topMaskGroup = new Kinetic.Group({
    x: 169.9,
    y: 110
});

var bottomMaskGroup = new Kinetic.Group({
    x: 169.9,
    y: 150+138
});

var cropperGroup = new Kinetic.Group({
    x: 170,
    y: 150,
    draggable: true,
    dragBoundFunc: function(pos) {
        var newX = pos.x;
        var newY = pos.y;
        var image = this.getParent().get('.image')[0];
        var cropper = this.get('.cropper')[0];

        // Bound horizontally
        if(newX < 100) {
            newX = 100;
        }
        else if(newX > image.getWidth() + 100 - cropper.getWidth()) {
            newX = image.getWidth() + 100 - cropper.getWidth();
        }

        // Bound vertically
        if(newY < 110) {
            newY = 110;
        }
        else if(newY > image.getHeight() + 110 - cropper.getHeight()) {
            newY = image.getHeight() + 110 - cropper.getHeight();
        }

        return {
            x: newX,
            y: newY
        }
    }
});
var layer = new Kinetic.Layer();

/*
 * go ahead and add the groups
 * to the layer and the layer to the
 * stage so that the groups have knowledge
 * of its layer and stage
 */

layer.add(imageGroup);
layer.add(leftMaskGroup);
layer.add(rightMaskGroup);
layer.add(topMaskGroup);
layer.add(bottomMaskGroup);
layer.add(cropperGroup);
stage.add(layer);

// cropping rectangle
var cropperRect = new Kinetic.Rect({
    x: 0,
    y: 0,
    width: 100,
    height: 138,
    stroke: 'black',
    name: 'cropper',
    strokeWidth: 1
});

cropperGroup.add(cropperRect);
addAnchor(cropperGroup, 0, 0, 'topLeft');
addAnchor(cropperGroup, 100, 0, 'topRight');
addAnchor(cropperGroup, 100, 138, 'bottomRight');
addAnchor(cropperGroup, 0, 138, 'bottomLeft');

cropperGroup.on('dragstart', function() {
    this.moveToTop();
});

cropperGroup.on('dragmove', function() {
    var layer = this.getLayer();
    var topLeft = this.get('.topLeft')[0];
    var bottomLeft = this.get('.bottomLeft')[0];
    var topRight = this.get('.topRight')[0];
    var leftMask = this.getParent().get('.leftMask')[0];
    var rightMask = this.getParent().get('.rightMask')[0];
    var topMask = this.getParent().get('.topMask')[0];
    var bottomMask = this.getParent().get('.bottomMask')[0];
    updateLeftMaskWidth(leftMask,topLeft);
    updateRightMaskWidthAndPos(rightMask,topRight);
    updateTopMaskHeight(topMask,this.get('.cropper')[0],topLeft);
    updateBottomMaskHeightAndPos(bottomMask,this.get('.cropper')[0],bottomLeft);
    layer.draw();
});

// left mask
var leftMaskRect = new Kinetic.Rect({
    x: 0,
    y: 0,
    width: 70,
    height: 236,
    fill: 'black',
    name: 'leftMask',
    strokeWidth: 0,
    opacity: 0.5
});

leftMaskGroup.add(leftMaskRect);

// right mask
var rightMaskRect = new Kinetic.Rect({
    x: 0,
    y: 0,
    width: 213-170,
    height: 236,
    fill: 'black',
    name: 'rightMask',
    strokeWidth: 0,
    opacity: 0.5
});

rightMaskGroup.add(rightMaskRect);

// top mask
var topMaskRect = new Kinetic.Rect({
    x: 0,
    y: 0,
    width: 100.2,
    height: 150-110,
    fill: 'black',
    name: 'topMask',
    strokeWidth: 0,
    opacity: 0.5
});

topMaskGroup.add(topMaskRect);

// bottom mask
var bottomMaskRect = new Kinetic.Rect({
    x: 0,
    y: 0,
    width: 100.2,
    height: 236-138-(150-110),
    fill: 'black',
    name: 'bottomMask',
    strokeWidth: 0,
    opacity: 0.5
});

bottomMaskGroup.add(bottomMaskRect);

// image
var srcImg = new Kinetic.Image({
    x: 0,
    y: 0,
    image: img,
    name: 'image'
});

imageGroup.add(srcImg);

stage.draw();
}
var img = new Image();
img.onload = function() {
    initStage(this);
}
img.src = 'http://www.html5canvastutorials.com/demos/assets/yoda.jpg';

它几乎可以完美运行。问题是,当您使用圆形锚调整大小时,经过几次尝试(只是给它几张照片),当您尝试拖动整个矩形时,它允许您将其拖出边界!

从我的调试来看,这似乎是库的问题,但如果 sb.在我的代码中发现问题或看到优化方法,请分享您的想法。

我的努力的结果可以在这里看到:

http://jsfiddle.net/wanderer/WLpXF/

【问题讨论】:

  • 这个浏览器是特定的!?或者使用 Chrome/Firefox 就可以了!?

标签: html image canvas crop jcrop


【解决方案1】:

A) 重现错误:

  1. 向上拖动一个顶部锚点(比如说它被拖动了 N 个像素)
  2. 拖动整个裁剪器向上;它可以离开图像边界 N 个像素

其他锚点和方向的行为类似。

B) 解决方案:

将此函数添加到脚本中:

function readjust() {
    var group = this.getParent();

    var topLeft = group.get('.topLeft')[0];
    var topRight = group.get('.topRight')[0];
    var bottomRight = group.get('.bottomRight')[0];
    var bottomLeft = group.get('.bottomLeft')[0];
    var cropper = group.get('.cropper')[0];

    var tlx = cropper.getX(),
        tly = cropper.getY(),
        w = cropper.getWidth(),
        h = cropper.getHeight();

    group.setX(group.getX() + tlx);
    group.setY(group.getY() + tly);

    topLeft.setPosition(0,0);
    topRight.setPosition(w,0);
    bottomLeft.setPosition(0,h);
    bottomRight.setPosition(w,h);
    cropper.setPosition(0,0);
}

以及addAnchor() 中的以下处理程序:

anchor.on('dragend', readjust);

小提琴:http://jsfiddle.net/BMy7b/1/

按照 MarmiK 的建议,readjust() 中的代码也可以包含在 update() 中。这将在每次拖动时运行更多操作,因此可能会更慢(但我不确定;意见?)。小提琴:http://jsfiddle.net/vUPeQ/1/

问题是裁剪器和锚点相对于cropperGroup 移动,但cropperGroup 拖动边界函数没有考虑到它。

玩得开心!

【讨论】:

  • 我认为这也应该与这个 anchor.on('dragmove', function() { update(this); layer.draw(); });
  • 我猜update()readjust()的逻辑可以合并。但是,建议的更改对原始代码的侵入性很小。
  • 我只是在做和你一样的事情,所以我们只需要在原始函数的'dragmove'部分添加你的函数,拖动也是改变画布的原因之一如何。
  • 确实,在var tlx=... 之后将readjust() 的内容添加到update() 的底部并删除所有其他更改可以完成同样的事情。我认为拖动速度有点慢(每次拖动可能需要更多操作?)。
  • 您可能希望防止焦点矩形反转的情况(例如,顶部拖动圆圈可以拖动到底部拖动圆圈下方)当这种情况发生时,焦点矩形仍然可以拖动到外面图片。
【解决方案2】:

一个快速更新:我正在追求这样的东西(看起来不错的裁剪矩形),但是当我使用最新的 KineticJS 5.0.1 尝试这段代码时,它做了一些令人讨厌的事情。您可以在这里自行查看:[http://jsfiddle.net/vUPeQ/2/]:http://jsfiddle.net/vUPeQ/2/

我的猜测是,这是因为某些 api 发生了变化,但我找不到哪一个......

这里有人可以帮忙吗?

非常感谢!!!

【讨论】:

  • 是的,API 已更改。要使其工作,您必须从版本 5.0.0 all multi-dimensional inputs and outputs must be object literals for performance reasons. i.e. shape.setScale(1) will no longer work. You need to use shape.setScale({x: 1, y: 1}); 开始承认这一点。对于我们的示例,这意味着您必须将所有 setPosition(xCoord,yCoord)setAbsolutePosition(xCoord,yCoord) 更改为 setPosition({x:xCoord,y:yCoord})setAbsolutePosition({x:xCoord,y:yCoord})。最后更改的是 setSize(newWidth,newHeight)' which must be changed to setSize({width:newWidth,height:newHeight})`。跨度>
猜你喜欢
  • 2013-10-05
  • 2014-02-03
  • 1970-01-01
  • 1970-01-01
  • 2013-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多