首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >区分同一元素上的onClick和onDoubleClick,以在Dart中执行不同的操作

区分同一元素上的onClick和onDoubleClick,以在Dart中执行不同的操作
EN

Stack Overflow用户
提问于 2014-07-12 07:07:40
回答 3查看 1.4K关注 0票数 3

我想做不明智的事情,将onClick和onDoubleClick放在同一个元素上,每种类型的事件都会导致不同的操作。具体而言,在图像上,单击可前进到下一图像,双击可切换全屏。

很自然地,我得到两次单击,然后是双击(尽管我知道有些浏览器在双击之前只触发一次单击)。

我曾想过让自己轻松一点,把每个事件放到一个缓冲区(列表)中--或者更确切地说,将event.type字符串添加到列表中,然后,在适当的时间后,比如250或300毫秒,检查缓冲区中的最后一项,如果双击,则全屏显示列表的长度。

我发现列表中只有一项,而且我还没有弄清楚如何让计时器工作。

在我的尝试中,有一个:

代码语言:javascript
复制
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);
}

这将导致以下输出打印到控制台:

代码语言:javascript
复制
[click]
[click]
[dblclick]

而不是

代码语言:javascript
复制
[click, click, dblclick]

如果我放慢速度,在这三种事件类型一起打印之前会有一个明显的延迟

所以..。

更大的问题是‘如何区分单击和双击,并对每种操作执行不同的操作?’

其他问题包括:

如何用连续的事件填充缓冲区(并在以后将其清除)-或者甚至如何使用Dart的事件流作为缓冲区……

在检查缓冲区内容之前,实际的超时时间是多少?

(我猜最后一个问题是‘我是否应该放弃努力,转而采用一组传统的带有字形图标的按钮?’)

EN

回答 3

Stack Overflow用户

发布于 2014-07-12 08:15:25

您的页面应该尽可能快地对用户输入做出反应。如果您等待确认双击-您的onClick将变得更慢的响应。您可以在第一次单击后通过更改元素的可视状态(例如,播放动画)来隐藏问题,但这可能会使用户感到困惑。而手持设备更是雪上加霜。此外,如果元素只对onClick事件做出反应,你可以“作弊”并监听onmousedown -它会让你的UI响应得更快。

最重要的是,双击,从一个客户端到另一个客户端,可能会有明显不同的触发时间间隔,而且对于用户来说,你可以双击一些东西并不直观。你将不得不用不必要的提示来扩充你的界面。

编辑:添加了我的解决方案。它应该是相当可扩展的,并且是未来的证明。

代码语言:javascript
复制
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));
}
票数 1
EN

Stack Overflow用户

发布于 2014-07-12 14:30:01

问题是您的变量不是全局变量。

代码语言:javascript
复制
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)跳过第二个单击事件。所以你只有clickdblclick

现在,当您获得一个单击事件时,您可以像以前一样创建一个新的计时器并运行单击操作。如果您得到dblclick事件,您只需运行您的操作。这可能是这样的:

代码语言:javascript
复制
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,只要我们一个计数器,并在点击事件后几毫秒后重置它。

我希望这对你有帮助。

打招呼,罗伯特

票数 1
EN

Stack Overflow用户

发布于 2014-07-12 18:47:57

我不确定IE是否仍具有此处说明的事件序列(无第二次单击事件) https://stackoverflow.com/a/5511527/217408

如果是,您可以使用Roberts解决方案的一个稍有偏差的变体:

代码语言:javascript
复制
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解决方案。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24707911

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档