Skip to main content

画布形状上的手势事件

如何在画布形状上监听滑动、捏合缩放、旋转和其他多点触控手势事件?

默认情况下,Konva 仅支持基本的触摸事件,如 touchstarttouchmovetouchend

你需要手动从这些触摸事件中实现手势事件。

如果你在寻找整个舞台的平移和缩放逻辑,可以查看 多点触控缩放舞台演示

但我能够稍微修改 Hammer.js 使其与 Konva 一起工作!

你可以在这里找到修改后的 hammer.js 源代码

说明:你可以在矩形上试验不同的手势,如滑动、旋转、缩放、拖放、按压。在桌面浏览器上,按住 Shift 键可以模拟触摸事件。

import Konva from 'konva';

// 加载所需的脚本
const loadScript = (src) => {
  return new Promise((resolve, reject) => {
    if (document.querySelector(`script[src="${src}"]`)) {
      resolve();
      return;
    }
    const script = document.createElement('script');
    script.src = src;
    script.onload = resolve;
    script.onerror = reject;
    document.head.appendChild(script);
  });
};

// 加载依赖后初始化演示
async function initDemo() {
  try {
    await loadScript('https://cdn.rawgit.com/hammerjs/touchemulator/master/touch-emulator.js');
    await loadScript('https://konvajs.org/js/hammer-konva.js');

    // 在桌面上模拟触摸
    TouchEmulator();
    Konva.hitOnDragEnabled = true;
    Konva.captureTouchEventsEnabled = true;

    const stage = new Konva.Stage({
      container: 'container',
      width: window.innerWidth,
      height: window.innerHeight,
    });

    const layer = new Konva.Layer();
    stage.add(layer);

    const originalAttrs = {
      x: stage.width() / 2,
      y: stage.height() / 2,
      scaleX: 1,
      scaleY: 1,
      draggable: true,
      rotation: 0,
    };

    const group = new Konva.Group(originalAttrs);
    layer.add(group);

    const size = 200;

    const rect = new Konva.Rect({
      width: size,
      height: size,
      fill: 'yellow',
      offsetX: size / 2,
      offsetY: size / 2,
      cornerRadius: 5,
      shadowBlur: 10,
      shadowColor: 'grey',
    });
    group.add(rect);

    const defaultText = '试试\拖动、滑动、捏合缩放、旋转、按压...';
    const text = new Konva.Text({
      text: defaultText,
      x: -size / 2,
      width: size,
      align: 'center',
    });
    group.add(text);

    // 附加修改后的 Hammer.js
    // "domEvents" 属性允许在组上触发事件
    // 而不是 "hammertime" 实例
    const hammertime = new Hammer(group, { domEvents: true });

    // 添加旋转手势
    hammertime.get('rotate').set({ enable: true });

    // 现在附加所有可能的事件
    group.on('swipe', function (ev) {
      text.text('正在滑动');
      group.to({
        x: group.x() + ev.evt.gesture.deltaX,
        y: group.y() + ev.evt.gesture.deltaY,
        onFinish: function () {
          group.to(Object.assign({}, originalAttrs));
          text.text(defaultText);
        },
      });
    });

    group.on('press', function (ev) {
      text.text('正在按压');
      rect.to({
        fill: 'green',
      });
    });

    group.on('touchend', function (ev) {
      rect.to({
        fill: 'yellow',
      });

      setTimeout(() => {
        text.text(defaultText);
      }, 300);
    });

    group.on('dragend', () => {
      group.to(Object.assign({}, originalAttrs));
    });

    let oldRotation = 0;
    let startScale = 0;
    group.on('rotatestart', function (ev) {
      oldRotation = ev.evt.gesture.rotation;
      startScale = rect.scaleX();
      group.stopDrag();
      group.draggable(false);
      text.text('正在旋转...');
    });

    group.on('rotate', function (ev) {
      const delta = oldRotation - ev.evt.gesture.rotation;
      group.rotate(-delta);
      oldRotation = ev.evt.gesture.rotation;
      group.scaleX(startScale * ev.evt.gesture.scale);
      group.scaleY(startScale * ev.evt.gesture.scale);
    });

    group.on('rotateend rotatecancel', function (ev) {
      group.to(Object.assign({}, originalAttrs));
      text.text(defaultText);
      group.draggable(true);
    });
  } catch (error) {
    console.error('初始化演示失败:', error);
  }
}

// 启动演示
initDemo();