fix: ci and remove issuehunt (#5277)

* fix: remove issue hunt

* chore: remote pnpm-lock file, and fix lint

* chore: update action

* chore: fix ci

* chore: remove coverall
This commit is contained in:
hustcc 2024-01-05 17:14:07 +08:00 committed by GitHub
parent 2d02bdc0de
commit 9ff0fd9944
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 446 additions and 47683 deletions

View File

@ -13,7 +13,6 @@ module.exports = {
'consistent-return': 0,
'lines-between-class-members': 0,
'class-methods-use-this': 0,
'lines-between-class-members': 0,
'no-multi-assign': 0,
'no-continue': 0,
'no-underscore-dangle': 0,

5
.github/FUNDING.yml vendored
View File

@ -1,5 +0,0 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
open_collective: antvis
issuehunt: antvis/G6

View File

@ -1,117 +0,0 @@
name: 'Issue Hunt Program'
description: Create a report to help us improve
body:
- type: checkboxes
id: issue_hunt_program
attributes:
label: Issue Hunt Program (optional)
description: |
The Issue Hunt Program is an incentive program proposed by AntV to encourage community contributors to participate in the AntV program. For more information, you can refer to the [Issue Hunt Program](https://github.com/antvis/G6/blob/master/ISSUEHUNT.en-US.md).
Issues submitted through the Issue Hunt Program are treated the same as regular issues. Regular issues can also be included in the Issue Hunt Program, but they need to be reviewed by the AntV team.
options:
- label: This issue will participate in the Issue Hunt Program
validations:
required: false
- type: dropdown
id: issue_hunt_difficulty
attributes:
label: Issue Difficulty
options:
- Low
- Medium
- High
validations:
required: false
- type: textarea
id: bounty_description
attributes:
label: Bounty
description: |
If you are not a program maintainer and are willing to offer a bounty for this issue, you can provide a bounty description here and provide the bounty amount on [IssueHunt](https://oss.issuehunt.io/r/antvis/G6/issues) after submitting the issue.
The platform may charge a certain fee, which is unrelated to AntV.
placeholder: |
Example: I will pay $100 if you can resolve this issue within 24 hours.
validations:
required: false
- type: markdown
attributes:
value: |
Thank you for reporting an issue :pray:.
This issue tracker is for reporting bugs found in G6 (https://github.com/antvis/G6).
If you have a question about how to achieve something and are struggling, please post a question
inside of G6's Discussion's tab: https://github.com/antvis/G6/discussions
Before submitting a new bug/issue, please check the links below to see if there is a solution or question posted there already:
- G6's Issue's tab: https://github.com/antvis/G6/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-desc
- G6's closed issues tab: https://github.com/antvis/G6/issues?q=is%3Aissue+sort%3Aupdated-desc+is%3Aclosed
- G6 Discussion's tab: https://github.com/antvis/G6/discussions
The more information you fill in, the better the community can help you.
- type: textarea
id: description
attributes:
label: Describe the bug
description: Provide a clear and concise description of the challenge you are running into.
validations:
required: true
- type: input
id: link
attributes:
label: Your Example Website or App
description: |
Which website or app were you using when the bug happened?
Note:
- Your bug will may get fixed much faster if we can run your code and it doesn't have dependencies other than the G6 npm package.
- To create a shareable code example you can use Stackblitz (https://stackblitz.com/) or CodeSandbox (https://codesandbox.io/s/new). Please no localhost URLs.
- Please read these tips for providing a minimal example: https://stackoverflow.com/help/mcve.
placeholder: |
e.g. Stackblitz, Code Sandbox app url
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to Reproduce the Bug or Issue
description: Describe the steps we have to take to reproduce the behavior.
placeholder: |
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
description: Provide a clear and concise description of what you expected to happen.
placeholder: |
As a user, I expected ___ behavior but i am seeing ___
validations:
required: true
- type: textarea
id: screenshots_or_videos
attributes:
label: Screenshots or Videos
description: |
If applicable, add screenshots or a video to help explain your problem.
For more information on the supported file image/file types and the file size limits, please refer
to the following link: https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/attaching-files
placeholder: |
You can drag your video or image files inside of this editor ↓
- type: textarea
id: platform
attributes:
label: Platform
value: |
- OS: [e.g. macOS, Windows, Linux]
- Browser: [e.g. Chrome, Safari, Firefox]
- Version: [e.g. 91.1]
validations:
required: true
- type: textarea
id: additional
attributes:
label: Additional context
description: Add any other context about the problem here.

View File

@ -1,101 +0,0 @@
name: 'Issue Hunt 计划'
description: 创建一个新的 issue如果你的 issue 不符合规范,它将会被自动关闭。
body:
- type: checkboxes
id: issue_hunt_program
attributes:
label: Issue Hunt 计划(可选)
description: |
Issue Hunt 计划是 AntV 为了鼓励社区贡献者参与到 AntV 项目中来,提出的一个激励计划。如果想了解更多,可以查看 [Issue Hunt 计划](https://github.com/antvis/G6/blob/master/ISSUEHUNT.md)
通过 Issue Hunt 计划提出的 issue 与普通 issue 无异。普通 issue 也可加入 Issue Hunt 计划,但需要经过 AntV 团队审核。
options:
- label: 我同意将这个 Issue 参与 Issue Hunt 计划
validations:
required: false
- type: dropdown
id: issue_hunt_difficulty
attributes:
label: Issue 难度
options:
- 低难度
- 中等难度
- 高难度
validations:
required: false
- type: textarea
id: bounty_description
attributes:
label: 悬赏
description: |
如果您非项目维护者,并愿意对该 issue 悬赏,可以在此处提供悬赏描述,并在提交 Issue 后在 [IssueHunt](https://oss.issuehunt.io/r/antvis/G6/issues) 提供悬赏金额。
平台可能会收取一定的手续费,这与 AntV 无关。
placeholder: |
例如:如果您能够在 24 小时内解决此问题,我将支付 $100。
validations:
required: false
- type: markdown
attributes:
value: |
在提交新 issue 之前,先通过以下链接查看有没有类似的 bug 或者建议:
- [G6 Issues](https://github.com/antvis/G6/issues)
- [G6 Closed Issues](https://github.com/antvis/G6/issues?q=is%3Aissue+is%3Aclosed)
- [G6 Discussions](https://github.com/antvis/G6/discussions)
- type: textarea
id: description
attributes:
label: 问题描述
description: 简洁清晰地描述你遇到的问题。
validations:
required: true
- type: input
id: link
attributes:
label: 重现链接
description: |
可以使用 CodeSandbox(https://codesandbox.io/s/new) 或者 StackBlitz(https://stackblitz.com/) 重现你的问题。
placeholder: |
示例: CodeSandBox 或者 StackBlitz URL
validations:
required: true
- type: textarea
id: steps
attributes:
label: 重现步骤
description: 简洁清晰的重现步骤能够帮助我们更迅速地定位问题所在。
placeholder: |
1.进入页面...
2.点击....
3.查看错误....
validations:
required: true
- type: textarea
id: expected
attributes:
label: 预期行为
description: 描述你期望的结果以及实际的结果。
placeholder: |
我期望看到...,但我看到了...
validations:
required: true
- type: textarea
id: platform
attributes:
label: 平台
value: |
- 操作系统: [macOS, Windows, Linux, React Native ...]
- 网页浏览器: [Google Chrome, Safari, Firefox]
- 版本: [例如 91.1]
validations:
required: true
- type: textarea
id: screenshots_or_videos
attributes:
label: 屏幕截图或视频(可选)
description: 可以添加屏幕截图或视频帮助你解释问题。
placeholder: |
可以将你的图片或者视频拖拽到此处↓
- type: textarea
id: additional
attributes:
label: 补充说明(可选)
description: 比如:遇到这个 bug 的业务场景、上下文。

53
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,53 @@
name: build
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
build:
runs-on: macOS-latest
steps:
- name: Checkout
uses: actions/checkout@v2.3.4
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 16
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 8
run_install: false
- uses: actions/cache@v3
name: Setup pnpm cache
id: cache
with:
path: |
node_modules/
packages/g6/node_modules/
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install Dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: pnpm install --no-frozen-lockfile
- name: Run CI
run: |
# pnpm run lint
# pnpm run test
pnpm run build
# - name: coverall
# if: success()
# uses: coverallsapp/github-action@master
# with:
# github-token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,45 +0,0 @@
name: Auto Reply to Issue Hunt Program
on:
issues:
types: [labeled]
jobs:
auto-reply:
runs-on: ubuntu-latest
steps:
- name: Comment on Issue
if: ${{ github.event.label.name == 'Reward/悬赏' }}
uses: peter-evans/create-or-update-comment@v3
with:
issue-number: ${{ github.event.issue.number }}
body: |
请复制以下模板,填写相关信息,然后删除模板中的注释。
Please copy the following template, fill in the relevant information, and then delete the comments in the template.
```template
## Issue 认领 / Issue Claim
**贡献者/Contributor**
> 例如:张三, 李四
> For example: John Smith, Sarah Johnson
**预计完成时间/Estimated Completion Date**
> 例如2023-06-06
> For example: May 30, 2023
**联系方式/Contact Information**
> 例如contact@email.com
> For example: contact@email.com
**其他信息/Additional Information**
> 例如:这个 issue 比预期的要复杂,我希望能获得更多的奖励
> For example: This issue is more complex than expected, and I hope to receive more reward.
```
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

1
.gitignore vendored
View File

@ -9,6 +9,7 @@ es
# lock
package-lock.json
pnpm-lock.yaml
# Runtime data
pids

View File

@ -1,19 +0,0 @@
# 不要修改该文件, 会自动生成, 详见 http://gitlab.alibaba-inc.com/node/ci
# 不要使用 ci 生成,直接按照 gitlab 文档自己写吧!
before_script:
- export PATH=$PWD/node_modules/.bin:$PWD/.node/bin:$PATH
- time enclose install tnpm:tnpm
- tnpm -v
stages:
- test
# job1
test:
image: reg.docker.alibaba-inc.com/dockerlab/node-ci:3.2.0
stage: test
script:
- env -u CI tnpm i --silent --internal-oss-cache --install-node=10
# - tnpm run ci ci 环境问题导致 electron 安装失败暂时跑不了
- tnpm run lint
tags:
- swarm

View File

@ -1,30 +0,0 @@
language: node_js
node_js:
- '12'
env:
- NODE_ENV=test
addons:
apt:
packages:
- xvfb
- libgconf-2-4
install:
- export DISPLAY=':99.0'
- Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
- yarn
script:
- |
if [ "$TEST_TYPE" = test ]; then
yarn ci && bash <(curl -s https://codecov.io/bash)
else
yarn $TEST_TYPE
fi
env:
matrix:
- TEST_TYPE=lint
- TEST_TYPE=test

View File

@ -1,75 +0,0 @@
# Issue Hunt Program
## What is the Issue Hunt Program?
The Issue Hunt Program is an incentive program proposed by AntV to encourage community contributors to participate in the AntV project.
## How to Participate in Issue Hunt?
1. Find an issue that interests you. (issue with 'Reward/悬赏' tag)
2. Contribute code to the issue.
3. Earn rewards.
## How to Find Issues You Like?
- Find issues that interest you on [Issue Hunt](https://oss.issuehunt.io/r/antvis/G6/issues)
- Find issues you like on [GitHub](https://github.com/antvis/G6/issues?q=is%3Aopen+is%3Aissue+label%3AReward%2F%E6%82%AC%E8%B5%8F)
## Set Rewards for Issues
Both project maintainers and community contributors can set rewards for issues.
If you are a project maintainer, you can set the reward amount for an issue by using a label. For example:
`Reward/悬赏`, `$10` means the reward amount for this issue is $10.
If you are a community contributor, you can reward the issue **you proposed** or the issue **you want to be resolved** on [Issue Hunt](https://oss.issuehunt.io/r/antvis/G6/issues).
> ⚠️ Issue Hunt platform may charge a certain fee.。
## Reward Amounts
The reward amounts for project maintainers are divided into three tiers:
- **Low Difficulty**`$0.5` - `$5`
- Simple question replies, documentation modifications, issue resolutions, new feature development
- Workload within 2 hours (Evaluated by the project maintainer)
- **Medium Difficulty**`$5` - `$10`
- Requires some code modifications
- Workload within 2 day
- **High Difficulty**`$10` - `$50` (no upper limit in principle)
- Requires extensive code modifications
- Fixes critical and urgent issues
- Complex new feature development
- Workload within 2 week
## Reward Distribution
If a reward is initiated through the Issue Hunt platform, Issue Hunt will distribute the reward amount to the contributor's account within 7 days after the issue is closed.
If a reward is initiated by a project maintainer through GitHub, we will distribute rewards to closed issues based on the [Milestone](https://github.com/antvis/G6/milestones) schedule. Prior to distribution, we will contact the contributor to confirm the reward amount and distribution method.
## Issue Claiming
To avoid multiple people claiming the same issue simultaneously, you can leave a comment below an issue marked with `Reward/悬赏`. The project maintainer will reply within 24 hours to confirm if you can claim the issue and add the `Claimed/已认领` label to the issue.
Your claiming comment should include the following information (copy and edit the template to reply):
```template
## Issue Claiming
**Contributors**
> For example: John Smith, Sarah Johnson
**Estimated Completion Date**
> For example: May 30, 2023
**Contact Information**
> For example: contact@email.com
**Additional Information**
> For example: This issue is more complex than expected, and I hope to receive more reward.
```

View File

@ -1,74 +0,0 @@
# Issue Hunt 计划
## 什么是 Issue Hunt 计划?
Issue Hunt 计划是 AntV 为了鼓励社区贡献者参与到 AntV 项目中来,提出的一个激励计划。
## 如何参与 Issue Hunt?
1. 找到你感兴趣的 issue (带有 `Reward/悬赏` 标签的 issue)
2. 为 issue 贡献代码
3. 获得奖励
## 如何找到你喜欢的 Issues?
1. 从 [Issue Hunt](https://oss.issuehunt.io/r/antvis/G6/issues) 上找到你感兴趣的 issues
2. 从 [GitHub](https://github.com/antvis/G6/issues?q=is%3Aopen+is%3Aissue+label%3AReward%2F%E6%82%AC%E8%B5%8F) 上找到你喜欢的 issues
## 为 Issues 设置悬赏
项目维护者和社区人员都可以为 issue 设置悬赏。
如果你是项目维护者,你可以在 Issue 上以 Label 的形式设置奖励金额,例如:
`Reward/悬赏`, `$10` 表示这个 Issue 的奖励金额为 10 美元。
如果你是社区工作者,你可以到[Issue Hunt](https://oss.issuehunt.io/r/antvis/G6/issues)上为**你提出的**/**你希望尽快解决的** issue 进行悬赏。
> ⚠️ Issue Hunt 平台可能会收取一定的手续费。
## 激励金额
项目维护者的悬赏金额分为三档:
- **低难度**`$0.5` - `$5`
- 简单的问题答复、文档修改、问题解决、新特性开发
- 工作量在 2 小时以内(由项目维护者评估)
- **中等难度**`$5` - `$10`
- 需要一定的代码修改
- 工作量在 2 天以内
- **高难度**`$10` - `$50` (原则上不设上限)
- 需要大量的代码修改
- 严重且紧急的问题修复
- 复杂的新特性开发
- 工作量在 2 周以内
## 激励发放
如果是通过 Issue Hunt 平台发起的悬赏Issue Hunt 会在 issue 被关闭后 7 天内将奖励金额发放到贡献者的账户上。
如果是项目维护者通过 GitHub 发起的悬赏,我们会根据 [Milestone](https://github.com/antvis/G6/milestones) 的周期对关闭的 issues 进行奖励发放。发放前我们会联系贡献者确认奖励金额及发放方式。
## 项目认领
为了避免多人同时认领同一个 issue你可以在被标记了 `Reward/悬赏` 的 issue 下方留言,项目维护者会在 24 小时内回复你是否可以认领该 issue并在 issue 上添加 `Claimed/已认领` 标签。
你的认领留言应包含以下信息(请复制后编辑回复)
```template
## Issue 认领
**贡献者**
> 例如:张三, 李四
**预计完成时间**
> 例如2023-06-06
**联系方式**
> 例如contact@email.com
**其他信息**
> 例如:这个 issue 比预期的要复杂,我希望能获得更多的奖励
```

View File

@ -6,7 +6,13 @@
![](https://user-images.githubusercontent.com/6113694/45008751-ea465300-b036-11e8-8e2a-166cbb338ce2.png)
[![travis-ci](https://img.shields.io/travis/antvis/g6/master.svg)](https://travis-ci.org/antvis/g6) [![codecov](https://codecov.io/gh/antvis/G6/branch/master/graph/badge.svg)](https://codecov.io/gh/antvis/G6) ![typescript](https://img.shields.io/badge/language-typescript-red.svg) ![MIT](https://img.shields.io/badge/license-MIT-000000.svg) [![npm package](https://img.shields.io/npm/v/@antv/g6.svg)](https://www.npmjs.com/package/@antv/g6) [![NPM downloads](http://img.shields.io/npm/dm/@antv/g6.svg)](https://npmjs.org/package/@antv/g6) [![Percentage of issues still open](http://isitmaintained.com/badge/open/antvis/g6.svg)](http://isitmaintained.com/project/antvis/g6 'Percentage of issues still open')
[![build](https://github.com/antvis/G6/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/antvis/G6/actions/)
[![Coverage Status](https://coveralls.io/repos/github/antvis/G6/badge.svg?branch=master)](https://coveralls.io/github/antvis/G6?branch=master)
![typescript](https://img.shields.io/badge/language-typescript-red.svg)
![MIT](https://img.shields.io/badge/license-MIT-000000.svg)
[![npm package](https://img.shields.io/npm/v/@antv/g6.svg)](https://www.npmjs.com/package/@antv/g6)
[![NPM downloads](http://img.shields.io/npm/dm/@antv/g6.svg)](https://npmjs.org/package/@antv/g6)
[![Percentage of issues still open](http://isitmaintained.com/badge/open/antvis/g6.svg)](http://isitmaintained.com/project/antvis/g6)
## What is G6
@ -143,12 +149,6 @@ For React project integration, we have an independent product recommendation: [G
At present, Graphin has good practices in business graph analysis projects. For details, see [《Who uses Graphin》](https://github.com/antvis/Graphin/issues/212)
## Online Analysis ToolG6VP
If you have a piece of relational data (graph data) and want to quickly visualize it online and analyze it efficiently, then we recommend using the official [G6VP](https://github.com/antvis/G6VP), which supports local File JSON, Excel, and graph data sources such as TuGraph, Neo4J, and GraphScope are also supported. With 60+ built-in analysis assets, graph analysis can be as simple as building blocks. The platform also provides one-click export of SDK, which can be quickly integrated into the business system
![G6VP Image](https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*GOVySaZ1iHYAAAAAAAAAAAAADmJ7AQ/original)
## G6 Communication Group
Welcome to join the **G6 Communication Group**. We also welcome the github issues.

View File

@ -6,7 +6,13 @@
![](https://user-images.githubusercontent.com/6113694/45008751-ea465300-b036-11e8-8e2a-166cbb338ce2.png)
[![travis-ci](https://img.shields.io/travis/antvis/g6.svg)](https://travis-ci.org/antvis/g6) [![codecov](https://codecov.io/gh/antvis/G6/branch/master/graph/badge.svg)](https://codecov.io/gh/antvis/G6) ![typescript](https://img.shields.io/badge/language-typescript-red.svg) ![MIT](https://img.shields.io/badge/license-MIT-000000.svg) [![npm package](https://img.shields.io/npm/v/@antv/g6.svg)](https://www.npmjs.com/package/@antv/g6) [![NPM downloads](http://img.shields.io/npm/dm/@antv/g6.svg)](https://npmjs.org/package/@antv/g6) [![Percentage of issues still open](http://isitmaintained.com/badge/open/antvis/g6.svg)](http://isitmaintained.com/project/antvis/g6 'Percentage of issues still open')
[![build](https://github.com/antvis/G6/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/antvis/G6/actions/)
[![Coverage Status](https://coveralls.io/repos/github/antvis/G6/badge.svg?branch=master)](https://coveralls.io/github/antvis/G6?branch=master)
![typescript](https://img.shields.io/badge/language-typescript-red.svg)
![MIT](https://img.shields.io/badge/license-MIT-000000.svg)
[![npm package](https://img.shields.io/npm/v/@antv/g6.svg)](https://www.npmjs.com/package/@antv/g6)
[![NPM downloads](http://img.shields.io/npm/dm/@antv/g6.svg)](https://npmjs.org/package/@antv/g6)
[![Percentage of issues still open](http://isitmaintained.com/badge/open/antvis/g6.svg)](http://isitmaintained.com/project/antvis/g6)
## 什么是 G6
@ -145,12 +151,6 @@ DEBUG_MODE=1 npm test -- --watch ./tests/unit/algorithm/find-path-spec
目前 Graphin 在商业图分析项目中均有良好的实践,具体查看[《谁在使用 Graphin》](https://github.com/antvis/Graphin/issues/212)
## 在线分析工具 G6VP
如果你有一份关系数据(图数据),想要快速在线进行可视化,并能够高效分析,那么我们推荐使用官方出品的 [G6VP](https://github.com/antvis/G6VP),它支持本地文件 JSONExcel也支持 TuGraphNeo4JGraphScope 等图数据源,内置了 60+ 的分析资产,图分析可以像搭积木一样简单。平台还提供一键导出 SDK快速集成到业务系统中大大降 低初始研发门槛 与 后续维护成本。
![G6VP Image](https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*GOVySaZ1iHYAAAAAAAAAAAAADmJ7AQ/original)
## G6 图可视化交流群
欢迎各界 G6 使用者、图可视化爱好者加入 **G6 图可视化交流群****G6 图可视化交流二群**钉钉群使用钉钉扫一扫加入讨论与交流。Graphin 的使用者,爱好者请加入 **Graphin's Group Chat**

View File

@ -44,34 +44,34 @@
]
},
"dependencies": {
"eslint": "^7.11.0",
"eslint-config-prettier": "^6.7.0",
"eslint-plugin-import": "^2.22.1",
"husky": "^4.2.5",
"lint-staged": "^10.2.2",
"eslint": "^7.32.0",
"eslint-config-prettier": "^6.15.0",
"eslint-plugin-import": "^2.29.1",
"husky": "^4.3.8",
"lint-staged": "^10.5.4",
"monaco-editor": "0.29.1",
"monaco-editor-webpack-plugin": "5.0.0",
"normalize-url": "^7.0.3",
"normalize-url": "^7.2.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.1.2",
"pretty-quick": "^3.0.2",
"prettier": "^2.8.8",
"pretty-quick": "^3.1.3",
"react-monaco-editor": "0.40.0",
"rimraf": "^3.0.0",
"rimraf": "^3.0.2",
"tslint": "^6.1.3",
"tslint-config-airbnb": "^5.11.2",
"tslint-config-prettier": "^1.18.0",
"tslint-eslint-rules": "^5.4.0",
"typescript": "^5.3.2"
"typescript": "^5.3.3"
},
"devDependencies": {
"@changesets/cli": "^2.26.2",
"@types/react": "^16.9.35",
"@types/react-dom": "^16.9.8",
"@umijs/fabric": "^2.3.1",
"@changesets/cli": "^2.27.1",
"@types/react": "^16.14.54",
"@types/react-dom": "^16.9.24",
"@umijs/fabric": "^2.14.1",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-react": "^7.22.0",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-unicorn": "^27.0.0",
"pre-commit": "^1.2.2",
"react-scripts": "3.1.2"

View File

@ -61,7 +61,7 @@ export default {
unbind(graph: IAbstractGraph) {
const { events } = this;
let draggable = graph.get('canvas').get('draggable');
const draggable = graph.get('canvas').get('draggable');
if (
this.type === 'drag-canvas' ||
this.type === 'brush-select' ||

View File

@ -346,7 +346,7 @@ const singleNode: ShapeOptions = {
name,
});
} else {
let { width: w, height: h } = icon;
const { width: w, height: h } = icon;
group['shapeMap'][name] = group.addShape('image', {
attrs: {
...icon,

View File

@ -115,7 +115,7 @@ export default class ViewController {
realRatio = maxZoom;
console.warn('fitview failed, ratio out of range, ratio: %f', ratio, 'graph maxzoom has been used instead');
}
let zoomedMatrix = transform(translatedMatrix, [
const zoomedMatrix = transform(translatedMatrix, [
['t', -viewCenter.x, -viewCenter.y],
['s', realRatio, realRatio],
['t', viewCenter.x, viewCenter.y],
@ -425,7 +425,7 @@ export default class ViewController {
const startMatrix = group.getMatrix() || [1, 0, 0, 0, 1, 0, 0, 0, 1];
group.resetMatrix();
let bbox: BBox = {
const bbox: BBox = {
x: 0, y: 0,
minX: Number.MAX_SAFE_INTEGER, minY: Number.MAX_SAFE_INTEGER,
maxX: Number.MIN_SAFE_INTEGER, maxY: Number.MIN_SAFE_INTEGER,

View File

@ -2718,8 +2718,8 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
edges.forEach(edge => {
const { isVEdge, size = 1 } = edge.getModel();
if (edge.isVisible() && !isVEdge) return;
let source = edge.getSource();
let target = edge.getTarget();
const source = edge.getSource();
const target = edge.getTarget();
let otherEnd = null;
let otherEndIsSource;
if (source.getModel().id === comboModel.id ||
@ -2827,10 +2827,10 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
const addedVEdgeMap = {};
edges.forEach(edge => {
if (edge.isVisible() && !edge.getModel().isVEdge) return;
let source = edge.getSource();
let target = edge.getTarget();
let sourceId = source.get('id');
let targetId = target.get('id');
const source = edge.getSource();
const target = edge.getTarget();
const sourceId = source.get('id');
const targetId = target.get('id');
let otherEnd = null;
let otherEndIsSource;
if (sourceId === comboModel.id ||

View File

@ -265,7 +265,7 @@ export default class Node extends Item implements INode {
))
) return 'bbox|label';
let updateLabel = keys.includes('label') || keys.includes('labelCfg');
const updateLabel = keys.includes('label') || keys.includes('labelCfg');
return updateLabel ? 'style|label' : 'style';
}

View File

@ -83,8 +83,8 @@ export const getLoopCfgs = (cfg: EdgeData): EdgeData => {
let startPoint = [cfg.startPoint.x, cfg.startPoint.y];
let endPoint = [cfg.endPoint.x, cfg.endPoint.y];
let halfOfHeight = bbox.height / 2;
let halfOfWidth = bbox.width / 2;
const halfOfHeight = bbox.height / 2;
const halfOfWidth = bbox.width / 2;
let rstart = halfOfHeight;
let rend = halfOfHeight;

View File

@ -841,9 +841,9 @@ export const lerp = (start: number, end: number, alpha: number): number => {
* @returns {number[]}
*/
export const lerpArray = (start: number[], end: number[], alpha: number): number[] => {
var len = Math.min(start.length, end.length);
const len = Math.min(start.length, end.length);
const out = new Array(len);
for (var i = 0; i < len; i++) {
for (let i = 0; i < len; i++) {
out[i] = lerp(start[i], end[i], alpha);
}
return out;

View File

@ -336,6 +336,88 @@ export const getNeighborPoints = (
});
return filterConnectPoints(neighbors);
};
/**
* sorted array ascendly
* add new item to proper index when calling add
*/
export class SortedArray {
public arr: {
id: string;
value: number;
}[] = [];
private map: {
[id: string]: boolean;
} = {};
constructor() {
this.arr = [];
this.map = {};
}
private _innerAdd(item, length) {
const idxRange = [0, length - 1];
while (idxRange[1] - idxRange[0] > 1) {
const midIdx = Math.floor((idxRange[0] + idxRange[1]) / 2);
if (this.arr[midIdx].value > item.value) {
idxRange[1] = midIdx;
} else if (this.arr[midIdx].value < item.value) {
idxRange[0] = midIdx;
} else {
this.arr.splice(midIdx, 0, item);
this.map[item.id] = true;
return;
}
}
this.arr.splice(idxRange[1], 0, item);
this.map[item.id] = true;
}
public add(item) {
// 已经存在,先移除
delete this.map[item.id];
const length = this.arr.length;
if (!length) {
this.arr.push(item);
this.map[item.id] = true;
return;
}
// 比最后一个大,加入尾部
if (this.arr[length - 1].value < item.value) {
this.arr.push(item);
this.map[item.id] = true;
return;
}
this._innerAdd(item, length);
}
// only remove from the map to avoid cost
// clear the invalid (not in the map) item when calling minId(true)
public remove(id) {
if (!this.map[id]) return;
delete this.map[id];
}
private _clearAndGetMinId() {
let res;
for (let i = this.arr.length - 1; i >= 0; i--) {
if (this.map[this.arr[i].id]) res = this.arr[i].id;
else this.arr.splice(i, 1);
}
return res;
}
private _findFirstId() {
while (this.arr.length) {
const first = this.arr.shift();
if (this.map[first.id]) return first.id;
}
}
public minId(clear) {
if (clear) {
return this._clearAndGetMinId();
} else {
return this._findFirstId();
}
}
}
export const pathFinder = (
points: PolyPoint[],
start: PolyPoint,
@ -348,7 +430,7 @@ export const pathFinder = (
// A-Star Algorithm
const closedSet = [];
const openSet = {
[start.id]: start
[start.id]: start,
};
const cameFrom: {
[key: string]: any;
@ -367,8 +449,8 @@ export const pathFinder = (
const sortedOpenSet = new SortedArray();
sortedOpenSet.add({
id: start.id,
value: fScore[start.id]
})
value: fScore[start.id],
});
const pointById: {
[key: string]: PolyPoint;
@ -381,12 +463,12 @@ export const pathFinder = (
let current;
while (Object.keys(openSet).length) {
const minId = sortedOpenSet.minId(false);
if (minId) {
current = openSet[minId];
} else {
break;
}
if (minId) {
current = openSet[minId];
} else {
break;
}
// 若 openSet 中 fScore 最小的点就是终点
if (current === goal) {
// ending condition
@ -405,31 +487,31 @@ export const pathFinder = (
if (closedSet.indexOf(neighbor) !== -1) {
return;
}
const neighborId = neighbor.id;
if (!openSet[neighborId]) {
openSet[neighborId] = neighbor;
}
const tentativeGScore = fScore[current.id] + distance(current, neighbor); // + distance(neighbor, goal);
if (gScore[neighborId] && tentativeGScore >= gScore[neighborId]) {
sortedOpenSet.add({
id: neighborId,
value: fScore[neighborId]
})
value: fScore[neighborId],
});
return;
}
cameFrom[neighborId] = current.id;
gScore[neighborId] = tentativeGScore;
fScore[neighborId] =
gScore[neighborId] + heuristicCostEstimate(neighbor, goal, start, os, ot);
sortedOpenSet.add({
id: neighborId,
value: fScore[neighborId]
})
value: fScore[neighborId],
});
});
}
};
iterateNeighbors(neighborPoints);
}
@ -512,11 +594,11 @@ export const getPolylinePoints = (
maxX: sKeyShapeBBox.maxX + sx,
minY: sKeyShapeBBox.minY + sy,
maxY: sKeyShapeBBox.maxY + sy,
}
};
sBBox.centerX = (sBBox.minX + sBBox.maxX) / 2;
sBBox.centerY = (sBBox.minY + sBBox.maxY) / 2;
} else {
sBBox = getBBoxFromPoint(start) as PBBox
sBBox = getBBoxFromPoint(start) as PBBox;
}
} else {
sBBox = sNode && sNode.getBBox();
@ -537,11 +619,11 @@ export const getPolylinePoints = (
maxX: tKeyShapeBBox.maxX + tx,
minY: tKeyShapeBBox.minY + ty,
maxY: tKeyShapeBBox.maxY + ty,
}
};
tBBox.centerX = (tBBox.minX + tBBox.maxX) / 2;
tBBox.centerY = (tBBox.minY + tBBox.maxY) / 2;
} else {
tBBox = getBBoxFromPoint(end) as PBBox
tBBox = getBBoxFromPoint(end) as PBBox;
}
} else {
tBBox = tNode && tNode.getBBox();
@ -614,15 +696,15 @@ export const getPolylinePoints = (
/**
* x y y x
* @param points { x: number, y: number, id: string }[]
* @returns
* @returns
*/
export const removeRedundantPoint = (points) => {
if (!points?.length) return points;
const beginPoint = points[points.length - 1];
const current = {
x: beginPoint.x,
y: beginPoint.y
}
y: beginPoint.y,
};
let continueSameX = [beginPoint];
let continueSameY = [beginPoint];
for (let i = points.length - 2; i >= 0; i--) {
@ -651,86 +733,4 @@ export const removeRedundantPoint = (points) => {
}
}
return points;
}
/**
* sorted array ascendly
* add new item to proper index when calling add
*/
export class SortedArray {
public arr: {
id: string,
value: number
}[] = [];
private map: {
[id: string]: boolean
} = {};
constructor() {
this.arr = [];
this.map = {};
}
private _innerAdd(item, length) {
const idxRange = [0, length - 1];
while (idxRange[1] - idxRange[0] > 1) {
const midIdx = Math.floor((idxRange[0] + idxRange[1]) / 2);
if (this.arr[midIdx].value > item.value) {
idxRange[1] = midIdx;
} else if (this.arr[midIdx].value < item.value) {
idxRange[0] = midIdx;
} else {
this.arr.splice(midIdx, 0, item);
this.map[item.id] = true;
return;
}
}
this.arr.splice(idxRange[1], 0, item);
this.map[item.id] = true;
}
public add(item) {
// 已经存在,先移除
delete this.map[item.id];
const length = this.arr.length;
if (!length) {
this.arr.push(item);
this.map[item.id] = true;
return;
}
// 比最后一个大,加入尾部
if (this.arr[length - 1].value < item.value) {
this.arr.push(item);
this.map[item.id] = true;
return;
}
this._innerAdd(item, length);
}
// only remove from the map to avoid cost
// clear the invalid (not in the map) item when calling minId(true)
public remove(id) {
if (!this.map[id]) return;
delete this.map[id];
}
private _clearAndGetMinId(){
let res;
for (let i = this.arr.length - 1; i >= 0; i--) {
if (this.map[this.arr[i].id]) res = this.arr[i].id;
else this.arr.splice(i, 1);
}
return res;
}
private _findFirstId() {
while (this.arr.length) {
const first = this.arr.shift();
if (this.map[first.id]) return first.id;
}
}
public minId(clear) {
if (clear) {
return this._clearAndGetMinId();
} else {
return this._findFirstId();
}
}
}
};

View File

@ -5,7 +5,7 @@ import {
NodeConfig,
BaseGlobal as Global,
UpdateType,
Util
Util,
} from '@antv/g6-core';
import { deepMix } from '@antv/util';
@ -14,37 +14,256 @@ const { defaultSubjectColors } = Util;
const FAN_NAME_PREFIX = 'fan-shape-';
type DonutAttrs = {
[propKey: string]: number
[propKey: string]: number;
};
type DonutColorMap = {
[propKey: string]: string
[propKey: string]: string;
};
interface DonutNodeConfig extends NodeConfig {
// values for fan shapes on the donut
donutAttrs?: DonutAttrs,
donutAttrs?: DonutAttrs;
// assign the color for a fan, propKey corresponds to the keys in donutAttrs. If not assigned, default palette will be used
donutColorMap?: DonutColorMap
donutColorMap?: DonutColorMap;
}
type FanValue = {
key: string; // key of the fan, came from the key of corresponding property of donutAttrs
value: number; // format number value of the single fan
color: string; // color from corresponding position of donutColorMap
}
};
type FanConfig = {
arcR: number; // the radius of the fan
arcBegin: [number, number]; // the beginning position of the arc
beginAngle: number; // the beginning angle of the arc
config: FanValue, // value and color of the fan
config: FanValue; // value and color of the fan
fanIndex: number; // the index of the fan at the donut fans array
lineWidth: number; // the line width for the arc path
totalValue: number; // the total value of the donut configs
drawWhole?: boolean; // whether draw a arc with radius 2*PI to represent a circle
updateShape?: IShape; // the shape to be updated, if not assgined, draw a new fan shape
}
};
/**
* calculate the total value and format single value for each fan
* @param donutAttrs
* @param donutColorMap
* @returns
*/
const getDonutConfig = (
donutAttrs: DonutAttrs,
donutColorMap: DonutColorMap,
): {
totalValue: number;
configs: FanValue[];
} => {
let totalValue = 0;
const configs = [];
Object.keys(donutAttrs).forEach((name) => {
const value = +donutAttrs[name];
if (isNaN(value)) return;
configs.push({
key: name,
value,
color: donutColorMap[name],
});
totalValue += value;
});
return { totalValue, configs };
};
/**
* calculate the lineWidth and radius for fan shapes according to the keyShape's radius
* @param keyShape
* @returns
*/
const getDonutSize = (
keyShape: IShape,
): {
lineWidth: number;
arcR: number;
} => {
const keyShapeR = keyShape.attr('r');
const innerR = 0.6 * keyShapeR; // 甜甜圈的内环半径
const arcR = (keyShapeR + innerR) / 2; // 内环半径与外环半径的平均值
const lineWidth = keyShapeR - innerR;
return { lineWidth, arcR };
};
/**
* draws one fan shape and returns the next position and angle
* @param group
* @param fanConfig
* @returns
*/
const drawFan = (
group: IGroup,
fanConfig: FanConfig,
): {
beginAngle: number; // next begin iangle
arcBegin: [number, number]; // next begin position
shape: IShape | undefined; // shape added by this function
shouldEnd: boolean; // finish fans drawing
} => {
const {
arcR,
arcBegin,
beginAngle,
config,
fanIndex,
lineWidth,
totalValue,
drawWhole = false,
updateShape = undefined,
} = fanConfig;
const percent = config.value / totalValue;
if (percent < 0.001) {
// too small to add a fan
return {
beginAngle,
arcBegin,
shape: undefined,
shouldEnd: false,
};
}
let arcEnd, endAngle, isBig;
// draw a path represents the whole circle, or the percentage is close to 1
if (drawWhole || percent > 0.999) {
arcEnd = [arcR, 0.0001]; // [arcR * cos(2 * PI), -arcR * sin(2 * PI)]
isBig = 1;
} else {
const angle = percent * Math.PI * 2;
endAngle = beginAngle + angle;
arcEnd = [arcR * Math.cos(endAngle), -arcR * Math.sin(endAngle)];
isBig = angle > Math.PI ? 1 : 0;
}
const style = {
path: [
['M', arcBegin[0], arcBegin[1]],
['A', arcR, arcR, 0, isBig, 0, arcEnd[0], arcEnd[1]],
],
stroke:
config.color ||
updateShape?.attr('stroke') ||
defaultSubjectColors[fanIndex % defaultSubjectColors.length],
lineWidth,
};
if (updateShape) {
// update
updateShape.attr(style);
} else {
// draw
group['shapeMap'][`${FAN_NAME_PREFIX}${fanIndex}`] = group.addShape('path', {
attrs: style,
name: `${FAN_NAME_PREFIX}${fanIndex}`,
draggable: true,
});
}
return {
beginAngle: endAngle,
arcBegin: arcEnd,
shape: group['shapeMap'][`${FAN_NAME_PREFIX}${fanIndex}`],
shouldEnd: drawWhole || percent > 0.999,
};
};
/**
* draws the fan shapes
* @param cfg
* @param group
* @param keyShape
* @returns
*/
const drawFans = (cfg: DonutNodeConfig, group: IGroup, keyShape: IShape) => {
const { donutAttrs = {}, donutColorMap = {} } = cfg;
const attrNum = Object.keys(donutAttrs).length;
if (donutAttrs && attrNum > 1) {
const { configs, totalValue } = getDonutConfig(donutAttrs, donutColorMap);
if (totalValue) {
const { lineWidth, arcR } = getDonutSize(keyShape);
let arcBegin: [number, number] = [arcR, 0];
let beginAngle = 0;
if (attrNum === 1) {
// draw a path represents a circle
drawFan(group, {
arcR,
arcBegin,
beginAngle,
config: configs[0],
fanIndex: 0,
lineWidth,
totalValue,
drawWhole: true,
});
return;
}
for (let i = 0; i < configs.length; i++) {
const result = drawFan(group, {
arcR,
arcBegin,
beginAngle,
config: configs[i],
fanIndex: i,
lineWidth,
totalValue,
});
if (result.shouldEnd) return;
arcBegin = result.arcBegin;
beginAngle = result.beginAngle;
}
}
}
};
/**
* utilizes the existing fan shapes, update them with new configs
* removes the redundent fan shapes
* or adds more fan shapes
* @param cfg
* @param item
* @param keyShape
*/
const updateFans = (cfg: DonutNodeConfig, item: Item, keyShape: IShape) => {
const { donutAttrs, donutColorMap = {} } = cfg;
const visitMap = {};
const group = item.getContainer();
if (donutAttrs) {
const { configs, totalValue } = getDonutConfig(donutAttrs, donutColorMap);
if (totalValue) {
const { lineWidth, arcR } = getDonutSize(keyShape);
let arcBegin: [number, number] = [arcR, 0];
let beginAngle = 0;
for (let i = 0; i < configs.length; i++) {
const shapeName = `${FAN_NAME_PREFIX}${i}`;
const result = drawFan(group, {
arcR,
arcBegin,
beginAngle,
config: configs[i],
fanIndex: i,
lineWidth,
totalValue,
drawWhole: configs.length === 1,
updateShape: group['shapeMap'][shapeName],
});
if (result.shape) visitMap[shapeName] = true;
if (result.shouldEnd) break;
arcBegin = result.arcBegin;
beginAngle = result.beginAngle;
}
}
}
// remove the old shapes which are not visited, including the situation taht donutAttrs is empty
const fanKeys = Object.keys(group['shapeMap']).filter((key) => key.includes(FAN_NAME_PREFIX));
fanKeys.forEach((key) => {
if (!visitMap[key]) {
group['shapeMap'][key].remove(true);
delete group['shapeMap'][key];
}
});
};
// 饼图节点
registerNode(
@ -64,7 +283,7 @@ registerNode(
style: {
fill: Global.nodeLabel.style.fill,
fontSize: Global.nodeLabel.style.fontSize,
fontFamily: Global.windowFontFamily
fontFamily: Global.windowFontFamily,
},
},
// 节点上左右上下四个方向上的链接circle配置
@ -96,14 +315,14 @@ registerNode(
// 文本位置
labelPosition: 'center',
drawShape(cfg: DonutNodeConfig, group: IGroup): IShape {
const { icon: defaultIcon = {} } = this.mergeStyle || this.getOptions(cfg) as NodeConfig;
const { icon: defaultIcon = {} } = this.mergeStyle || (this.getOptions(cfg) as NodeConfig);
const style = this.getShapeStyle!(cfg);
const icon = deepMix({}, defaultIcon, cfg.icon);
const keyShape: IShape = group.addShape('circle', {
attrs: style,
className: `${this.type}-keyShape`,
draggable: true,
name: `${this.type}-keyShape`
name: `${this.type}-keyShape`,
});
group['shapeMap'][`${this.type}-keyShape`] = keyShape;
@ -146,7 +365,13 @@ registerNode(
return keyShape;
},
updateShape(cfg: DonutNodeConfig, item: Item, keyShapeStyle: object, hasIcon: boolean, updateType: UpdateType) {
updateShape(
cfg: DonutNodeConfig,
item: Item,
keyShapeStyle: object,
hasIcon: boolean,
updateType: UpdateType,
) {
// here cfg is merged configure including old model and new configs
const keyShape = item.get('keyShape');
keyShape.attr({
@ -166,205 +391,3 @@ registerNode(
},
'circle',
);
/**
* draws the fan shapes
* @param cfg
* @param group
* @param keyShape
* @returns
*/
const drawFans = (cfg: DonutNodeConfig, group: IGroup, keyShape: IShape) => {
const { donutAttrs = {}, donutColorMap = {} } = cfg;
const attrNum = Object.keys(donutAttrs).length;
if (donutAttrs && attrNum > 1) {
const { configs, totalValue } = getDonutConfig(donutAttrs, donutColorMap);
if (totalValue) {
const { lineWidth, arcR } = getDonutSize(keyShape);
let arcBegin: [number, number] = [arcR, 0];
let beginAngle = 0;
if (attrNum === 1) {
// draw a path represents a circle
drawFan(group, {
arcR,
arcBegin,
beginAngle,
config: configs[0],
fanIndex: 0,
lineWidth,
totalValue,
drawWhole: true
});
return;
}
for (let i = 0; i < configs.length; i++) {
const result = drawFan(group, {
arcR,
arcBegin,
beginAngle,
config: configs[i],
fanIndex: i,
lineWidth,
totalValue
});
if (result.shouldEnd) return;
arcBegin = result.arcBegin;
beginAngle = result.beginAngle;
}
}
}
}
/**
* draws one fan shape and returns the next position and angle
* @param group
* @param fanConfig
* @returns
*/
const drawFan = (group: IGroup, fanConfig: FanConfig): {
beginAngle: number, // next begin iangle
arcBegin: [number, number], // next begin position
shape: IShape | undefined, // shape added by this function
shouldEnd: boolean, // finish fans drawing
} => {
const { arcR, arcBegin, beginAngle, config, fanIndex, lineWidth, totalValue, drawWhole = false, updateShape = undefined } = fanConfig;
const percent = config.value / totalValue;
if (percent < 0.001) {
// too small to add a fan
return {
beginAngle,
arcBegin,
shape: undefined,
shouldEnd: false
}
}
let arcEnd, endAngle, isBig;
// draw a path represents the whole circle, or the percentage is close to 1
if (drawWhole || percent > 0.999) {
arcEnd = [arcR, 0.0001]; // [arcR * cos(2 * PI), -arcR * sin(2 * PI)]
isBig = 1;
} else {
const angle = percent * Math.PI * 2;
endAngle = beginAngle + angle;
arcEnd = [
arcR * Math.cos(endAngle),
-arcR * Math.sin(endAngle),
];
isBig = angle > Math.PI ? 1 : 0;
}
const style = {
path: [
['M', arcBegin[0], arcBegin[1]],
['A', arcR, arcR, 0, isBig, 0, arcEnd[0], arcEnd[1]]
],
stroke: config.color || updateShape?.attr('stroke') || defaultSubjectColors[fanIndex % defaultSubjectColors.length],
lineWidth,
};
if (updateShape) {
// update
updateShape.attr(style)
} else {
// draw
group['shapeMap'][`${FAN_NAME_PREFIX}${fanIndex}`] = group.addShape('path', {
attrs: style,
name: `${FAN_NAME_PREFIX}${fanIndex}`,
draggable: true,
});
}
return {
beginAngle: endAngle,
arcBegin: arcEnd,
shape: group['shapeMap'][`${FAN_NAME_PREFIX}${fanIndex}`],
shouldEnd: drawWhole || percent > 0.999
}
}
/**
* utilizes the existing fan shapes, update them with new configs
* removes the redundent fan shapes
* or adds more fan shapes
* @param cfg
* @param item
* @param keyShape
*/
const updateFans = (cfg: DonutNodeConfig, item: Item, keyShape: IShape) => {
const { donutAttrs, donutColorMap = {} } = cfg;
const visitMap = {};
const group = item.getContainer();
if (donutAttrs) {
const { configs, totalValue } = getDonutConfig(donutAttrs, donutColorMap);
if (totalValue) {
const { lineWidth, arcR } = getDonutSize(keyShape);
let arcBegin: [number, number] = [arcR, 0];
let beginAngle = 0;
for (let i = 0; i < configs.length; i++) {
const shapeName = `${FAN_NAME_PREFIX}${i}`;
const result = drawFan(group, {
arcR,
arcBegin,
beginAngle,
config: configs[i],
fanIndex: i,
lineWidth,
totalValue,
drawWhole: configs.length === 1,
updateShape: group['shapeMap'][shapeName]
});
if (result.shape) visitMap[shapeName] = true;
if (result.shouldEnd) break;
arcBegin = result.arcBegin;
beginAngle = result.beginAngle;
}
}
}
// remove the old shapes which are not visited, including the situation taht donutAttrs is empty
const fanKeys = Object.keys(group['shapeMap']).filter(key => key.includes(FAN_NAME_PREFIX));
fanKeys.forEach(key => {
if (!visitMap[key]) {
group['shapeMap'][key].remove(true);
delete group['shapeMap'][key];
}
});
}
/**
* calculate the total value and format single value for each fan
* @param donutAttrs
* @param donutColorMap
* @returns
*/
const getDonutConfig = (donutAttrs: DonutAttrs, donutColorMap: DonutColorMap): {
totalValue: number,
configs: FanValue[]
} => {
let totalValue = 0;
const configs = [];
Object.keys(donutAttrs).forEach((name) => {
const value = (+donutAttrs[name]);
if (isNaN(value)) return;
configs.push({
key: name,
value,
color: donutColorMap[name],
});
totalValue += value;
});
return { totalValue, configs };
}
/**
* calculate the lineWidth and radius for fan shapes according to the keyShape's radius
* @param keyShape
* @returns
*/
const getDonutSize = (keyShape: IShape): {
lineWidth: number,
arcR: number
} => {
const keyShapeR = keyShape.attr('r');
const innerR = 0.6 * keyShapeR; // 甜甜圈的内环半径
const arcR = (keyShapeR + innerR) / 2; // 内环半径与外环半径的平均值
const lineWidth = keyShapeR - innerR;
return { lineWidth, arcR };
}

View File

@ -52,6 +52,7 @@
},
"devDependencies": {
"@types/jest": "^26.0.18",
"@umijs/fabric": "^2.0.0",
"event-simulate": "^1.0.1",
"father": "^2.30.0",
"jest": "^26.6.3",

View File

@ -1,10 +1,10 @@
import { AbstractShape, AnimateCfg } from '@antv/g-canvas';
import { AnimateCfg } from '@antv/g-canvas';
import { animations } from './animateFunc';
export type AnimationConfig = AnimateCfg & { animate: keyof typeof animations };
export const animateShapeWithConfig = (
shape: AbstractShape,
shape: any,
config?: Partial<AnimationConfig>,
initMatrix?: number[],
) => {

File diff suppressed because it is too large Load Diff