如何实现多项选择?

How to implement multiple item selection?

我正在开发一个绘图应用程序,用户可以在其中创建和删除形状,并 select 使用鼠标拖动它们。 selected 形状应该在 "selection" 数组中引用,还是它们每个都应该有一个 isSelected 属性?一种方法比另一种方法有什么优势吗?到目前为止,这是我注意到的(我会根据人们发现的任何内容对其进行更新)。我主要关心的是编程的简单性和性能。这个问题在很大程度上与语言无关,但应用程序在 javascript 中并且渲染是在 html5 canvas.

上完成的

检查select离子状态

虽然我们可以立即知道某个项目是否通过布尔值 select 编辑 属性,但基于数组的解决方案需要遍历数组以搜索该项目的引用。考虑到当鼠标悬停在 selected 形状上时光标应更改为 "dragging" 图标或当鼠标悬停在非 select 形状上时应更改为 "pointing hand" 图标,此验证非常普遍编辑项目。

删除select 项目

使用布尔值 属性 选择和取消select 特定项目是即时的。但是,对于数组-selection,我们必须首先循环遍历selection 以查看该项目是否存在,然后再将其添加到selection 或从selection 中删除。这使得 "toggle selection" 和 "add to selection" 选项慢得多。但是,在这种绘图程序中最常见的操作是在 selecting 特定元素之前清除 selection。使用数组方法,清除 selection 就像替换数组一样简单,而布尔方法需要将所有项目的 isSelected 属性 设置为 false。

正在删除项目

值得一提的是,一个项目在被删除之前必须从 selection 数组中移除。此详细信息不会显示在 isSelected 方法中。

调用所有 selected 项目

适用于所有 selected 项目的操作需要最少的数组代码,因为我们只需要遍历 selection 并在每个元素上调用该方法。如果 selection 与项目总量相比较小,则只需要循环遍历 selection 可能会节省大量时间。使用布尔值 属性,对所有 selected 项目调用操作所需的时间取决于项目的总数而不是 selection 的大小。

绘图时间

所选项目通常有彩色边框,可以透过其他元素看到。这意味着 selection 边界必须绘制在所有其他元素的前面。给定项目数 "n" 和 selected 项目数 ("s")...

数组解决方案需要 O(n)2*O(n) 来渲染。
布尔解决方案大约需要 2*O(n) 来渲染。

虽然您可能认为这证明选择数组方法本身是合理的,但请记住,重绘仅在触发操作后进行,而不是每秒 60 次。检查尖头形状以了解它是否 selected 比绘图更常见。唯一可能明显减慢速度的功能是拖动、拉伸和制作矩形 select 离子。由于该应用程序用于表示现实生活中的物品,因此人们通常在键盘上输入他们想要的尺寸,而不是实际拖动物品。

封装

布尔值 属性 的封装更强大,因为项目知道它们是否被 select 编辑,而无需查看应用程序范围变量。这种差异在严格范围的环境中可能意义重大,但在 javascript 中并不是什么大问题。我想有人会认为这不是形状对 select 本身的作用。

我已经实现了布尔值 isSelected 属性,并且随着应用程序的增长,我构建了越来越多适用于所有元素的函数。在每个函数中,我必须放置一个 for 循环,以确保只更改 selected 项目。我将编写某种伪代码以使该解决方案再次与语言无关。

for each item {
  if item is selected
    do stuff
}

到处都是同样的循环让我很烦,感觉不对。当您在函数之间复制粘贴代码部分时,这绝不是一个好兆头。所以我创建了一个 getSelection() 函数,它返回一个 selected 元素的数组。这似乎通过提取可怕的循环解决了我之前遇到的问题。

function getSelection() {
  selection = new empty array

  for each item in selection {
    if item is selected
      add item to selection array
  }
  return selection
}

function doStuffOnSelection() {
  selection = getSelection()

  for each item in selection {
    do stuff
  }
}

但这只揭示了更糟糕的事情。操作数组(不断地将项目推入数组、创建数组等)比仅仅浏览它们要慢得多。此更改大大降低了应用程序的速度,以至于在 Firefox 中,我什至不能一次拥有 100 个项目,而我过去可以拥有 2000 个项目而不掉帧。这样的减速都是因为我现在创建了一个数组。这是因为需要在程序的每一帧调用 getSelection 函数,以便在所有 selected 元素上绘制蓝色边框!

当我发布这个问题时,我不确定它是否是过早的优化,现在我知道它不是。

使用数组跟踪 selected 项目的主要缺点
- 您需要查看所有 selected 元素以确定特定元素是否已 selected 并更改该状态。
- 要从所有元素的数组中删除 selected 元素,您需要进行数组研究,这在任一数组都很大时特别慢。

使用每个项目的布尔值
跟踪 selected 项目的主要缺点 - 您需要查看所有元素以了解哪些元素被 selected 并更改该状态。

所以我确定了一个更好的方法来处理所有的消极方面:两者兼顾。我将在后台保留一组 selected 项目,当事情被 selected 和 deselected 时,它会更新。这消除了仅数组 selection 的所有缺点,因为我不再需要搜索任何数组来了解特定元素是否为 selected(我可以查看其布尔值) .所有适用于 selected 项但不适用于 select 和 deselect 项的函数(例如移动、调整大小、绘制)都可以循环遍历 selection 数组.两全其美!