Как добавить логи вращения c для мобильных устройств при перетаскивании на трехмерный глобус с помощью D3. js и html холста - PullRequest
1 голос
/ 07 мая 2020

Я следовал примеру в Learning D3. js 5 сопоставление для создания трехмерного глобуса и реализации логики увеличения и поворота c для перетаскивания карты.

Вот специфика c функция, которая позаботится об обеих функциях на устройствах, где я могу использовать мышь, например, ноутбуках и настольных компьютерах

function ready(data) {
      /* Interactivity goes here */

      var zoom = d3
        .zoom()
        .scaleExtent([0.5, 4])
        .on("zoom", zoomed);

      canvas.call(zoom);

      function zoomed() {
        var event = d3.event.sourceEvent.type;
        var dx = d3.event.sourceEvent.movementX;
        var dy = d3.event.sourceEvent.movementY;

        if (event === "wheel") {
          var transformScale = d3.event.transform.k;

          projectionScaleChange =
            (transformScale - prevTransformScale) * origProjectionScale;
          projectionScale = projectionScale + projectionScaleChange;
          projection.scale(projectionScale);
          prevTransformScale = transformScale;
        } else if (event === "mousemove") {
          // Here goes the rotation logic as this will be triggered upon dragging
          var r = projection.rotate();
          console.log(r);
          rotation = [r[0] + dx * 0.4, r[1] - dy * 0.5, r[2]];
          projection.rotate(rotation);
        } else {
          console.warn("unknown mouse event in zoomed()"); // alerting issues
        }

        requestAnimationFrame(function() {
          renderScene(data);
        });
      }

    }

logi c отлично работает, пока я не хочу реализовать эти функции на мобильных устройствах. Я попытался добавить случай для события touchmove, как показано ниже

функция готова (данные) {

  /* Interactivity goes here */

  var zoom = d3
    .zoom()
    .scaleExtent([0.5, 4])
    .on("zoom", zoomed);

  canvas.call(zoom);

  function zoomed() {
    var event = d3.event.sourceEvent.type;
    var dx = d3.event.sourceEvent.movementX;
    var dy = d3.event.sourceEvent.movementY;

    if (event === "wheel") {
      var transformScale = d3.event.transform.k;

      projectionScaleChange =
        (transformScale - prevTransformScale) * origProjectionScale;
      projectionScale = projectionScale + projectionScaleChange;
      projection.scale(projectionScale);
      prevTransformScale = transformScale;
    } else if (event === "mousemove") {
      // Here goes the rotation logic as this will be triggered upon dragging
      var r = projection.rotate();
      rotation = [r[0] + dx * 0.4, r[1] - dy * 0.5, r[2]];
      projection.rotate(rotation);
    } else if (event === "touchmove") {
      var r = projection.rotate();
      rotation = [r[0] + dx * 0.4, r[1] - dy * 0.5, r[2]];
      projection.rotate(rotation);
    } else {
      console.warn("unknown mouse event in zoomed()"); // alerting issues
    }

    requestAnimationFrame(function() {
      renderScene(data);
    });
  }

}

, но это не приведет к перемещению карты при перетаскивании с помощью мобильного устройства. Я также попытался записать значения переменной r, когда eventtype = touchmove, и понял, что 3 значения в массиве не меняются по сравнению с тем, когда я регистрирую ту же переменную r, когда eventtype = mousemove. Мне нужно понять почему. Кто-нибудь, пожалуйста, помогите.

1 Ответ

1 голос
/ 10 мая 2020

Как вы говорите, сенсорные устройства не отображают событие mousemove, а скорее событие touchmove. После тестирования на touchmove вы также должны учитывать, что координаты экрана не являются прямыми дочерними элементами объекта sourceEvent события, но свойства события касаются свойств объекта.

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

function getPostions(e) {
  let x;
  let y;

  // Ringfencing, in case type is not defined.
  if (!e) return [0, 0];

  // Distinguish between desktop and touch.
  if (e.type === "mousemove") {
    x = e.screenX;
    y = e.screenY;
  } else if (e.type === "touchmove") {
    x = e.changedTouches[0].screenX;
    y = e.changedTouches[0].screenY;
  }

  return [x, y];
}

В зависимости от выставленного <event>.type (mousemove или touchmove) вы получаете координаты экрана либо непосредственно из объекта события, либо как вложенные свойства из первого свойства <event>.changedTouches.

Однако масштабирование сложнее и не реализовано в приведенном выше примере.

Для более элегантного общего решения (также решает проблему Gimbal lock проблема с двумерным вращением глобуса) см. этот вариант масштабирования реализация.

...