d3.behaviour.zoom在使用单指操作时禁用触摸屏平移功能

6
我有一个力导向图D3图表,使用d3.behaviour.zoom进行缩放和平移。它的工作很顺利,但因为我的图表在iPad上占据了整个屏幕,所以在触摸屏上向下滚动页面变得不可能,因为我的触摸会导致图表平移而不是滚动页面。我想在触摸设备上保留捏合缩放行为。是否有可能仅在d3.event.touches.length === 1时取消touchmove.zoom事件?如果这样做,所有触摸交互都将被禁用,因此滚动没问题,但无法再缩放图表:
selection.call(zoom)
    .on('touchstart.zoom', null)
    .on('touchmove.zoom', null)
    .on('touchend.zoom', null);

我也尝试添加另一个 touchmove 监听器,就像这样:

selection.call(zoom)
    .on('touchmove', function () {
        if (d3.event.touches.length === 1) {
            d3.event.stopPropagation();
            // i've also tried d3.event.preventDefault() but it doesn't do anything         
        }
    });

我不认为我可以访问d3中的默认缩放touchmove监听器,我想知道是否可以在touchstart上删除监听器,然后在用户仅使用一个触摸时重新绑定它在touchend上。

2个回答

0

不使用d3 dispatch的一种方法是使用原始的touchstart事件监听器而不是d3。 通过event.touches.length,您可以获得屏幕上检测到的触摸数量。

通过添加监听器并在d3.behaviour.zoom中增加一个额外的检查,当触摸计数为1时,它会防止在图表上有一个手指时进行平移,并将默认滚动页面。

let touchCount = 0;

// Only necessary to add listener when running on mobile/tablet
// (assuming you already have some detection set up)

if (isMobileSize) {
    document.querySelector('.your-svg').addEventListener('touchstart', (e) => {
        touchCount = e.touches.length;
    });
}


let selection = d3.select('.your-svg');
let zoom = d3.behavior.zoom().on('zoom', () => {

    // When 1 finger, do not zoom
    if (touchCount === 1) {
        d3.event.sourceEvent.stopPropagation();
        return;
    }

    // Otherwise run zoom/move
    // ... zoom logic goes here....
})

selection.call(zoom)

0

听起来你需要比内置事件更细粒度的控制。尝试创建一个处理标准事件并将其转换为自定义事件的对象(根据您选择的规则,例如仅限1个触摸)。然后,您可以借助d3分派机制监听这些抛出的自定义事件。

Events = function() {

  // custom event set up using d3 dispatch
  var dispatch = d3.dispatch("drag", "drag2");

  //listen for standard event on containing DOM element
  var events = function (g) {
    //g is the container DOM element

    // register the standard events required
    g.on("touchmove", touchmove)

    return events;
  };

  //function that handles the touchmove standard event
  function touchmove(d,i) {

    //stop the default event going any further
    d3.event.stopPropagation();

    //depending on your conditions choose which custom event you trigger
    if(condition1)
    {
      dispatch.drag.call(d3.event.target, d, i);
    }
    else if(condition2)
    {
      dispatch.drag2.call(d3.event.target, d, i);
    }
  }

  //add events defined in d3 dispatch function to events object and return   
  return d3.rebind(events, dispatch, "on");
}

在代码的其他位置创建事件对象并监听其自定义事件:

events = new Events();

//Set the containing element the events object should be registering event listeners to
d3.select(container).call(events);

events.on("drag", function (d, i, position) {
    //do something here
})

events.on("drag2", function (d, i, position) {
    //do something else here
})

使用这种技术,我需要编写自己的自定义 touchmove.zoom 处理程序来复制内置处理程序,是吗? - Daniel Crisp
没错。你确定你不能让 selection.call(zoom) zoom.on("zoom", function() {... 做你需要做的事情吗? - Andrew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接