如何在paperjs中拖动/调整大小和旋转矩形?

时间:2017-01-15 02:15:27

标签: graphics transformation paperjs

我想在paperjs中拖动并调整矩形大小,我还想旋转矩形并调整其大小,同时保持其相对尺寸。

理想情况下,我想通过拖动其中一个角(锚点)来鼠标移动。什么数学或特征有助于在paperjs中做到这一点?

我已经尝试过使用缩放和修改角落,但它并没有像我想要的那样工作。有人能指出我的解决方案吗?

提前致谢。

2 个答案:

答案 0 :(得分:1)

这是一个简单的解决方案,可以帮助您入门。它不能处理旋转,因为我不确定你如何设想UI工作,但是通过修改边界框来调整矩形的大小,你应该可以毫无问题地旋转它。

paperjs sketch

我决定构建自己的用户界面并继续进行,让您的示例更加复杂,尽可能多地解决您的问题,而无需更多信息。这是新草图:

new sketch

用户界面是

  1. 点击矩形以拖动
  2. 移动它
  3. 点击一个角落并拖动以调整其大小
  4. 按住Ctrl键并单击一个角落以旋转它
  5. 点击角落有点棘手,但这是一个留给读者的练习。它们是彩色圆圈,只是为了强调Path的每个分段点所在的位置。

    代码的要点:

    • 使用矩形的边界进行缩放。就纸张而言,Path.Rectangle不是矩形。连接四个分段点的是四条曲线(恰好是直线)。当您需要使用矩形来获取其中心,左上角等时,您需要Rectangle。使用矩形的边界(Path.Rectangle.bounds)缩放可见矩形。该代码用一个额外的浅水矩形图示了边界,使其可见(旋转时最容易看到)。

    • onMouseDown()设置onMouseDrag()的状态,并设置每个州所需的数据,例如,为调整大小保存比例基础。

    • onMouseDrag()实现移动,调整大小和旋转。

    tool.onMouseDrag = function(e) { if (rect.data.state === 'moving') { rect.position = rect.position + e.point - e.lastPoint; adjustRect(rect); } else if (rect.data.state === 'resizing') { // scale by distance from down point var bounds = rect.data.bounds; var scale = e.point.subtract(bounds.center).length / rect.data.scaleBase.length; var tlVec = bounds.topLeft.subtract(bounds.center).multiply(scale); var brVec = bounds.bottomRight.subtract(bounds.center).multiply(scale); var newBounds = new Rectangle(tlVec + bounds.center, brVec + bounds.center);
    rect.bounds = newBounds;
    adjustRect(rect); } else if (rect.data.state === 'rotating') { // rotate by difference of angles, relative to center, of // the last two points. var center = rect.bounds.center; var baseVec = center - e.lastPoint; var nowVec = center - e.point; var angle = nowVec.angle - baseVec.angle; rect.rotate(angle); adjustRect(rect); } }

    • 移动非常简单 - 只需计算事件中当前和最后一个点之间的差异,然后将矩形的位置改变那么多。

    • 调整大小并不明显。策略是根据mousedown点和矩形中心之间的原始距离(scaleBase.length)调整x和y边界。请注意,虽然paper-full.js允许使用运算符(" +"," - "," *"," /&#34 ;)使用点数,我使用原始的subtract()multiply()方法几次 - 我发现用这种方式链接计算是很自然的。

    • 旋转使用非常好的纸张概念,即点也定义了矢量,矢量具有角度。它只是注意到事件lastPointpoint之间相对于矩形中心的角度差异,并按该差异旋转矩形。

    • moveCircles()adjustRect()只是用于更新角圆和浅绿色矩形的簿记功能。

答案 1 :(得分:0)

考虑以下问题。我刚刚根据大量示例完成了弄清楚这一点的过程。

我的目标:

  • 选择项目时使用我自己的边界框
  • 移动、调整大小和旋转(使用对齐旋转 [45 度])所选项目
  • 显示项目的标题/名称

Example Sketch

Paper.js 代码

var hitOptions = {
    segments: true,
    stroke: true,
    fill: true,
    tolerance: 5
};

function drawHex(w, c, n){
    var h = new Path.RegularPolygon(new Point(100, 100), 6, w / 2);
    h.selectedColor = 'transparent';
    
    c = c != undefined ? c : "#e9e9ff";
    n = n != undefined ? n : "Hexayurt";
    h.name = n;
    h.fillColor = c;
    h.data.highlight = new Group({
       children: [makeBounds(h), makeCorners(h), makeTitle(h)],
       strokeColor: '#a2a2ff',
       visible: false
    });
    
    return h;
}

function makeCorners(o, s){
    s = s != undefined ? s : 5;
    var g = new Group();
    var corners = [
        o.bounds.topLeft,
        o.bounds.topRight,
        o.bounds.bottomLeft,
        o.bounds.bottomRight
    ];
    corners.forEach(function(corner, i) {
        var h = new Path.Rectangle({
            center: corner,
            size: s
        });
        g.addChild(h);
    });
    return g;
}

function makeBounds(o){
    return new Path.Rectangle({
        rectangle: o.bounds
    });
}

function makeTitle(o, n, c){
    c = c != undefined ? c : 'black';
    var t = new PointText({
        fillColor: c,
        content: n != undefined ? n : o.name,
        strokeWidth: 0
    });
    t.bounds.center = o.bounds.center;
    return t;
}

function selectItem(o){
    console.log("Select Item", o.name);
    o.selected = true;
    o.data.highlight.visible = true;
    o.data.highlight.bringToFront();
}

function clearSelected(){
    project.selectedItems.forEach(function(o, i){
        console.log("Unselect Item", o.name);
        o.data.highlight.visible = false;
    });
    project.activeLayer.selected = false;
}

function moveBoxes(o){
    var boxes = o.data.highlight.children[1].children;
    boxes[0].position = o.bounds.topLeft;
    boxes[1].position = o.bounds.topRight;
    boxes[2].position = o.bounds.bottomLeft;
    boxes[3].position = o.bounds.bottomRight;
}

function moveTitle(o){
    var t = o.data.highlight.children[2];
    t.bounds.center = o.bounds.center;
}

function adjustBounds(o){
    if(o.data.state == "moving"){
        o.data.highlight.position = o.position;
    } else {
        o.data.highlight.children[0].bounds = o.bounds;
        moveBoxes(o);
    }
}

var hex1 = drawHex(200);
console.log(hex1.data, hex1.data.highlight);

var segment, path;
var movePath = false;
var tool = new Tool();

tool.minDistance = 10;

tool.onMouseDown = function(event) {
    segment = path = null;
    var hitResult = project.hitTest(event.point, hitOptions);
    if (!hitResult){
        clearSelected();
        return;
    }
    if(hitResult && hitResult.type == "fill"){
        path = hitResult.item;
    }
    if (hitResult && hitResult.type == "segment") {
        path = project.selectedItems[0];
        segment = hitResult.segment;
        if(event.modifiers.control){
            path.data.state = "rotating";
        } else {
            path.data.state = "resizing";
            path.data.bounds = path.bounds.clone();
            path.data.scaleBase = event.point - path.bounds.center;
        }
        console.log(path.data);
    }
    movePath = hitResult.type == 'fill';
    if (movePath){
        project.activeLayer.addChild(hitResult.item);
        path.data.state = "moving";
        selectItem(path);
        console.log("Init Event", path.data.state);
    }
};

tool.onMouseDrag = function(event) {
    console.log(path, segment, path.data.state);
    if (segment && path.data.state == "resizing") {
        var bounds = path.data.bounds;
        var scale = event.point.subtract(bounds.center).length / path.data.scaleBase.length;
        var tlVec = bounds.topLeft.subtract(bounds.center).multiply(scale);
        var brVec = bounds.bottomRight.subtract(bounds.center).multiply(scale);
        var newBounds = new Rectangle(tlVec + bounds.center, brVec + bounds.center);        
        path.bounds = newBounds;
        adjustBounds(path);
    } else if(segment && path.data.state == "rotating") {
        var center = path.bounds.center;
        var baseVec = center - event.lastPoint;
        var nowVec = center - event.point;
        var angle = nowVec.angle - baseVec.angle;
        if(angle < 0){
            path.rotate(-45);
        } else {
            path.rotate(45);
        }
        adjustBounds(path);
    } else if (path && path.data.state == "moving") {
        path.position += event.delta;
        adjustBounds(path);
    }
};

这利用 .data 将边界框、手柄和标题的引用存储为一个组。这样,他们总是在那里,他们可以visible对或错。这样可以根据需要轻松显示和隐藏它们。

drawHex( width , color, name )

  • 宽度 - 必需,像素宽度
  • Color - 可选,定义填充颜色的字符串。默认值:#e9e9ff
  • Name - 可选,用作名称和标题的字符串。默认值:“Hexayurt”

互动

  • 点击 - 选择项目(显示边界框和手柄)
  • 点击 + 拖动 - 移动项目
  • 单击 + 拖动手柄 - 调整项目大小
  • Ctrl + 单击 + 拖动手柄 - 旋转项目

这是我第一次通过它,我可能会清理很多代码。例如,我可以专门将事件绑定到句柄,而不是查看更多全局事件。