移动端图可视化开发利器——手把手教你写F6

6,793 阅读12分钟

将知识、内容转换为图的形式进行归纳输出,成为越来越主流的方式。使用图可视化技术可以帮助我们更好的整理日常面对的大量信息。而手机,作为我们携带率最高的设备,天然的是一个优秀的图可视化输出载体。

现有的移动端图可视化技术有Antv旗下的F2、F6。F2主要专注于数据分析的统计图,而F6专注与各种场景的关系图。两者各有侧重。F6 是一个简单、易用、完备的移动端图可视化引擎,它在高定制能力的基础上,提供了一系列设计优雅、便于使用的图可视化解决方案。能帮助开发者搭建属于自己的图可视化、图分析、或图编辑器应用。如果您希望将内容通过流程图、知识图谱、思维导图等形式进行输出,并希望可以方便的实现对图的操控,那么建议您一定要尝试一下F6。

欢迎star和提交issue

github.com/antvis/F6

基本概念

对于图的分类,不同的角度会有不同的分类。我们从功能特点的角度,对图进行分类的话,可以分为以下几类

image.png

图的种类很多,但是都有一个共同的特点,就是都是由若干节点组成的。节点表示我们绘制的实体,而边表示实体之间的关系。举个例子,下图就是我们今天将要带大家绘制的环形弧线图。图中带英文的圆圈就是节点,表示一个特定的实体。而连接不同圆圈的弧线就是边,表示实体与实体间由联系。

所谓环形弧线图,环形,指节点围绕同一个圆心排列。而弧线,指节点与节点之间的边是弧线的形式表现。 截屏2021-09-18 下午2.51.24.png

如何使用F6绘制可视化图

那么我们应该如何通过F6来绘制这个看着很复杂的环形弧线图呢?首先可以想到,我们要给F6提供数据。我们要告诉F6,我们需要画哪些节点,哪些边,这些节点的名字,哪些节点之间有关系。具体在代码层面,我们需要提供以下结构的数据

{
  nodes:[],
  edges:[]
}
  • nodes是一个对象数组,里面的每一个对象都表示一个节点
  • edges是一个对象数组,里面的每一个对象都表示一条边

更具体一点,下面一段数据,就可以表示有“sea”和“water”两个节点,以及一条连接这两个节点的边。

{
  "nodes": [
    {
      "id":"water"
    },
    {
      "id":"sea"
    }
  ],
  "edges": [
    {
      "source":"water",
      "target":"sea"
    }
  ]
}

有了数据之后,我们就需要调用F6来进行绘图。这时候我们就需要引入数据,调用F6接口,并把数据给F6,让它帮我们画出想要的图。从代码层面的角度来看,我们需要在js文件中完成以下三件事:

  1. 新建实例:new F6.Graph( )来新建一个图的实例graph
  2. 传入数据:graph.data( ) 传入画图的数据
  3. 画图:graph.render( ) 进行绘制。

怎么说,是不是很简单!其实核心的部分也就在第一步,新建实例。在这一步需要传递一些参数,比如图是否居中,自定义点,自定义边,整个图的大小,使用的插件,布局等~,举个栗子🌰

this.graph = new F6.Graph({
  context: this.context,
  renderer: this.renderer,
  width,
  height,
  pixelRatio,
  fitView,
  modes: {
    default:['drag-canvas', 'drag-node']
  }
  defaultNode:{},
  defaultEdge:{},
  layout:{},
  plugin:[]
})

上面就是一个简单的新建图的实例的例子,参数看着有点多,但是每一个都非常好理解:

  • width和height就是设置画出来的图的宽和高
  • pixelRatio就是缩放比例
  • fitView, 适应屏幕
  • modes 图的拖拽模式,如可选拖拽canvas、拖拽节点
  • defaultnode:默认的节点样式
  • defaultEdge:默认的边样式
  • layout,设置整个图的布局
  • plugin,使用拓展的插件功能,如时间轴,鱼眼放大镜等拓展功能

新建好实例后,我们给这个实例传递我们前面写好的数据。然后再调用实例的render方法,就能够将我们想要的图画出来了!

前面介绍了使用F6画图的一些核心步骤。可以说看到这里,F6画图的流程你都以及掌握了。接下来我们就进入实战环节,将我们之前立的flag:环形弧线图给实现出来!

支付宝小程序上使用F6

我们以支付宝小程序为例,首先我们在支付宝小程序开发者工具上,新建一个支付宝小程序的应用: 选择支付宝小程序

image.png

选择一个最简单的空白模版,并为这个项目起一个响亮的名字,名字最好使用英文命名 image.png

完成之后,我们就能得到一个支付宝小程序的空白模版了。接着,我们去控制台,使用npm安装两个需要的包:@antv/f6@antv/f6-alipay

  • npm init
  • npm install @antv/f6
  • npm install @antv/f6-alipay

安装好之后,就可以开始写代码啦~

我们在pages/ 下新建一个文件夹 circularArcDiagram/。并且创建一些必要的文件。文件的结构如下:

pages/
    circularArcDiagram/
    index.js
    index.axml
    index.acss
    index.json
    data.js

绘制一个图大概需要的文件就是上面的5个文件。 没错,我们这次要画一个环形弧线图,最终的实现目录如下:

index.axml

我们首先来看一下index.axml。F6绘图是绘制在canavs上的,我们需要设定canvas画布的宽,高,放缩比例,回调函数等基本信息。

<f6-canvas
  width="{{width}}"
  height="{{height}}"
  forceMini="{{forceMini}}"
  pixelRatio="{{pixelRatio}}"
  onTouchEvent="handleTouch"
  onInit="handleInit"
></f6-canvas>

index.css

然后就是平平无奇的css,这个主要是控制页面的一个背景颜色和绘制出来的canvas图的padding

page {
  background-color: #f7f7f7;
  border: 1px solid rgba(0,0,0,0);
  box-sizing: border-box;
}

.page-section-demo {
  padding: 32rpx;
}

index.json

index.json: 这里主要是控制使用的组件,我们在axml中使用的就是在这里引入的。

{
  "defaultTitle": "环形弧线图",
  "usingComponents": {
    "f6-canvas": "@antv/f6-alipay/es/container/container"
  }
}

index.js

最后是控制主要逻辑的index.js文件,由于js文件是我们使用F6的大头,我们一点点说 首先是我们先前提到的,使用F6的三部曲,new、data、render

//new,新建F6实例
this.graph = new F6.Graph({
  context: this.ctx,
  renderer: this.renderer,
  width,
  height,
  pixelRatio,
  linkCenter: true,
  fitView: true,
  modes: {
    default: [
      {
        type: 'edge-tooltip',
        formatText: function formatText(model) {
          const text = `source: ${model.sourceName}<br/> target: ${model.targetName}`;
          return text;
        },
      },
    ],
  },
  defaultNode: {
    style: {
      opacity: 0.8,
      lineWidth: 1,
      stroke: '#999',
    },
  },
  defaultEdge: {
    size: 1,
    color: '#e2e2e2',
    style: {
      opacity: 0.6,
      lineAppendWidth: 3,
    },
  },
});

//data,处理数据
this.graph.data(data)

//render,绘图
this.render()

以上就是主体结构,完整的代码如下:

import F6 from '@antv/f6';
import data from './data';
import mds from '@antv/f6/dist/extends/layout/mdsLayout';
/**
 * 环形弧线图
 */
Page({
  canvas: null,
  ctx: null,
  renderer: '', // mini、mini-native等,F6需要,标记环境
  isCanvasInit: false, // canvas是否准备好了
  graph: null,
  
  data: {
    width: 375,
    height: 600,
    pixelRatio: 2,
    forceMini: false,
  },

  onLoad() {
    // 注册布局
    F6.registerLayout('mds', mds);
    // 同步获取window的宽高
    const { windowWidth, windowHeight, pixelRatio } = my.getSystemInfoSync();

    this.setData({
      width: windowWidth,
      height: windowHeight,
      pixelRatio,
    });
  },

  /**
   * 初始化cnavas回调,缓存获得的context
   * @param {*} ctx 绘图context
   * @param {*} rect 宽高信息
   * @param {*} canvas canvas对象,在render为mini时为null
   * @param {*} renderer 使用canvas 1.0还是canvas 2.0,mini | mini-native
   */
  handleInit(ctx, rect, canvas, renderer) {
    this.isCanvasInit = true;
    this.ctx = ctx;
    this.renderer = renderer;
    this.canvas = canvas;
    this.updateChart();
  },

  /**
   * canvas派发的事件,转派给graph实例
   */
  handleTouch(e) {
    this.graph && this.graph.emitEvent(e);
  },

  updateChart() {
    const { width, height, pixelRatio } = this.data;
    const colors = [
      'rgb(91, 143, 249)',
      'rgb(90, 216, 166)',
      'rgb(93, 112, 146)',
      'rgb(246, 189, 22)',
      'rgb(232, 104, 74)',
      'rgb(109, 200, 236)',
      'rgb(146, 112, 202)',
      'rgb(255, 157, 77)',
      'rgb(38, 154, 153)',
      'rgb(227, 137, 163)',
    ];

    // 创建F6实例
    this.graph = new F6.Graph({
      context: this.ctx,
      renderer: this.renderer,
      width,
      height,
      pixelRatio,
      linkCenter: true,
      fitView: true,
      modes: {
        default: [
          {
            type: 'edge-tooltip',
            formatText: function formatText(model) {
              const text = `source: ${model.sourceName}<br/> target: ${model.targetName}`;
              return text;
            },
          },
        ],
      },
      defaultNode: { //默认节点样式
        style: {
          opacity: 0.8,
          lineWidth: 1,
          stroke: '#999',
        },
      },
      defaultEdge: { //默认边样式
        size: 1,
        color: '#e2e2e2',
        style: {
          opacity: 0.6,
          lineAppendWidth: 3,
        },
      },
    });

    // 处理数据
    function scaleNodeProp(nodes, propName, refPropName, dataRange, outRange) {
      const outLength = outRange[1] - outRange[0];
      const dataLength = dataRange[1] - dataRange[0];
      nodes.forEach(function(n) {
        n[propName] = ((n[refPropName] - dataRange[0]) * outLength) / dataLength + outRange[0];
      });
    }

    const origin = [width / 2, height / 2];
    const radius = width < height ? width / 3 : height / 3;
    const { edges } = data;
    const { nodes } = data;
    const nodeMap = new Map();
    const clusterMap = new Map();
    let clusterId = 0;
    const n = nodes.length;
    const angleSep = (Math.PI * 2) / n;
    nodes.forEach(function(node, i) {
      const angle = i * angleSep;
      node.x = radius * Math.cos(angle) + origin[0];
      node.y = radius * Math.sin(angle) + origin[1];
      node.angle = angle;
      nodeMap.set(node.id, node);
      // cluster
      if (node.cluster && clusterMap.get(node.cluster) === undefined) {
        clusterMap.set(node.cluster, clusterId);
        clusterId++;
      }
      const id = clusterMap.get(node.cluster);
      if (node.style) {
        node.style.fill = colors[id % colors.length];
      } else {
        node.style = {
          fill: colors[id % colors.length],
        };
      }
      // label
      node.label = node.name;
      node.labelCfg = {
        position: 'center',
        style: {
          rotate: angle,
          rotateCenter: 'lefttop',
          textAlign: 'start',
        },
      };
    });
    edges.forEach((edge) => {
      edge.type = 'quadratic';
      const source = nodeMap.get(edge.source);
      const target = nodeMap.get(edge.target);
      edge.controlPoints = [
        {
          x: origin[0],
          y: origin[1],
        },
      ];
      edge.color = source.style.fill;
      edge.sourceName = source.name;
      edge.targetName = target.name;
    });

    // map the value to node size
    let maxValue = -9999;
    let minValue = 9999;
    nodes.forEach(function(k) {
      if (maxValue < k.value) maxValue = k.value;
      if (minValue > k.value) minValue = k.value;
    });
    const sizeRange = [3, 30];
    const sizeDataRange = [minValue, maxValue];
    scaleNodeProp(nodes, 'size', 'value', sizeDataRange, sizeRange);

    this.graph.data(data);
    this.graph.render();
    this.graph.fitView();
  },
});

data.json

最后,本次绘图使用的数据如下:

export default {
  nodes: [
    {
      id: '0',
      name: 'analytics.cluster',
      cluster: 'analytics',
      value: 21,
    },
    {
      id: '1',
      name: 'analytics.graph',
      cluster: 'analytics',
      value: 34,
    },
    {
      id: '2',
      name: 'analytics.optimization',
      cluster: 'analytics',
      value: 8,
    },
    {
      id: '3',
      name: 'animate',
      cluster: 'animate',
      value: 40,
    },
    {
      id: '4',
      name: 'animate.interpolate',
      cluster: 'animate',
      value: 18,
    },
    {
      id: '5',
      name: 'data.converters',
      cluster: 'data',
      value: 25,
    },
    {
      id: '6',
      name: 'data',
      cluster: 'data',
      value: 10,
    },
    {
      id: '7',
      name: 'display',
      cluster: 'display',
      value: 4,
    },
    {
      id: '8',
      name: 'flex',
      cluster: 'flex',
      value: 6,
    },
    {
      id: '9',
      name: 'physics',
      cluster: 'physics',
      value: 22,
    },
    {
      id: '10',
      name: 'query',
      cluster: 'query',
      value: 67,
    },
    {
      id: '11',
      name: 'query.methods',
      cluster: 'query',
      value: 71,
    },
    {
      id: '12',
      name: 'scale',
      cluster: 'scale',
      value: 33,
    },
    {
      id: '13',
      name: 'util',
      cluster: 'util',
      value: 23,
    },
    {
      id: '14',
      name: 'util.heap',
      cluster: 'util',
      value: 2,
    },
    {
      id: '15',
      cluster: 'util',
      name: 'util.math',
      value: 2,
    },
    {
      id: '16',
      name: 'util.palette',
      cluster: 'util',
      value: 5,
    },
    {
      id: '17',
      name: 'vis.axis',
      cluster: 'vis',
      value: 24,
    },
    {
      id: '18',
      name: 'vis.controls',
      cluster: 'vis',
      value: 28,
    },
    {
      id: '19',
      name: 'vis.data',
      cluster: 'vis',
      value: 70,
    },
    {
      id: '20',
      name: 'vis.data.render',
      cluster: 'vis',
      value: 11,
    },
    {
      id: '21',
      name: 'vis.events',
      cluster: 'vis',
      value: 8,
    },
    {
      id: '22',
      name: 'vis.legend',
      cluster: 'vis',
      value: 27,
    },
    {
      id: '23',
      name: 'vis.operator.distortion',
      cluster: 'vis',
      value: 9,
    },
    {
      id: '24',
      name: 'vis.operator.encoder',
      cluster: 'vis',
      value: 30,
    },
    {
      id: '25',
      name: 'vis.operator.filter',
      cluster: 'vis',
      value: 17,
    },
    {
      id: '26',
      name: 'vis.operator',
      cluster: 'vis',
      value: 27,
    },
    {
      id: '27',
      name: 'vis.operator.label',
      cluster: 'vis',
      value: 18,
    },
    {
      id: '28',
      name: 'vis.operator.layout',
      cluster: 'vis',
      value: 91,
    },
    {
      id: '29',
      name: 'vis',
      cluster: 'vis',
      value: 13,
    },
  ],
  edges: [
    {
      source: '10',
      target: '10',
      sourceWeight: 61,
      targetWeight: 61,
    },
    {
      source: '11',
      target: '11',
      sourceWeight: 39,
      targetWeight: 39,
    },
    {
      source: '3',
      target: '3',
      sourceWeight: 30,
      targetWeight: 30,
    },
    {
      source: '19',
      target: '19',
      sourceWeight: 26,
      targetWeight: 26,
    },
    {
      source: '13',
      target: '13',
      sourceWeight: 23,
      targetWeight: 23,
    },
    {
      source: '9',
      target: '9',
      sourceWeight: 22,
      targetWeight: 22,
    },
    {
      source: '12',
      target: '12',
      sourceWeight: 19,
      targetWeight: 19,
    },
    {
      source: '28',
      target: '19',
      sourceWeight: 34,
      targetWeight: 0,
    },
    {
      source: '4',
      target: '4',
      sourceWeight: 16,
      targetWeight: 16,
    },
    {
      source: '11',
      target: '10',
      sourceWeight: 32,
      targetWeight: 0,
    },
    {
      source: '28',
      target: '28',
      sourceWeight: 14,
      targetWeight: 14,
    },
    {
      source: '18',
      target: '18',
      sourceWeight: 12,
      targetWeight: 12,
    },
    {
      source: '26',
      target: '26',
      sourceWeight: 11,
      targetWeight: 11,
    },
    {
      source: '28',
      target: '13',
      sourceWeight: 20,
      targetWeight: 0,
    },
    {
      source: '5',
      target: '6',
      sourceWeight: 17,
      targetWeight: 2,
    },
    {
      source: '19',
      target: '13',
      sourceWeight: 17,
      targetWeight: 0,
    },
    {
      source: '17',
      target: '17',
      sourceWeight: 7,
      targetWeight: 7,
    },
    {
      source: '6',
      target: '6',
      sourceWeight: 7,
      targetWeight: 7,
    },
    {
      source: '12',
      target: '13',
      sourceWeight: 14,
      targetWeight: 0,
    },
    {
      source: '1',
      target: '19',
      sourceWeight: 14,
      targetWeight: 0,
    },
    {
      source: '5',
      target: '5',
      sourceWeight: 7,
      targetWeight: 7,
    },
    {
      source: '21',
      target: '19',
      sourceWeight: 6,
      targetWeight: 4,
    },
    {
      source: '25',
      target: '19',
      sourceWeight: 10,
      targetWeight: 0,
    },
    {
      source: '0',
      target: '0',
      sourceWeight: 5,
      targetWeight: 5,
    },
    {
      source: '3',
      target: '13',
      sourceWeight: 9,
      targetWeight: 0,
    },
    {
      source: '20',
      target: '19',
      sourceWeight: 5,
      targetWeight: 4,
    },
    {
      source: '19',
      target: '12',
      sourceWeight: 9,
      targetWeight: 0,
    },
    {
      source: '0',
      target: '19',
      sourceWeight: 8,
      targetWeight: 0,
    },
    {
      source: '24',
      target: '19',
      sourceWeight: 8,
      targetWeight: 0,
    },
    {
      source: '22',
      target: '22',
      sourceWeight: 4,
      targetWeight: 4,
    },
    {
      source: '24',
      target: '24',
      sourceWeight: 4,
      targetWeight: 4,
    },
    {
      source: '26',
      target: '3',
      sourceWeight: 7,
      targetWeight: 0,
    },
    {
      source: '24',
      target: '16',
      sourceWeight: 7,
      targetWeight: 0,
    },
    {
      source: '16',
      target: '16',
      sourceWeight: 3,
      targetWeight: 3,
    },
    {
      source: '10',
      target: '13',
      sourceWeight: 6,
      targetWeight: 0,
    },
    {
      source: '7',
      target: '7',
      sourceWeight: 3,
      targetWeight: 3,
    },
    {
      source: '22',
      target: '13',
      sourceWeight: 6,
      targetWeight: 0,
    },
    {
      source: '20',
      target: '20',
      sourceWeight: 3,
      targetWeight: 3,
    },
    {
      source: '1',
      target: '26',
      sourceWeight: 6,
      targetWeight: 0,
    },
    {
      source: '27',
      target: '19',
      sourceWeight: 6,
      targetWeight: 0,
    },
    {
      source: '28',
      target: '12',
      sourceWeight: 6,
      targetWeight: 0,
    },
    {
      source: '22',
      target: '7',
      sourceWeight: 6,
      targetWeight: 0,
    },
    {
      source: '28',
      target: '3',
      sourceWeight: 6,
      targetWeight: 0,
    },
    {
      source: '17',
      target: '7',
      sourceWeight: 5,
      targetWeight: 0,
    },
    {
      source: '26',
      target: '13',
      sourceWeight: 5,
      targetWeight: 0,
    },
    {
      source: '27',
      target: '13',
      sourceWeight: 5,
      targetWeight: 0,
    },
    {
      source: '1',
      target: '13',
      sourceWeight: 5,
      targetWeight: 0,
    },
    {
      source: '1',
      target: '3',
      sourceWeight: 5,
      targetWeight: 0,
    },
    {
      source: '26',
      target: '29',
      sourceWeight: 3,
      targetWeight: 2,
    },
    {
      source: '22',
      target: '16',
      sourceWeight: 5,
      targetWeight: 0,
    },
    {
      source: '18',
      target: '21',
      sourceWeight: 4,
      targetWeight: 0,
    },
    {
      source: '22',
      target: '12',
      sourceWeight: 4,
      targetWeight: 0,
    },
    {
      source: '23',
      target: '23',
      sourceWeight: 2,
      targetWeight: 2,
    },
    {
      source: '17',
      target: '29',
      sourceWeight: 2,
      targetWeight: 2,
    },
    {
      source: '28',
      target: '17',
      sourceWeight: 4,
      targetWeight: 0,
    },
    {
      source: '15',
      target: '15',
      sourceWeight: 2,
      targetWeight: 2,
    },
    {
      source: '17',
      target: '12',
      sourceWeight: 4,
      targetWeight: 0,
    },
    {
      source: '27',
      target: '27',
      sourceWeight: 2,
      targetWeight: 2,
    },
    {
      source: '14',
      target: '14',
      sourceWeight: 2,
      targetWeight: 2,
    },
    {
      source: '18',
      target: '29',
      sourceWeight: 3,
      targetWeight: 1,
    },
    {
      source: '25',
      target: '26',
      sourceWeight: 3,
      targetWeight: 0,
    },
    {
      source: '28',
      target: '9',
      sourceWeight: 3,
      targetWeight: 0,
    },
    {
      source: '27',
      target: '7',
      sourceWeight: 3,
      targetWeight: 0,
    },
    {
      source: '24',
      target: '12',
      sourceWeight: 3,
      targetWeight: 0,
    },
    {
      source: '17',
      target: '13',
      sourceWeight: 3,
      targetWeight: 0,
    },
    {
      source: '18',
      target: '13',
      sourceWeight: 3,
      targetWeight: 0,
    },
    {
      source: '20',
      target: '13',
      sourceWeight: 3,
      targetWeight: 0,
    },
    {
      source: '0',
      target: '13',
      sourceWeight: 3,
      targetWeight: 0,
    },
    {
      source: '24',
      target: '13',
      sourceWeight: 3,
      targetWeight: 0,
    },
    {
      source: '19',
      target: '6',
      sourceWeight: 3,
      targetWeight: 0,
    },
    {
      source: '29',
      target: '3',
      sourceWeight: 3,
      targetWeight: 0,
    },
    {
      source: '25',
      target: '3',
      sourceWeight: 3,
      targetWeight: 0,
    },
    {
      source: '24',
      target: '3',
      sourceWeight: 3,
      targetWeight: 0,
    },
    {
      source: '17',
      target: '3',
      sourceWeight: 3,
      targetWeight: 0,
    },
    {
      source: '0',
      target: '15',
      sourceWeight: 2,
      targetWeight: 0,
    },
    {
      source: '28',
      target: '26',
      sourceWeight: 2,
      targetWeight: 0,
    },
    {
      source: '24',
      target: '26',
      sourceWeight: 2,
      targetWeight: 0,
    },
    {
      source: '16',
      target: '13',
      sourceWeight: 2,
      targetWeight: 0,
    },
    {
      source: '1',
      target: '14',
      sourceWeight: 2,
      targetWeight: 0,
    },
    {
      source: '29',
      target: '21',
      sourceWeight: 2,
      targetWeight: 0,
    },
    {
      source: '21',
      target: '21',
      sourceWeight: 1,
      targetWeight: 1,
    },
    {
      source: '29',
      target: '19',
      sourceWeight: 2,
      targetWeight: 0,
    },
    {
      source: '19',
      target: '14',
      sourceWeight: 2,
      targetWeight: 0,
    },
    {
      source: '4',
      target: '13',
      sourceWeight: 2,
      targetWeight: 0,
    },
    {
      source: '19',
      target: '15',
      sourceWeight: 2,
      targetWeight: 0,
    },
    {
      source: '8',
      target: '17',
      sourceWeight: 2,
      targetWeight: 0,
    },
    {
      source: '2',
      target: '13',
      sourceWeight: 2,
      targetWeight: 0,
    },
    {
      source: '18',
      target: '19',
      sourceWeight: 2,
      targetWeight: 0,
    },
    {
      source: '1',
      target: '1',
      sourceWeight: 1,
      targetWeight: 1,
    },
    {
      source: '23',
      target: '17',
      sourceWeight: 2,
      targetWeight: 0,
    },
    {
      source: '23',
      target: '19',
      sourceWeight: 2,
      targetWeight: 0,
    },
    {
      source: '0',
      target: '3',
      sourceWeight: 2,
      targetWeight: 0,
    },
    {
      source: '18',
      target: '3',
      sourceWeight: 2,
      targetWeight: 0,
    },
    {
      source: '19',
      target: '3',
      sourceWeight: 2,
      targetWeight: 0,
    },
    {
      source: '29',
      target: '13',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '8',
      target: '29',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '21',
      target: '3',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '22',
      target: '3',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '3',
      target: '4',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '2',
      target: '29',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '22',
      target: '19',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '23',
      target: '3',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '2',
      target: '26',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '2',
      target: '19',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '26',
      target: '19',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '2',
      target: '17',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '27',
      target: '3',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '5',
      target: '13',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '2',
      target: '12',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '28',
      target: '20',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '18',
      target: '28',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '23',
      target: '21',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '8',
      target: '6',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '2',
      target: '3',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '1',
      target: '29',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '23',
      target: '28',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '6',
      target: '13',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '25',
      target: '13',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '8',
      target: '7',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '7',
      target: '13',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '27',
      target: '26',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '18',
      target: '7',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '0',
      target: '26',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '19',
      target: '7',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '8',
      target: '19',
      sourceWeight: 1,
      targetWeight: 0,
    },
    {
      source: '28',
      target: '29',
      sourceWeight: 1,
      targetWeight: 0,
    },
  ],
};

微信小程序

微信小程序和支付宝小程序的代码基本是一致的。对于在微信小程序上使用F6画图,大家可以参考我们写好的一些demos: github.com/antvis/F6/t…

对于更加丰富的图感兴趣,可以访问官网:f6.antv.vision/ 或者github:github.com/antvis/F6

欢迎加入我们

欢迎star和提交issue

github.com/antvis/F6

对于树图,或者图可视化感兴趣,可以添加我的微信 openwayne 进入我们的微信群。