使用KineticJS选择多个对象






4.20/5 (4投票s)
KineticJS 库使使用 HTML5 canvas 标签变得轻松而直接。此示例演示如何
引言
该KineticJS 库使使用 HTML5 canvas 标签变得轻松而直接。此示例演示如何使用KineticJS
库在屏幕上创建可以通过拖动鼠标创建选择框来选择的对象。
背景
如果您不熟悉 HTML5 canvas 标签,请访问HTML5CanvasTutorials.com。如果您不熟悉 KineticJS 库,请点击这里了解更多信息以及它们如何使 Web 浏览器的图形体验焕然一新。此示例针对 KineticJS 库的 4.7.4 版本,可在这里找到。本文是使用 KineticJS 库选择和操作 HTML5 canvas 对象的两篇文章系列中的第一篇。
Using the Code
在此示例中,我在 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();
要使此方法起作用的第一个技巧是添加一个透明的背景矩形。这将允许我们捕获背景上发生的点击,同时仍然允许在单击时像往常一样拖动和放下我们的对象。
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
在我们计算鼠标仍在移动时的框的大小时防止重入代码。bHaveSelBox
防止我们多次绘制框rectSel
是一个全局变量,用于保存选择框,这样我们就不必一直查找它(性能更快)initX
和initY
包含按下鼠标时的初始位置。这使我们能够计算拖动时选择矩形的大小。
现在我们需要引入一些事件处理程序。我们需要捕获的第一件事是我们透明背景上的点击,以便知道开始绘制选择框。
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 code
function 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
数组中,以便以后可以访问它们。我们还需要突出显示我们的方块,以便我们可以直观地看到哪些项目被选中。
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 rectangle
if (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 box
var 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 to
var 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);
}
}
}
}
}
最后,用户会期望在选择背景或单个项目时,项目将取消选中。为了实现这一点,我们添加以下内容
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 button
if (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);
}
关注点
需要注意的是,调用setAbsolutePosition
的行是绝对必要的。如果您只设置x
和y
,则该位置相对于舞台的鼠标点击位置不会相同,这会在您计算哪些项目落在选择框内时给您带来很大的痛苦。
本系列的第二篇文章可在这里找到。
您可以在这里看到此项目的运行演示。
历史
- 2013 年 11 月 21 日:初始版本