我想做不明智的事情,将onClick和onDoubleClick放在同一个元素上,每种类型的事件都会导致不同的操作。具体而言,在图像上,单击可前进到下一图像,双击可切换全屏。
很自然地,我得到两次单击,然后是双击(尽管我知道有些浏览器在双击之前只触发一次单击)。
我曾想过让自己轻松一点,把每个事件放到一个缓冲区(列表)中--或者更确切地说,将event.type字符串添加到列表中,然后,在适当的时间后,比如250或300毫秒,检查缓冲区中的最后一项,如果双击,则全屏显示列表的长度。
我发现列表中只有一项,而且我还没有弄清楚如何让计时器工作。
在我的尝试中,有一个:
void catchClickEvents(Event e) {
var eventTypes = new List<String>();
eventTypes.add(e.type);
Duration duration = const Duration(milliseconds: 300);
var timeout = new Timer(duration, () => processEvents(eventTypes));
}
void processEvents(List eTypes) {
// just to see what is going on...
print(eTypes);
}这将导致以下输出打印到控制台:
[click]
[click]
[dblclick]而不是
[click, click, dblclick]如果我放慢速度,在这三种事件类型一起打印之前会有一个明显的延迟
所以..。
更大的问题是‘如何区分单击和双击,并对每种操作执行不同的操作?’
其他问题包括:
如何用连续的事件填充缓冲区(并在以后将其清除)-或者甚至如何使用Dart的事件流作为缓冲区……
在检查缓冲区内容之前,实际的超时时间是多少?
(我猜最后一个问题是‘我是否应该放弃努力,转而采用一组传统的带有字形图标的按钮?’)
发布于 2014-07-12 08:15:25
您的页面应该尽可能快地对用户输入做出反应。如果您等待确认双击-您的onClick将变得更慢的响应。您可以在第一次单击后通过更改元素的可视状态(例如,播放动画)来隐藏问题,但这可能会使用户感到困惑。而手持设备更是雪上加霜。此外,如果元素只对onClick事件做出反应,你可以“作弊”并监听onmousedown -它会让你的UI响应得更快。
最重要的是,双击,从一个客户端到另一个客户端,可能会有明显不同的触发时间间隔,而且对于用户来说,你可以双击一些东西并不直观。你将不得不用不必要的提示来扩充你的界面。
编辑:添加了我的解决方案。它应该是相当可扩展的,并且是未来的证明。
import 'dart:html';
import 'dart:async';
import 'dart:math';
//enum. Kinda... https://code.google.com/p/dart/issues/detail?id=88
class UIAction {
static const NEXT = const UIAction._(0);
static const FULLSCREEN = const UIAction._(1);
static const ERROR = const UIAction._(2);
final int value;
const UIAction._(this.value);
}
/**
*[UIEventToUIAction] transforms UIEvents into corresponding UIActions.
*/
class UIEventToUIAction implements StreamTransformer<UIEvent, UIAction> {
/**
* I use "guesstimate" value for [doubleClickInterval] but you can calculate
* comfortable value for the user from his/her previous activity.
*/
final Duration doubleClickInterval = const Duration(milliseconds: 400);
final StreamController<UIAction> st = new StreamController();
Stream<UIAction> bind(Stream<UIEvent> originalStream) {
int t1 = 0,
t2 = 0;
bool isOdd = true;
Duration deltaTime;
originalStream.timeout(doubleClickInterval, onTimeout: (_) {
if ((deltaTime != null) && (deltaTime >= doubleClickInterval)) {
st.add(UIAction.NEXT);
}
}).forEach((UIEvent uiEvent) {
isOdd ? t1 = uiEvent.timeStamp : t2 = uiEvent.timeStamp;
deltaTime = new Duration(milliseconds: (t1 - t2).abs());
if (deltaTime < doubleClickInterval) st.add(UIAction.FULLSCREEN);
isOdd = !isOdd;
});
return st.stream;
}
}
void main() {
//Eagerly perform time consuming tasks to decrease the input latency.
Future NextImageLoaded;
Future LargeImageLoaded;
element.onMouseDown.forEach((_) {
NextImageLoaded = asyncActionStub(
"load next image. Returns completed future if already loaded");
LargeImageLoaded = asyncActionStub(
"load large version of active image to show in fullscreen mode."
"Returns completed future if already loaded");
});
Stream<UIEvent> userInputs = element.onClick as Stream<UIEvent>;
userInputs.transform(new UIEventToUIAction()).forEach((action) {
switch (action) {
case UIAction.FULLSCREEN:
LargeImageLoaded.then( (_) =>asyncActionStub("fullscreen mode") )
.then((_) => print("'full screen' finished"));
break;
case UIAction.NEXT:
NextImageLoaded.then( (_) =>asyncActionStub("next image") )
.then((_) => print("'next image' finished"));
break;
default:
asyncActionStub("???");
}
});
}
final DivElement element = querySelector("#element");
final Random rng = new Random();
final Set performed = new Set();
/**
*[asyncActionStub] Pretends to asynchronously do something usefull.
* Also pretends to use cache.
*/
Future asyncActionStub(String msg) {
if (performed.contains(msg)) {
return new Future.delayed(const Duration(milliseconds: 0));
}
print(msg);
return new Future.delayed(
new Duration(milliseconds: rng.nextInt(300)),
() => performed.add(msg));
}发布于 2014-07-12 14:30:01
问题是您的变量不是全局变量。
var eventTypes = new List<String>();
void catchClickEvents(Event e) {
eventTypes.add(e.type);
Duration duration = const Duration(milliseconds: 300);
var timeout = new Timer(duration, () => processEvents(eventTypes));
}
void processEvents(List eTypes) {
print(eTypes);
}还有一个应该返回点击次数的e.detail。如果你不需要Internet Explorer,你可以使用它。你的列表的问题是,它会增长,并且永远不会被清除。
让我们考虑一下我们所知道的:你会得到点击事件,在某个时候你会有双击。
你可以在这里使用点击计数器。(或使用e.detail)跳过第二个单击事件。所以你只有click和dblclick。
现在,当您获得一个单击事件时,您可以像以前一样创建一个新的计时器并运行单击操作。如果您得到dblclick事件,您只需运行您的操作。这可能是这样的:
DivElement div = querySelector('#div');
Timer timeout = null;
div.onClick.listen((MouseEvent e) {
if(e.detail >= 2) {
e.preventDefault();
} else {
if(timeout != null) {
timeout.cancel();
}
timeout = new Timer(new Duration(milliseconds: 150), () => print('click'));
}
});
div.onDoubleClick.listen((MouseEvent e) {
if(timeout != null) {
timeout.cancel();
timeout = null;
}
print('dblclick');
});这是为我工作的示例代码。如果你不能依靠e.detail,只要我们一个计数器,并在点击事件后几毫秒后重置它。
我希望这对你有帮助。
打招呼,罗伯特
发布于 2014-07-12 18:47:57
我不确定IE是否仍具有此处说明的事件序列(无第二次单击事件) https://stackoverflow.com/a/5511527/217408
如果是,您可以使用Roberts解决方案的一个稍有偏差的变体:
library app_element;
import 'dart:html' as dom;
import 'dart:async' as async;
Duration dblClickDelay = new Duration(milliseconds: 500);
async.Timer clickTimer;
void clickHandler(dom.MouseEvent e, [bool forReal = false]) {
if(clickTimer == null) {
clickTimer = new async.Timer(dblClickDelay, () {
clickHandler(e, true);
clickTimer = null;
});
} else if(forReal){
print('click');
}
}
void dblClickHandler(dom.MouseEvent e) {
if(clickTimer != null) {
clickTimer.cancel();
clickTimer = null;
}
print('doubleClick');
}
void main() {
dom.querySelector('button')
..onClick.listen(clickHandler)
..onDoubleClick.listen(dblClickHandler);
}或者只使用具有mouseUp事件而不是click事件的Roberts解决方案。
https://stackoverflow.com/questions/24707911
复制相似问题