2015-02-18 3 views
2

Я создаю визуализацию с использованием D3, у которой есть довольно богатое взаимодействие с пользователем. На данный момент у меня есть фон холст, который поддерживает следующие события:Как использовать пользовательские события

  • клик
  • двойного щелчок
  • долго нажмите
  • сопротивление (используется для панорамирования)
  • увеличения (щепотка + колесо)

Я пытаюсь управлять всеми этими событиями, и я дошел до того, что начинаю повторять свой код для разных частей приложения. В качестве примера в приложении:

  1. Двойной щелчок не должен вызвать одно действие щелчка
  2. Двойной щелчок должен также игнорировать тот факт, пользователь может случайно потащил слегка

Я Мы разработали подход к этому. Теперь мне интересно, есть ли метод, позволяющий мне повторно использовать созданное мной событие (немного похоже на создание d3.behaviour.zoom и использование .call).

Так, чтобы проиллюстрировать


Нажмите + двойной клик

У меня есть некоторое поведение, чтобы начать таймер на клик, который вызовет единственное поведение щелчка после тайм-аута. Если есть еще один клик и таймер все еще существует, отмените его и выполните двойное нажатие. Я в настоящее время закреплять это вверх так:

d3.select(target) 
    .append("circle") 
    .on("click", function (d) { 
    __onClick(d, this); 
    }); 

Я тогда определяется __onClick так:

var __onClick = function (d) { 
    var self = this; 

    if (d3.event.defaultPrevented) return; // ignore drag 
    d3.event.stopPropagation();   // prevent event bubbling 

    // If we already have a timer then we need to execute 
    // a double click behavior event 
    if (self.__timer) { 
     console.log("dblclick simulated"); 
     clearTimeout(self.__timer); 
     self.__timer = null; 
     self.DoADoubleClickThingy(); 
     return; 
    } 

    // Start a single click operation 
    self.__timer = setTimeout(function() { 
     console.log("click simulated"); 
     self.__timer = null; 
     self.DoSingleClickThingy(); 
    }, 250); 
}; 

Так что я думаю, я задаюсь вопросом, можно ли создать что-то вроде следующего зарегистрировать новое поведение? В настоящее время я пытаюсь пройти код D3, но недостаточно понимаю его, чтобы узнать, возможно ли это.

var clicks = d3.behaviours.customDoubleClick 
      .clickSpeed(250) 
      .on("click", function() { self.DoSingleClickThingy(); }) 
      .on("dblclick", function() { self.DoADoubleClickThingy(); }); 

d3.select(target) 
     .append("circle") 
     .call(clicks); 

ответ

1

После повторного чтения через zoom код на GitHub для D3 Я понял, что это было не так сложно, как я думал - особенно когда я понял, что call фактически просто выполняет функцию zoom. Вот решение, которое я собрал, что я надеюсь надеть github или подобное:

var custom = { }; 
custom.events = function() { 

    var clickTimeout = 3000;  // The timeout that is used to distinguish between a single and double click 
    var clickTimer;     // The timer that is used for a single click event 

    var dispatch = d3.dispatch("click", "dblclick"); 

    function events(g) { 

     g.on("click", clicked) 
     .on("dblclick", doubleClicked); 
    }; 

    function clicked(d, i) { 

     if (d3.event.defaultPrevented) return;  // Ignore if a drag operation is taking place 
     d3.event.stopPropagation();     // Prevent the event going any further 

     // If we already have a timer then we need to execute 
     // a double click behaviour event 
     if (clickTimer) { 
      clearTimeout(clickTimer); 
      clickTimer = null; 
      dispatch.dblclick.call(this, d, i); 
      return; 
     } 

     // Setup the timer for single click 
     clickTimer = setTimeout(function() { 
      // Trigger a single click 
      clickTimer = null; 
      dispatch.click.call(this, d, i); 
     }, clickTimeout); 
    }; 

    function doubleClicked(d, i) { 
     // Suppress the natural double click event 
     d3.event.stopPropagation(); 
    }; 

    // Return the bound events 
    return d3.rebind(events, dispatch, "on"); 
}; 
1

Вы можете использовать фабричную функцию, что, учитывая тип поведения вы хотите, возвращает функцию, которая устанавливает соответствующие обработчики:

function mkClicks(type) { 
    // figure out what "self" should be depending on type 
    return function(sel) { 
    sel.on("click", function() { self.DoSingleClickThingy(); }) 
     .on("dblclick", function() { self.DoADoubleClickThingy(); }); 
    } 
} 

d3.select(target) 
    .append("circle") 
    .call(mkClicks(typeA)); 
+0

Привет Ларс, спасибо за ответ. Да, я изучал масштабирование немного больше, и я думаю, что это в основном то, что он делает.Я пытаюсь собрать что-то вместе, и я думаю, что у меня есть шаблон работы (опубликует ответ, когда у меня есть), просто борясь с mousedown, разбивая мои клики по какой-то причине. – Ian

+0

Вы также можете использовать обычный шаблон D3 resuable, который равен тому же. Это зависит от того, насколько вам нужна гибкость. –

+0

Я добавил более конкретный вопрос http://stackoverflow.com/questions/28588871/double-click-timer-is-always-null-when-mousedown-mouseup-регистрировал о конкретной проблеме, которую я получил, чтобы опубликовать шаблон, который я использовал здесь тоже. Надеюсь, это полезно кому-то другому. – Ian