使用KineticJS选择多个对象

分享于 

11分钟阅读

Web开发

  繁體

Sample Image - maximum width is 600 pixels

介绍

使用标记可以使 KineticJS库变得简单而直观。 这个示例演示如何使用 KineticJS 库在屏幕上创建对象,通过拖动鼠标来创建选择框,可以选择该对象。

背景

如果你是 HTML5 Canvas 标签的新手,请访问 HTML5CanvasTutorials.com。 如果你是KineticJS库的新手,点击这里了解更多关于它们以及它们如何为网页浏览器创建全新的体验的方式。 本示例适用于库版本 4.7.4,可以在这里找到 本文是一系列使用KineticJS库选择和操作 HTML5 Canvas 对象的系列中的第一部分。

使用代码

在这个例子中,我在一个 HTML5 Canvas 上创建了三个框,我将能够选择和对齐。 往常一样,KineticJS项目首先创建一个 div 容器:

<div id="container" style="position:absolute;left:0;top:0"></div>

接下来,创建一个阶段和一个层- 再次是标准KineticJS实践:

var stage = new Kinetic.Stage({
 container: 'container',
 width: 300,
 height: 500});var layer = new Kinetic.Layer();

让它工作的第一个技巧是添加透明的background rectangle。 这将允许我们捕获在 background 上发生的点击,同时仍允许对象被拖动并在单击时被拖放。

var rectBackground = new Kinetic.Rect({
 x: 0,
 y: 0,
 height: stage.attrs.height,
 width: stage.attrs.width,
 fill: 'transparent',
 draggable: false,
 name: 'rectBackground'});
layer.add(rectBackground);

现在,我们将把块添加到画布:

DrawBlocks();function DrawBlocks()
{
 var x, y, height;
 x = 90;
 y = 10;
 size = 40;
 CreateBlock(x, y, size, size, "green");
 x = 150;
 y = 80;
 CreateBlock(x, y, size + 20, size + 60, "red");
 x = 110;
 y = 170;
 CreateBlock(x, y, size, size, "blue");
 layer.draw();
}function CreateBlock(x, y, height, width, color)
{
 var grpBlk = new Kinetic.Group({
 x: x,
 y: y,
 height: height,
 width: width,
 name: color,
 draggable: true });
 var blk = new Kinetic.Rect({
 x: x,
 y: y,
 height: height,
 width: width,
 fill: color,
 name: color + ' block' });
 grpBlk.add(blk);
 blk.setAbsolutePosition(x, y);
 grpBlk.setAbsolutePosition(x, y);
 layer.add(grpBlk);
 return grpBlk;
}

通过这里示例,你可以通过将鼠标拖过画布的某个区域来创建选择框。 为此,我们需要几个变量:

var arSelected = new Array();var bDragging = false;var bHaveSelBox = false;var rectSel = null;var initX = 0;var initY = 0;
  • arSelected 将保存用户已经选择的块的名称。
  • 当我们在计算框的大小时,bDragging 会阻止进入的代码 moving.</li</>bHaveSelBox 使我们不能多次绘制盒子
  • rectSel 是一个全局保存选择框,以便我们不必继续查找( 更快的性能)
  • initXinitY 在鼠标按下时包含初始位置。 这允许我们计算选择 rectangle的大小,因为它被拖动。

现在我们需要介绍一些事件处理程序。 我们需要捕获的第一件事是点击我们的透明 background,以便我们知道开始绘制选择框。

rectBackground.on("mousedown", function (evt)
{
 bDragging = true;
});

接下来,我们需要在鼠标拖动时重新绘制选择框。

stage.getContent().addEventListener('mousemove', function (e)
{
 if (bDragging)
 {
 SetSelRectPosition(e);
 }
});var bInHere = false; //prevents re-entrance in event driven codefunction SetSelRectPosition(e)
{
 if (bDragging &&!bInHere)
 {
 bInHere = true;
 var canvas = layer.getCanvas();
 var mousepos = stage.getPointerPosition();
 var x = mousepos.x;
 var y = mousepos.y;
 if (!bHaveSelBox)
 {
 initX = x;
 initY = y;
 //create the selection rectangle rectSel = new Kinetic.Rect({
 x: initX,
 y: initY,
 height: 1,
 width: 1,
 fill: 'transparent',
 stroke: 'black',
 strokeWidth: 1 });
 layer.add(rectSel);
 layer.draw();
 bHaveSelBox = true;
 }
 else {
 var height = 0;
 var width = 0;
 var newX = 0;
 var newY = 0;
 if (x> initX)
 newX = initX;
 else newX = x;
 if (y> initY)
 newY = initY;
 else newY = y;
 height = Math.abs(Math.abs(y) - Math.abs(initY));
 width = Math.abs(Math.abs(x) - Math.abs(initX));
 rectSel.setHeight(height);
 rectSel.setWidth(width);
 rectSel.setX(newX);
 rectSel.setY(newY);
 layer.draw();
 }
 }
 bInHere = false;
}

当用户让鼠标出来时,我们需要找出哪些项目被选中并将他们的名字放在 arSelected array 中。 我们还需要 Highlight的盒子,以便我们可以看到哪些项目被选中。

stage.getContent().addEventListener('mouseup', function (e)
{
 if (bDragging)
 {
 bDragging = false;
 GetOverlapped();
 if (rectSel!= null)
 rectSel.remove();
 rectSel = null;
 bHaveSelBox = false;
 layer.draw();
 }
});function GetOverlapped()
{
 //bail if there is no rectangleif (rectSel == null)
 return;
 var iHeight = 0;
 var iWidth = -1000;
 arSelected.length = 0;
 initX = 10;
 initY = 10;
 var arGroups = layer.getChildren();
 for (i = 0; i <arGroups.length; i++)
 {
 var grp = arGroups[i];
 if (grp.attrs.name!= rectSel.attrs.name && 
 grp.attrs.name!= rectBackground.attrs.name && grp.attrs.name!= 'btn' &&
 grp.attrs.name!= 'highlightBlock')
 {
 var pos = rectSel.getAbsolutePosition();
 //get the extents of the selection boxvar selRecXStart = parseInt(pos.x);
 var selRecXEnd = parseInt(pos.x) + parseInt(rectSel.attrs.width);
 var selRecYStart = parseInt(pos.y);
 var selRecYEnd = parseInt(pos.y) + parseInt(rectSel.attrs.height);
 //get the extents of the group to compare tovar grpXStart = parseInt(grp.attrs.x);
 var grpXEnd = parseInt(grp.attrs.x) + parseInt(grp.attrs.width);
 var grpYStart = parseInt(grp.attrs.y);
 var grpYEnd = parseInt(grp.attrs.y) + parseInt(grp.attrs.height);
 //Are we inside the selction area?if ((selRecXStart <= grpXStart && selRecXEnd> = grpXEnd) && 
 (selRecYStart <= grpYStart && selRecYEnd> = grpYEnd))
 {
 if (arSelected.indexOf(grp.getName()) <0)
 {
 arSelected.push(grp.getName());
 var tmpX = parseInt(grp.attrs.x);
 var tmpY = parseInt(grp.attrs.y);
 var rectHighlight = new Kinetic.Rect({
 x: tmpX,
 y: tmpY,
 height: grp.attrs.height,
 width: grp.attrs.width,
 fill: 'transparent',
 name: 'highlightBlock',
 stroke: '#41d6f3',
 strokeWidth: 3 });
 layer.add(rectHighlight);
 }
 }
 }
 }
}

最后,用户会期望当 background 或者单个项目被选中时,这些项目将被取消选中。 为了实现这一目的,我们添加了以下内容:

stage.getContent().addEventListener('mousedown', function (e)
{ 
 if(arSelected.length> 0)
 {
 var name = "";
 if (e.shape!= undefined)
 name = e.shape.attrs.name;
 if(e.targetNode!= undefined)
 name = e.targetNode.attrs.name;
 //we don't want to unselect if we are pushing the buttonif (name!= 'btn')
 {
 RemoveHighlights();
 }
 }
});function RemoveHighlights()
{
 var arHighlights = layer.get('.highlightBlock');
 while (arHighlights.length> 0)
 {
 arHighlights[0].remove();
 arHighlights = layer.get('.highlightBlock');
 }
 arSelected.length = 0;
}

这里时,你应该能够给出这个运行,你应该能够选择这些项目。

如果希望能够获得选定对象的列表,请将这里按钮添加到屏幕:

x = 85;
y = 250;var grpGetSelectedButton = CreateButton(x, y, "Get Selected");
grpGetSelectedButton.on("click", function (evt) { ShowSelected(); });function CreateButton(x, y, text)
{
 var grpButton = new Kinetic.Group({
 x: x,
 y: y,
 height: 30,
 width: 135,
 name: 'btn',
 draggable: true });
 var blkButton = new Kinetic.Rect({
 x: x,
 y: y,
 height: 30,
 width: 135,
 fill: 'Violet',
 name: 'btn' });
 var txtButton = new Kinetic.Text({
 x: x + 2,
 y: y + 2,
 fontFamily: 'Calibri',
 fontSize: 22,
 text: text,
 fill: 'black',
 name: 'btn' });
 grpButton.add(blkButton);
 grpButton.add(txtButton);
 grpButton.setAbsolutePosition(x, y);
 blkButton.setAbsolutePosition(x, y);
 txtButton.setAbsolutePosition(x + 2, y + 2);
 layer.add(grpButton);
 return grpButton;
}function ShowSelected()
{
 var str = "";
 for (var i = 0; i <arSelected.length; i++)
 {
 str += arSelected[i] + ",";
 }
 if (str!= "")
 str = str.substring(0, str.length - 2);
 alert(str);
}

Points of Interest

应该注意的是,调用 setAbsolutePosition的行是绝对必要的。 设置的位置将与鼠标单击时的位置不相同,这将导致你在计算选择框中的项目时遇到很大的痛苦。

这系列的第二篇文章可以在这里找到。

你可以在这里看到这个项目的一个工作演示( )。

历史记录

  • 21st 2013年11月: 初始版本

相关文章