g6一些交互

493 阅读1分钟

高亮点击节点及其关联项:

const { ActivateRelations, DragCanvas, DragNode, ClickSelect } = Behaviors;
<ActivateRelations trigger="hover" resetSelected />//高亮行为组件

item.setState('selected', true);可以改变节点状态,然后走注册节点的setState周期

当使用ActivateRelations行为后,点击的节点及其相关项会graph.setItemState(item, 'active', true);

其他项会graph.setItemState(item, 'inactive', true);

点击画布时会触发相反的状态改变

基于此可以自定义样式:

setState(name, value, item) {
  const group = item!.getContainer();
  const header = group.find((e) => e.get('name') === 'rect-headGroup');
  const content = group.find((e) => e.get('name') === 'rect-contentGroup');
  const activeColor = '#CF1322';
  if (value) {
    if (name === 'active') {
      header.attr({
        shadowBlur: 20,
        shadowColor: activeColor,
      });
      content.attr({
        shadowBlur: 20,
        shadowColor: activeColor,
      });
    } else if (name === 'inactive') {
      group.attr({
        opacity: 0.1,
      });
    } 
  } else {
    header?.attr({
      shadowColor: undefined,
    });
    content?.attr({
      shadowColor: undefined,
    });
    group.attr({
      opacity: 1,
    });
  }
},

提示框:

简单的做法,将需要触发toolTip的shape的name做一个标识:

maskGroup!.addShape('text', {
  attrs: {
    fill: '#fff',
    fontSize: 14,
    x: -55,
    y: 0,
    text: fittingString(id, 100, 14, 4),
    textAlign: 'left',
    textBaseline: 'middle',
    opacity: 1,
  },
  name: 'mask-showToolTip-shape',
  draggable: true,
});

然后注册:

export const tooltip = new G6.Tooltip({
  offsetX: 20,
  offsetY: 30,
  itemTypes: ['node'],
  getContent: (e) => {
    const nodeName = ToolTipMap[e?.target.get('name')];
    const model = e?.item?.getModel() as NodeCfg;
    const text = JSON.stringify(model?.[nodeName]) ?? '';
    return `${text}`;
  },
  shouldBegin: (e) => {
    if (e?.target.get('name').includes('showToolTip')) {
      return true;
    }
    return false;
  },
});

根据model和name的映射关系来触发:

export const ToolTipMap = {
  'text-code-showToolTip': 'id',
  'text-dataType-showToolTip': 'nodeType',
  'text-data-showToolTip': 'nodeData',
  'mask-showToolTip-shape': 'id',
};

触屏双指滑动:

G6.registerBehavior('double-finger-drag-canvas', {
  getEvents: function getEvents() {
    return {
      wheel: 'onWheel',
    };
  },
  onWheel: function onWheel(ev: IG6GraphEvent) {
    const graph = this.graph as Graph;
    
    if (ev.ctrlKey) {
      const canvas = graph.get('canvas');
      const point = canvas.getPointByClient(ev.clientX, ev.clientY);
      let ratio = graph.getZoom() as number;
      if (ev.wheelDelta > 0) {
        ratio = ratio + ratio * 0.05;
      } else {
        ratio = ratio - ratio * 0.05;
      }
      graph.zoomTo(ratio, {
        x: point.x,
        y: point.y,
      });
    } else {
      const x = (ev.deltaX || ev.movementX) as number;
      let y = (ev.deltaY || ev.movementY) as number;
      if (!y && navigator.userAgent.indexOf('Firefox') > -1) y = (-ev.wheelDelta * 125) / 3;
      graph.translate(-x, -y);
    }
    ev.preventDefault();
  },
});

注意点:
onWheel为滚动事件,当函数被当做监听事件处理函数时, 其 this 指向触发该事件的元素 graph

navigator.userAgent可以获得当前浏览器的信息。

G6 中有三个坐标系:clientX/clientY、canvasX/canvasY、pointX/pointY。

clientX/clientY 坐标系的原点都在左上角

canvasX/canvsY 的原点在 Container DOM 的左上角

节点的 (x, y) 等都是与 pointX/pointY 坐标系相对应的。

超长文本的换行:

G6可以识别\n的换行

/**
 * @description: 将当前一行的文本截取为对应行数的文本
 * @param str  当前字符
 * @param maxWidth  最大宽度
 * @param fontSize  字体大小
 * @param maxLine  需要转化为的行数
 * @param iterateNum 迭代次数
 */
export const fittingString = (
  str: string,
  maxWidth: number,
  fontSize: number,
  maxLine: number,
  iterateNum = 1,
) => {
  let currentWidth = 0;
  let res = str;
  if (iterateNum >= maxLine) {
    return truncate(str, { length: 16 });
  }
  const pattern = new RegExp('[\u4E00-\u9FA5]+');
  str.split('').forEach((letter, i) => {
    if (currentWidth > maxWidth) return;
    if (pattern.test(letter)) {
      currentWidth += fontSize;
    } else {
      currentWidth += G6.Util.getLetterWidth(letter, fontSize);
    }
    if (currentWidth > maxWidth) {
      res = `${str.substring(0, i)}\n${fittingString(
        str.substring(i),
        maxWidth,
        fontSize,
        maxLine,
        iterateNum + 1,
      )}`;
    }
  });
  return res;
};

全屏:

Element 中的方法

Element.requestFullscreen()

请求浏览器(user agent)将特定元素(甚至延伸到它的后代元素)置为全屏模式,隐去屏幕上的浏览器所有 UI 元素,以及其他应用。返回一个 Promise,并会在全屏模式被激活的时候变成 resolved 状态。

export const CardContext = createContext<HTMLDivElement | null>(null);
const cardRef = useRef<HTMLDivElement>();
<ProCard
  ref={cardRef}
  >
  <CardContext.Provider value={cardRef.current}>
    <GraphinShow
      data={graphData}
      />
  </CardContext.Provider>

将外层的dom元素传递进来,一般在Toolbar上添加全屏功能:

const cardRef = useContext(CardContext);
const handleFullscreen = () => {
  cardRef!.requestFullscreen();
};

注意在全屏时相当于最外层元素是设置的全屏元素,对于select之类的下拉框之前是挂在body下的,需要更改容器,否则全屏下不可见:

<Select
  getPopupContainer={() => cardRef!}
  />