【问题标题】:How to implement multiple item selection?如何实现多项选择?
【发布时间】:2015-04-04 06:30:43
【问题描述】:

我正在开发一个绘图应用程序,用户可以在其中创建和删除形状并用鼠标选择它们以拖动它们。是否应该在“选择”数组中引用选定的形状,或者它们每个都应该有一个 isSelected 属性?一种方法比另一种方法有什么优势吗?这是我到目前为止注意到的(我会用人们发现的任何东西来更新它)。我主要关心的是编程的简单性和性能。这个问题在很大程度上与语言无关,但应用程序是 javascript 并且渲染是在 html5 画布上完成的。

检查选择状态

虽然我们可以通过布尔属性立即知道是否选择了某个项目,但基于数组的解决方案需要遍历数组来搜索该项目的引用。考虑到当鼠标悬停在选定的形状上时光标应该变为“拖动”图标,或者当鼠标悬停在未选定的项目上时,光标应该变为“指向手”图标,这种验证非常常见。

取消选择项目

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

删除项目

值得一提的是,必须先从选择数组中删除一个项目,然后才能将其删除。这个细节不会出现在 isSelected 方法中。

调用所有选定的项目

适用于所有选定项的操作需要最少的数组代码,因为我们只需要循环选择并调用每个元素的方法。如果与项目总数相比,选择很小,则必须仅循环通过选择可能会节省大量时间。使用布尔属性,对所有选定项调用操作所需的时间取决于项的总数,而不是选定项的大小。

绘制时间

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

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

虽然您可能认为这有理由单独选择数组方法,但请记住,重绘仅在触发操作后完成,而不是每秒 60 次。检查指出的形状以了解它是否被选中比绘图更常见。唯一可能会明显减慢速度的功能是拖动、拉伸和进行矩形选择。由于该应用程序用于表示现实生活中的项目,因此人们通常在键盘上输入他们想要的大小,而不是实际拖动项目。

封装

布尔属性的封装更强,因为项目无需查看应用程序范围的变量即可知道它们是否被选中。这种差异在严格范围的环境中可能意味着很多,但在 javascript 中并不是什么大问题。我想人们可能会认为选择自己不是形状的角色。

【问题讨论】:

  • 我建议您研究 MV* 编程模式,因为它们与 JavaScript 相关:Backbone.js、Angular.js、Ember.js 和其他框架有助于解决这些类型的问题。 infragistics.com/community/blogs/nanil/archive/2013/04/01/…
  • @Diodeus 我看不出这对我有什么帮助,因为它是一个没有数据库的仅限客户端的应用程序,而且我遇到的问题主要与 UI 相关。再说了,大部分的应用都做完了,不能随便加个框架。
  • MV* 模式无需与后端通信即可使用。客户端模型、模板、视图和事件在您正在构建的应用程序类型中仍然有效且有用。是的,对于你正在做的事情来说,这可能为时已晚。

标签: javascript language-agnostic multipleselection


【解决方案1】:

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

for each item {
  if item is selected
    do stuff
}

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

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函数来为所有被选中的元素绘制蓝色边框!

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

使用数组跟踪所选项目的主要缺点
- 您需要查看所有选定元素以确定是否选择了特定元素并更改该状态。
- 要从所有元素的数组中删除选定的元素,您需要数组研究,当任何一个数组都很大时,这尤其慢。

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

因此,我选择了一种更好的方法来处理所有不利方面:两者兼而有之。我将在背景中保留一系列选定的项目,在选择并取消选择时会更新。这消除了仅数组选择的所有不利方面,因为我不再需要搜索任何数组来了解是否选择了特定元素(我可以查看它的布尔值)。并且所有适用于选定项目但不选择和取消选择项目的功能(例如移动,调整大小,绘制)都可以循环通过选择数组。两全其美!

【讨论】:

  • 好的,Firefox 使用该解决方案似乎仍然很慢。这一定是由于添加了旋转元素的功能。性能上的提升并不那么明显。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-15
  • 2015-04-23
  • 1970-01-01
  • 2014-06-10
  • 1970-01-01
  • 2019-04-07
  • 1970-01-01
相关资源
最近更新 更多