diff --git a/.github/workflows/hotfix-release.yml b/.github/workflows/hotfix-release.yml
index 08533f132..d5c2ce28b 100644
--- a/.github/workflows/hotfix-release.yml
+++ b/.github/workflows/hotfix-release.yml
@@ -7,7 +7,7 @@ on:
jobs:
build:
- runs-on: macOS-12 # 如果用了electron,记得改成 macOS-latest
+ runs-on: macOS-13 # 如果用了electron,记得改成 macOS-latest
permissions:
contents: write
pull-requests: write
diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml
index 2cdac14b6..b2a20d764 100644
--- a/.github/workflows/pre-release.yml
+++ b/.github/workflows/pre-release.yml
@@ -9,7 +9,7 @@ on:
jobs:
build:
- runs-on: macOS-12 # 如果用了electron,记得改成 macOS-latest
+ runs-on: macOS-13 # 如果用了electron,记得改成 macOS-latest
permissions:
contents: write
diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml
index 2f307bafc..4d5dd5294 100644
--- a/.github/workflows/release-changelog.yml
+++ b/.github/workflows/release-changelog.yml
@@ -7,7 +7,7 @@ on:
jobs:
update-changelog-after-publish-a-release:
name: GitHub Actions Test
- runs-on: macOS-12
+ runs-on: macOS-13
strategy:
matrix:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index e9910136a..711899fce 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -7,7 +7,7 @@ on:
jobs:
build:
- runs-on: macOS-12 # 如果用了electron,记得改成 macOS-latest
+ runs-on: macOS-13 # 如果用了electron,记得改成 macOS-latest
permissions:
contents: write
pull-requests: write
diff --git a/.github/workflows/sync-main-to-develop.yml b/.github/workflows/sync-main-to-develop.yml
index 7fe1a18ba..8b84b6160 100644
--- a/.github/workflows/sync-main-to-develop.yml
+++ b/.github/workflows/sync-main-to-develop.yml
@@ -11,7 +11,7 @@ jobs:
if_merged:
if: github.event.pull_request.merged == true
- runs-on: macOS-12
+ runs-on: macOS-13
permissions:
contents: write
diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml
index f451bb6b4..4b8271e2b 100644
--- a/.github/workflows/unit-test.yml
+++ b/.github/workflows/unit-test.yml
@@ -10,7 +10,7 @@ on:
jobs:
build:
runs-on:
- - macOS-12
+ - macOS-13
strategy:
matrix:
diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml
index 919854125..739c21178 100644
--- a/common/config/rush/pnpm-lock.yaml
+++ b/common/config/rush/pnpm-lock.yaml
@@ -14,8 +14,8 @@ importers:
'@types/react-dom': ^18.0.0
'@visactor/vchart': 1.3.0
'@visactor/vgrammar': ~0.5.7
- '@visactor/vrender': workspace:0.21.0
- '@visactor/vutils': ~0.19.1
+ '@visactor/vrender': workspace:0.21.1
+ '@visactor/vutils': ~0.19.2
'@vitejs/plugin-react': 3.1.0
axios: ^1.4.0
chalk: ^3.0.0
@@ -38,7 +38,7 @@ importers:
'@visactor/vchart': 1.3.0
'@visactor/vgrammar': 0.5.7
'@visactor/vrender': link:../packages/vrender
- '@visactor/vutils': 0.19.1
+ '@visactor/vutils': 0.19.2
axios: 1.7.8
highlight.js: 11.10.0
lodash: 4.17.21
@@ -71,8 +71,8 @@ importers:
'@types/react': ^18.0.0
'@types/react-dom': ^18.0.0
'@types/react-reconciler': ^0.28.2
- '@visactor/vrender': workspace:0.21.0
- '@visactor/vutils': ~0.19.1
+ '@visactor/vrender': workspace:0.21.1
+ '@visactor/vutils': ~0.19.2
'@vitejs/plugin-react': 3.1.0
cross-env: ^7.0.3
eslint: ~8.18.0
@@ -84,7 +84,7 @@ importers:
vite: 3.2.6
dependencies:
'@visactor/vrender': link:../vrender
- '@visactor/vutils': 0.19.1
+ '@visactor/vutils': 0.19.2
react-reconciler: 0.29.2_react@18.3.1
tslib: 2.8.1
devDependencies:
@@ -111,9 +111,9 @@ importers:
'@rushstack/eslint-patch': ~1.1.4
'@types/react': ^18.0.0
'@types/react-dom': ^18.0.0
- '@visactor/react-vrender': workspace:0.21.0
- '@visactor/vrender': workspace:0.21.0
- '@visactor/vutils': ~0.19.1
+ '@visactor/react-vrender': workspace:0.21.1
+ '@visactor/vrender': workspace:0.21.1
+ '@visactor/vutils': ~0.19.2
'@vitejs/plugin-react': 3.1.0
cross-env: ^7.0.3
eslint: ~8.18.0
@@ -126,7 +126,7 @@ importers:
dependencies:
'@visactor/react-vrender': link:../react-vrender
'@visactor/vrender': link:../vrender
- '@visactor/vutils': 0.19.1
+ '@visactor/vutils': 0.19.2
react-reconciler: 0.29.2_react@18.3.1
tslib: 2.8.1
devDependencies:
@@ -153,9 +153,9 @@ importers:
'@types/jest': ^26.0.0
'@types/react': ^18.0.0
'@types/react-dom': ^18.0.0
- '@visactor/vrender-core': workspace:0.21.0
- '@visactor/vrender-kits': workspace:0.21.0
- '@visactor/vutils': ~0.19.1
+ '@visactor/vrender-core': workspace:0.21.1
+ '@visactor/vrender-kits': workspace:0.21.1
+ '@visactor/vutils': ~0.19.2
'@vitejs/plugin-react': 3.1.0
canvas: 2.11.2
cross-env: ^7.0.3
@@ -179,7 +179,7 @@ importers:
'@types/jest': 26.0.24
'@types/react': 18.3.12
'@types/react-dom': 18.3.1
- '@visactor/vutils': 0.19.1
+ '@visactor/vutils': 0.19.2
'@vitejs/plugin-react': 3.1.0_vite@3.2.6
canvas: 2.11.2
cross-env: 7.0.3
@@ -200,10 +200,10 @@ importers:
'@internal/ts-config': workspace:*
'@rushstack/eslint-patch': ~1.1.4
'@types/jest': ^26.0.0
- '@visactor/vrender-core': workspace:0.21.0
- '@visactor/vrender-kits': workspace:0.21.0
- '@visactor/vscale': ~0.19.1
- '@visactor/vutils': ~0.19.1
+ '@visactor/vrender-core': workspace:0.21.1
+ '@visactor/vrender-kits': workspace:0.21.1
+ '@visactor/vscale': ~0.19.2
+ '@visactor/vutils': ~0.19.2
cross-env: ^7.0.3
eslint: ~8.18.0
jest: ^26.0.0
@@ -215,8 +215,8 @@ importers:
dependencies:
'@visactor/vrender-core': link:../vrender-core
'@visactor/vrender-kits': link:../vrender-kits
- '@visactor/vscale': 0.19.1
- '@visactor/vutils': 0.19.1
+ '@visactor/vscale': 0.19.2
+ '@visactor/vutils': 0.19.2
devDependencies:
'@internal/bundler': link:../../tools/bundler
'@internal/eslint-config': link:../../share/eslint-config
@@ -241,7 +241,7 @@ importers:
'@types/jest': ^26.0.0
'@types/react': ^18.0.0
'@types/react-dom': ^18.0.0
- '@visactor/vutils': ~0.19.1
+ '@visactor/vutils': ~0.19.2
'@vitejs/plugin-react': 3.1.0
color-convert: 2.0.1
cross-env: ^7.0.3
@@ -255,7 +255,7 @@ importers:
typescript: 4.9.5
vite: 3.2.6
dependencies:
- '@visactor/vutils': 0.19.1
+ '@visactor/vutils': 0.19.2
color-convert: 2.0.1
devDependencies:
'@internal/bundler': link:../../tools/bundler
@@ -287,8 +287,8 @@ importers:
'@types/node-fetch': 2.6.4
'@types/react': ^18.0.0
'@types/react-dom': ^18.0.0
- '@visactor/vrender-core': workspace:0.21.0
- '@visactor/vutils': ~0.19.1
+ '@visactor/vrender-core': workspace:0.21.1
+ '@visactor/vutils': ~0.19.2
'@vitejs/plugin-react': 3.1.0
canvas: 2.11.2
cross-env: ^7.0.3
@@ -302,7 +302,7 @@ importers:
dependencies:
'@resvg/resvg-js': 2.4.1
'@visactor/vrender-core': link:../vrender-core
- '@visactor/vutils': 0.19.1
+ '@visactor/vutils': 0.19.2
roughjs: 4.5.2
devDependencies:
'@internal/bundler': link:../../tools/bundler
@@ -369,10 +369,10 @@ importers:
'@rushstack/eslint-patch': ~1.1.4
'@types/node': '*'
'@types/node-fetch': 2.6.4
- '@visactor/vrender': workspace:0.21.0
- '@visactor/vrender-components': workspace:0.21.0
- '@visactor/vrender-core': workspace:0.21.0
- '@visactor/vrender-kits': workspace:0.21.0
+ '@visactor/vrender': workspace:0.21.1
+ '@visactor/vrender-components': workspace:0.21.1
+ '@visactor/vrender-core': workspace:0.21.1
+ '@visactor/vrender-kits': workspace:0.21.1
cross-env: ^7.0.3
eslint: ~8.18.0
form-data: ~4.0.0
@@ -3409,10 +3409,10 @@ packages:
'@visactor/vutils': 0.15.14
dev: false
- /@visactor/vscale/0.19.1:
- resolution: {integrity: sha512-hPNBP33sTzB/7xxot1FTSnuDmween06iM+JW3j7u/AmdGeMON4cphisEN5iJ5DR5Z9Br5PolvP4vnhemoqlTZw==}
+ /@visactor/vscale/0.19.2:
+ resolution: {integrity: sha512-grS2nI/dyky9+YWt6EbtBS7aPiHmJrnGnleeRLvaaa6uZN9ItLsuP7oSv63k1arJzkyJ0xcLH4ze/nZmrWopzA==}
dependencies:
- '@visactor/vutils': 0.19.1
+ '@visactor/vutils': 0.19.2
dev: false
/@visactor/vutils/0.13.3:
@@ -3431,8 +3431,8 @@ packages:
eventemitter3: 4.0.7
dev: false
- /@visactor/vutils/0.19.1:
- resolution: {integrity: sha512-ydvC2RvRISCsL86tkhyStJpsTX61UCCGN+BzPeMfmDv4cOc5IWJn3MnL3Twgj0lm6irCabEPQyetPSZ4NEmoOw==}
+ /@visactor/vutils/0.19.2:
+ resolution: {integrity: sha512-RIg+cZTLvTRNbj0f3pc/4W5ASXhuM6G5XJU4JJ2Ghdqt8YGaGzdLFrqOIu5MaHfm8EuJun0oPapDk8PXt4ZQgQ==}
dependencies:
'@turf/helpers': 6.5.0
'@turf/invariant': 6.5.0
diff --git a/common/config/rush/version-policies.json b/common/config/rush/version-policies.json
index 6265550bb..dc9457656 100644
--- a/common/config/rush/version-policies.json
+++ b/common/config/rush/version-policies.json
@@ -1 +1 @@
-[{"definitionName":"lockStepVersion","policyName":"vrenderMain","version":"0.21.0","nextBump":"minor"}]
+[{"definitionName":"lockStepVersion","policyName":"vrenderMain","version":"0.21.1","nextBump":"patch"}]
diff --git a/docs/assets/api/en/common/attribute.md b/docs/assets/api/en/common/attribute.md
index 8ba9aa31c..cc49d4dbc 100644
--- a/docs/assets/api/en/common/attribute.md
+++ b/docs/assets/api/en/common/attribute.md
@@ -219,3 +219,10 @@ shadowRoot 在宿主图元的上方还是下方,>0 为下方,<0 为上方
#${prefix} globalCompositeOperation(CanvasRenderingContext2D['globalCompositeOperation']) = ''
对应 Canvas 的 globalCompositeOperation,用来配置滤镜
+
+#${prefix} boundsPadding(number[]) = []
+
+图元的包围盒的 padding,当自动计算的AABBBounds不满足要求的时候可以配置,可以调整包围盒的大小。
+如果是number,那么四个方向的padding都是这个值
+如果是[number, number] ,那么分别是上下和左右的padding
+如果是[number, number, number, number],那么分别是上右下左的
diff --git a/docs/assets/api/zh/common/attribute.md b/docs/assets/api/zh/common/attribute.md
index 8ba9aa31c..cc49d4dbc 100644
--- a/docs/assets/api/zh/common/attribute.md
+++ b/docs/assets/api/zh/common/attribute.md
@@ -219,3 +219,10 @@ shadowRoot 在宿主图元的上方还是下方,>0 为下方,<0 为上方
#${prefix} globalCompositeOperation(CanvasRenderingContext2D['globalCompositeOperation']) = ''
对应 Canvas 的 globalCompositeOperation,用来配置滤镜
+
+#${prefix} boundsPadding(number[]) = []
+
+图元的包围盒的 padding,当自动计算的AABBBounds不满足要求的时候可以配置,可以调整包围盒的大小。
+如果是number,那么四个方向的padding都是这个值
+如果是[number, number] ,那么分别是上下和左右的padding
+如果是[number, number, number, number],那么分别是上右下左的
diff --git a/docs/assets/contributing/en/1-Setting-Up-the-Development-Environment.md b/docs/assets/contributing/en/1-Setting-Up-the-Development-Environment.md
new file mode 100644
index 000000000..307ccf046
--- /dev/null
+++ b/docs/assets/contributing/en/1-Setting-Up-the-Development-Environment.md
@@ -0,0 +1,117 @@
+---
+title: 1. Setting up Development Environment
+
+key words: VisActor, VChart, VTable, VStory, VMind, VGrammar, VRender, Visualization, Chart, Data, Table, Graph, Gis, LLM
+---
+
+# Github
+
+## 1.1 Register an Account
+
+The VisActor team usually develops and maintains issues on Github. Please open the [Github website](https://github.com/), click the `Sign up` button in the top right corner, register your own account, and take the first step on your open-source journey.
+
+If, due to special circumstances, you are unable to access the Github site, please inform us and proceed with project development through [Gitee](https://gitee.com/VisActor/VRender).
+
+## 1.2 Fork the Project
+
+First, you need to fork this project. Go to the [VRender project page](https://github.com/VisActor/VRender) and click the Fork button in the top right corner.
+
+
+
+Your github account will show the project xxxx(your github username)/vrender.
+
+
+
+# Local Development Environment
+
+## 2.1 Install Git
+
+Since the code is hosted on Github, we use git for version control.
+
+Git is a version control system used to track and manage code changes in software development projects. It helps developers record and manage the history of code, facilitating team collaboration, code version control, code merging, and other operations. With Git, you can track every version of each file and easily switch and compare between different versions. Git also provides branch management functionality, allowing multiple parallel development tasks to be carried out simultaneously.
+
+- Visit the Git official website: [https://git-scm.com/](https://git-scm.com/)
+
+- Download the latest version of the Git installer.
+
+- Run the downloaded installer and follow the installation wizard instructions.
+
+- After installation, you can confirm the successful installation by using the `git version` command in the command line.
+
+```
+git version
+**git version 2.39.2 (Apple Git-143)**
+```
+
+## 2.2 Install Development Tools (Recommended: VSCode)
+
+VisActor is mainly in the front-end technology stack, and there are many tools available for front-end development. We recommend using VSCode. Of course, you can also use your preferred development tool.
+
+If you are not familiar with VSCode, it is recommended to read the official documentation: [https://vscode.js.cn/docs/setup/setup-overview](https://vscode.js.cn/docs/setup/setup-overview)
+
+## 2.3 Install Marscode AI Programming Assistant
+
+[Marscode AI Programming Assistant](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a)
+
+Marscode AI Programming Assistant is an AI programming assistant provided by Marscode, offering AI features such as intelligent code completion. It supports mainstream programming languages and IDEs, providing suggestions for writing single lines of code or entire functions during development. Additionally, it supports functions such as code interpretation, unit test generation, and issue fixing, improving development efficiency and quality. For more information, please refer to the [documentation of Marscode AI Programming Assistant](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a).
+
+
+
+With Marscode, VisActor developers can more conveniently perform tasks such as code understanding, document writing, feature development, and unit testing. Detailed examples will be provided in the contribution guidelines for various tasks.
+
+
+
+## 2.4 Clone the Code to Local
+
+Navigate to the VRender folder and add the remote address of VRender.
+
+```
+git remote add upstream https://github.com/VisActor/VRender.git
+```
+
+Get the latest source code of VRender.
+
+```
+git pull upstream develop
+```
+
+# Initialize the Project
+
+First, globally install [@microsoft/rush](https://rushjs.io/pages/intro/get_started/)
+
+```
+$ npm i --global @microsoft/rush
+```
+
+Next, run the command to view the demo.
+
+```
+# Install dependencies
+$ rush update
+# Start the demo page of vrender
+$ rush run -p @visactor/vrender -s start
+# Start the local document site
+$ rush docs
+```
+
+# Next Steps
+
+So far, you have prepared for developing code. Please continue reading the next section of the tutorial to start different types of tasks.
+
+Github: [github.com/VisActor](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FVisActor)
+
+VisActor WeChat subscription account (you can join the WeChat group through the subscription account menu):
+
+
+
+VisActor official website: [www.visactor.io/](https://link.juejin.cn/?target=https%3A%2F%2Fwww.visactor.io%2Fvtable)
+
+Feishu group:
+
+
+
+Discord: [https://discord.com/invite/3wPyxVyH6m](https://discord.com/invite/3wPyxVyH6m)
+
+# This document is contributed by the following individuals
+
+[Xuanhun](https://github.com/xuanhun)
diff --git a/docs/assets/contributing/en/2-Howto Submit an Issue.md b/docs/assets/contributing/en/2-Howto Submit an Issue.md
new file mode 100644
index 000000000..c6445b09f
--- /dev/null
+++ b/docs/assets/contributing/en/2-Howto Submit an Issue.md
@@ -0,0 +1,61 @@
+---
+title: 2. How to Raise an Issue
+
+key words: VisActor, VChart, VTable, VStory, VMind, VGrammar, VRender, Visualization, Chart, Data, Table, Graph, Gis, LLM
+---
+
+Under the issues section of each project, you can create and search for issues.
+
+For example, VRender issues: [https://github.com/VisActor/VRender/issues](https://github.com/VisActor/VRender/issues)
+
+
+
+# Check for Existing Issues
+
+You can use the search filter to check the details of existing issues to see if a similar issue already exists.
+
+# Create an Issue
+
+If you don't find a similar issue, you can click the "New issue" button.
+
+
+
+Select "**Documentation Request**," click the "Get Start" button, and fill out the issue form.
+
+
+
+# Submit the Issue
+
+After filling out the issue form, click the "Submit new issue" button to submit your issue.
+
+# Follow Up on the Issue
+
+Once you have submitted the issue, you can check the status of your issue in the repository's "Issues" tab. The project developers may ask for more information or inform you that they are working on the issue.
+
+# Close the Issue
+
+If your problem has been resolved or your request has been fulfilled, the project developers will close the issue. You can also close the issue yourself if you believe the problem has been resolved or no longer need further assistance.
+
+By following these steps, you can successfully raise an issue for an open-source project on GitHub. Remember to describe the issue in as much detail and clarity as possible, as this will help the project developers understand and address your problem more quickly.
+
+# Next Steps
+
+By now, you are familiar with the concept of raising issues. Next, please continue reading the next section of the tutorial to start different types of tasks.
+
+GitHub: [github.com/VisActor](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FVisActor)
+
+VisActor WeChat subscription account (you can join the WeChat group through the subscription account menu):
+
+
+
+VisActor Official Website: [www.visactor.io/](https://link.juejin.cn/?target=https%3A%2F%2Fwww.visactor.io%2Fvtable)
+
+Feishu Group:
+
+
+
+Discord: [https://discord.com/invite/3wPyxVyH6m](https://discord.com/invite/3wPyxVyH6m)
+
+# Contributors to this Document
+
+[玄魂](https://github.com/xuanhun)
diff --git a/docs/assets/contributing/en/3-How to Contribute Documentation.md b/docs/assets/contributing/en/3-How to Contribute Documentation.md
new file mode 100644
index 000000000..62f0708bc
--- /dev/null
+++ b/docs/assets/contributing/en/3-How to Contribute Documentation.md
@@ -0,0 +1,248 @@
+---
+title: 3. How to Contribute to Documentation
+
+key words: VisActor, VChart, VTable, VStory, VMind, VGrammar, VRender, Visualization, Chart, Data, Table, Graph, Gis, LLM
+---
+
+# Create a Branch
+
+The default branch for VRender is the develop branch. Whether it's for feature development, bug fixes, or documentation writing, please create a new branch and then merge it into the develop branch. Use the following code to create a branch:
+
+```
+// Create a documentation or demo branch
+git checkout -b docs/add-funnel-demo
+```
+
+# Find or Create an Issue
+
+In principle, we require that every PR has a corresponding issue. Before starting development, please make sure there is a corresponding issue that has not been claimed.
+
+## Search for Documentation Issues
+
+You can search for documentation-related issues using the following method:
+
+```
+is:open label:docs
+```
+
+
+
+Some features may be associated with the "doc" label, so you can further check if the issue is purely a documentation task.
+
+## Create a Documentation Issue
+
+Click on "NEW ISSUE", open the issue selection page, and choose "**Documentation Request**".
+
+
+
+Fill in the relevant information for the documentation issue you want to submit.
+
+
+
+# Claim an Issue
+
+If you want to write or modify documentation, you can leave a message under the issue to claim it. An administrator will contact you, confirm, and then assign the issue to you.
+
+For example:
+
+
+
+# Create or Modify Documentation
+
+The location of VRender documentation and demos in the project is as follows:
+
+
+
+Currently, the types of documentation are as follows:
+
+- examples: Element examples, corresponding to the site:
+
+https://www.visactor.io/vrender/example
+
+- guide: Tutorials, corresponding to the site: https://www.visactor.io/vrender/guide/asd/VRender_Website_Guide
+
+Find the corresponding location of the documentation for additions or modifications. It is important to note that some documentation also requires maintenance of the "menu.json" file.
+
+
+
+This file corresponds to the final display location and name of the documentation on the site. For example:
+
+
+
+# Use Marscode AI Programming Assistant for Documentation Writing
+
+[Marscode AI Programming Assistant](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) can provide comprehensive assistance throughout the documentation creation process.
+
+If you have not installed [Marscode AI Programming Assistant](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) yet, please download it from this link: https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a
+
+In documentation writing, using the context command appropriately can enhance the accuracy of the content.
+
+`**⭐️ #Workspace**`
+
+Select global code in Workspace as context, and AI will automatically find relevant code context based on your query.
+
+
+
+`**⭐️ #Files**`
+
+Search and select files in the code repository as context.
+
+
+
+`**⭐️ #Code**`
+
+Search and select functions or classes in the code repository as context.
+
+
+
+Here are examples of how to use [Marscode AI Programming Assistant](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) for documentation writing.
+
+## 5.1 Provide Documentation Writing Ideas
+
+Here, **invoke #Workspace** and ask for help in generating an outline for developer documentation.
+
+
+
+## 5.2 Generate Project Structure Explanation
+
+Here, **invoke #Workspace** and ask for help in generating a document explaining the project structure.
+
+
+
+You can further inquire about subfolders for more detailed explanations.
+
+
+
+## 5.3 Generate File or Code Explanations
+
+### 5.3.1 Generate Code Explanations
+
+When selecting a piece of code in a file, you can choose the Explain command from the floating menu, and [Marscode](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) will generate a detailed code explanation. You can then review and adapt it as needed.
+
+
+
+You can also directly input the Explain command in the dialog box.
+
+
+
+You can also use the #Code context mentioned earlier to combine Explain with your instructions for more detailed tasks.
+
+### 5.3.2 Generate File-Specific Explanations
+
+Explain can be used in conjunction with Context or Files commands to generate explanations for the entire file.
+
+
+
+## 5.4 Generate Sample Code
+
+To better explain principles and usage, it is often necessary to provide runnable demos. You can use the code generation capabilities of [Marscode](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) to generate sample code. However, it is important to verify the accuracy of the generated code as AI-generated code may not always be precise.
+
+## 5.5 Content Retrieval
+
+Typically, each Q&A session with [Marscode](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) will provide reference documents that can offer more context for further analysis.
+
+
+
+You can also directly search for files:
+
+
+
+## 5.6 Translate Documentation
+
+VisActor's documentation needs to be provided in both Chinese and English, and Marscode can assist with translation.
+
+# Commit Code
+
+After completing the documentation, push the code to your remote branch. For example:
+
+```
+git commit -a -m "docs: add custom funnel demo and related docs"
+```
+
+VisActor's commit messages follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification:
+
+`[optional scope]: `
+
+Common `type` values include docs (documentation, log changes), feat (new feature), fix (bug fix), refactor (code refactoring), etc. Please choose accordingly based on the actual situation.
+
+Before submitting the commit, we will perform commit lint checks. You can refer to the [check rules](https://github.com/VisActor/VRender/blob/develop/common/autoinstallers/lint/commitlint.config.js) for more details.
+
+A common issue is when the upstream (@visactor/vrender) has new updates, which may cause conflicts when submitting a Pull Request. Therefore, before submitting, merge the commits from other developers with your own. Switch to the develop branch using the following code:
+
+```
+git checkout develop
+```
+
+Pull the latest code from the remote:
+
+```
+git pull upstream develop
+```
+
+Switch back to your development branch:
+
+```
+git checkout docs/add-funnel-demo
+```
+
+Merge the commits from develop into your branch:
+
+```
+git rebase develop
+```
+
+Push the updated code to your branch:
+
+```
+git push origin docs/add-funnel-demo
+```
+
+# Submit a PR
+
+You can click on the `Compare & pull request` button on your GitHub repository page.
+
+
+
+Or create one through the `contribute` button:
+
+Fill in the modifications for this submission using the template:
+
+- Check the type of modification
+
+
+
+- Fill in the associated issue
+
+
+
+- If there are complex changes, explain the background and solution
+
+
+
+After filling in the relevant information, click on Create pull request to submit.
+
+An administrator will review the PR and decide whether to approve it. If not approved, modifications will be required before resubmitting.
+
+# Next Steps
+
+Different types of documentation have specific requirements for demo documentation, which can be found in the "How to Contribute to Demos" section.
+
+You can continue to explore different types of tasks.
+
+GitHub: [github.com/VisActor](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FVisActor)
+
+VisActor WeChat subscription account (join the WeChat group through the subscription account menu):
+
+
+
+VisActor Official Website: [www.visactor.io/](https://link.juejin.cn/?target=https%3A%2F%2Fwww.visactor.io%2Fvtable)
+
+Feishu Group:
+
+
+
+Discord: https://discord.com/invite/3wPyxVyH6m
+
+# Contributors to this Documentation
+
+[玄魂](https://github.com/xuanhun)
diff --git a/docs/assets/contributing/en/4-How to Contribute to a Demo.md b/docs/assets/contributing/en/4-How to Contribute to a Demo.md
new file mode 100644
index 000000000..c34ba953a
--- /dev/null
+++ b/docs/assets/contributing/en/4-How to Contribute to a Demo.md
@@ -0,0 +1,240 @@
+---
+title: 4. How to Contribute Demo
+
+key words: VisActor, VChart, VTable, VStory, VMind, VGrammar, VRender, Visualization, Chart, Data, Table, Graph, Gis, LLM
+---
+
+# Create a Branch
+
+The default branch for VRender is the `develop` branch. Whether it's for feature development, bug fixes, or documentation writing, please create a new branch and then merge it into the `develop` branch. Use the following code to create a branch:
+
+```
+// Create a branch for documentation and demo
+git checkout -b docs/add-funnel-demo
+```
+
+# Find or Create an Issue
+
+In principle, we require that each PR has a corresponding issue. Before starting development, please make sure there is a corresponding issue that has not been claimed.
+
+## Search for Demo Issues
+
+You can search for demo-related issues using the following method:
+
+```
+label:demos
+```
+
+
+
+Some features may be associated with the `doc` label, so you can further check if the issue is purely a demo task.
+
+## Create a Demo Issue
+
+Click on "NEW ISSUE", open the issue selection page, and choose "**Others**".
+
+
+
+Fill in the relevant information for the document issue you want to submit, and tag it with the "demos" label.
+
+
+
+# Claim the Issue
+
+If you want to submit a demo or fix a demo bug, you can leave a message under that issue to claim it. The administrator will contact you, confirm, and then assign the issue to you.
+
+For example:
+
+
+
+# Create or Modify Demo
+
+The location of VRender documentation and demos in the project is as follows (examples):
+
+
+
+Taking the example document of `basic-arc` as an example (currently one example contains both Chinese and English versions, located in the `zh` & `en` paths):
+
+
+
+The example Markdown content is divided into several parts:
+
+- Metadata: Defines the attributes of the example content, including chart category, cover image, keywords, etc.
+- Title: The content under the first-level title corresponds to the description of the example.
+- Key Configurations: Key configuration explanations included in the example, which will be displayed in the "Key Configurations" section on the example page.
+- Code Demo: The specific code content executed in the example, currently only supports native JavaScript code.
+
+```js
+// Code example
+```
+
+The fields defined in the metadata of Markdown are:
+
+- group: The classification information of the example, describing which chart category the current example belongs to.
+- title: The title of the example.
+- keywords: Keywords of the example.
+- order: The sorting basis of the example under the same group.
+- cover: The cover image of the example.
+- tutorial: Link to the tutorial (the default example tutorial will jump to the tutorial corresponding to the example group).
+
+Currently, the group of the chart example contains multiple categories, such as `graphic-arc`, `graphic-area`, etc., corresponding to the categories under all charts in the VRender example gallery. You can refer to existing example documents to fill in the specific category fields.
+
+
+
+After completing the new demo writing, you can add the demo path and title to the `docs/assets/examples/menu.json` file:
+
+
+
+> For image resources that need to be uploaded during demo creation, please refer to the chapter [6. How to upload image resources](./6-How%20to%20upload%20image).
+
+# Use Marscode AI Programming Assistant for Demo Writing
+
+With the help of the [Marscode AI Programming Assistant](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a), you can provide comprehensive assistance throughout the document creation process.
+
+If you haven't installed the [Marscode AI Programming Assistant](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) yet, please download it from this link: [Download Marscode AI Programming Assistant](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a)
+
+In demo writing, using context commands appropriately can improve the accuracy of the content.
+
+`**⭐️ #Workspace**`
+
+Select global code in Workspace as context, and AI will automatically find relevant code context based on your query.
+
+
+
+`**⭐️ #Files**`
+
+Search and select files in the code repository as context.
+
+
+
+`**⭐️ #Code**`
+
+Search and select functions or classes in the code repository as context.
+
+
+
+Here are examples of how to use the [Marscode AI Programming Assistant](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) for demo writing.
+
+## 5.1 Provide Document Framework
+
+Here **invoke #Workspace**, then ask a question, select the content of an example document, and request it to generate a new example document based on that.
+
+
+
+You can continue to adjust the details based on this generated framework.
+
+## 5.2 Generate Descriptive Text
+
+The descriptive text for each demo can be generated first using [Marscode](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a), and then proofread and adjusted. For example:
+
+
+
+## 5.3 Generate Example Code
+
+To better explain the principles and usage, it is usually necessary to provide a demo that can be actually run. You can use the code generation capability of [Marscode](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) to generate example code for us. However, the code generation capabilities of various AIs cannot guarantee accuracy, so further verification is needed.
+
+## 5.4 Content Retrieval
+
+Usually, each of our Q&A in [Marscode](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) will provide reference documents, which can provide more context for further analysis.
+
+
+
+You can also directly search for files:
+
+
+
+# Submit Code
+
+After completing the document, push the code to your remote branch. For example:
+
+```
+git commit -a -m "docs: add custom funnel demo and related docs"
+```
+
+VisActor's commit messages follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification, with **docs used for demos**.
+
+`[optional scope]: `
+
+Common `type` values include docs (documentation, log changes), feat (new feature), fix (bug fix), refactor (code refactoring), etc. Please choose according to the actual situation.
+
+Write a concise and accurate description in English before committing.
+
+Before submitting the commit, we will perform a commit lint check. You can check the [lint rules](https://github.com/VisActor/VRender/blob/develop/common/autoinstallers/lint/commitlint.config.js) for more details.
+
+A common issue is that the remote upstream (@visactor/vrender) has been updated, which may cause conflicts when submitting a Pull Request. Therefore, you can merge the commits from other developers and your commits before submitting the PR. Switch to the `develop` branch using the following code:
+
+```
+git checkout develop
+```
+
+Pull the latest code from the remote:
+
+```
+git pull upstream develop
+```
+
+Switch back to your development branch:
+
+```
+git checkout docs/add-funnel-demo
+```
+
+Merge the commits from `develop` into your branch:
+
+```
+git rebase develop
+```
+
+Push the updated code to your branch:
+
+```
+git push origin docs/add-funnel-demo
+```
+
+# Submit PR
+
+You can click on the `Compare & pull request` button on your GitHub repository page.
+
+
+
+Or create one through the `contribute` button:
+
+Fill in the modifications for this submission according to the template:
+
+- Check the type of modification
+
+
+
+- Fill in the associated issue
+
+
+
+- If there are complex changes, explain the background and solution
+
+
+
+After filling in the relevant information, click on Create pull request to submit.
+
+The administrator will review the PR and decide whether to approve it. If it is not approved, you will need to make modifications and resubmit.
+
+# Next Steps
+
+You can continue to try different types of tasks.
+
+GitHub: [github.com/VisActor](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FVisActor)
+
+VisActor WeChat subscription account (you can join the WeChat group through the subscription account menu):
+
+
+
+VisActor Official Website: [www.visactor.io/](https://link.juejin.cn/?target=https%3A%2F%2Fwww.visactor.io%2Fvtable)
+
+Feishu Group:
+
+
+
+Discord: [Join Discord](https://discord.com/invite/3wPyxVyH6m)
+
+# This Document is Contributed by the Following Individuals
+
+[玄魂](https://github.com/xuanhun)
diff --git a/docs/assets/contributing/en/5-How to Contribute Code.md b/docs/assets/contributing/en/5-How to Contribute Code.md
new file mode 100644
index 000000000..3bb35c0b3
--- /dev/null
+++ b/docs/assets/contributing/en/5-How to Contribute Code.md
@@ -0,0 +1,244 @@
+---
+title: 5. How to Contribute Code
+
+key words: VisActor, VChart, VTable, VStory, VMind, VGrammar, VRender, Visualization, Chart, Data, Table, Graph, Gis, LLM
+---
+
+# Create a Branch
+
+The default branch for VRender is the develop branch. Whether it's for feature development, bug fixes, or documentation writing, please create a new branch and then merge it into the develop branch. Use the following code to create a branch:
+
+```
+// Create a branch for documentation and demo
+git checkout -b docs/add-funnel-demo
+```
+
+# Find or Create an Issue
+
+In principle, we require that every PR has a corresponding issue. Before starting development, please make sure there is a corresponding issue that has not been claimed.
+
+## Search for an Issue
+
+You can search for bug or feature-related issues in the following ways:
+
+
+
+## Create a Code-Related Issue
+
+Click on "NEW ISSUE" to open the issue selection page, then choose either "Bug Report" or "Feature Request".
+
+
+
+Fill in the relevant information for the documentation issue and add appropriate tags.
+
+
+
+# Claim an Issue
+
+If you want to contribute code, you can leave a message under the issue to claim it. An administrator will contact you, confirm, and then assign the issue to you.
+
+For example:
+
+
+
+# Write Code
+
+All components in the VRender ecosystem are located in the same directory, divided by package names. Developers need to develop code on their own code branch and then commit it.
+
+# Use Marscode AI Programming Assistant for Code Writing
+
+By using the [Marscode AI Programming Assistant](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a), you can receive comprehensive assistance throughout the code writing process.
+
+If you haven't installed the [Marscode AI Programming Assistant](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) yet, please download it from this link: https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a
+
+During code writing, using the context command appropriately can enhance the accuracy of the content.
+
+`**⭐️ #Workspace**`
+
+Select global code in Workspace as context, and AI will automatically find relevant code context based on your query.
+
+
+
+`**⭐️ #Files**`
+
+Search and select files in the code repository as context.
+
+
+
+`**⭐️ #Code**`
+
+Search and select functions or classes in the code repository as context.
+
+
+
+The following examples demonstrate how to use the [Marscode AI Programming Assistant](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) for code writing.
+
+## 5.1 Quickly Familiarize Yourself with the Entire Repository
+
+Here, **use #Workspace to invoke it**, then ask it to generate a project structure explanation document.
+
+
+
+You can also ask further questions about subfolders.
+
+
+
+## 5.2 Explain Code
+
+### 5.2.1 Generate Code Explanations
+
+When selecting a piece of code in a file, you can choose the Explain command from the floating menu, and [Marscode](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) will generate detailed code explanations for you. You can then review and modify based on this.
+
+
+
+You can also directly input the Explain command in the dialog box.
+
+
+
+You can also use the #Code context mentioned above to combine Explain with your instructions for more detailed tasks.
+
+### 5.2.2 Generate Explanations for the Entire File
+
+Explain can be used in conjunction with Context or Files commands to generate explanations for the entire file.
+
+
+
+## 5.3 Content Retrieval
+
+Usually, each Q&A session with [Marscode](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) will provide reference documents, which can provide more context for further analysis.
+
+
+
+You can also directly search for files:
+
+
+
+## 5.4 Code Generation
+
+In daily coding, you may often encounter scenarios where you need to use repetitive code, and sometimes you may not know if a certain function for a feature is already implemented. In such cases, use `#Workspace` to ask questions. For example:
+
+
+
+## 5.5 Add Comments
+
+Use the "/doc" command to generate code comments.
+
+
+
+## 5.6 Generate Unit Tests
+
+VRender unit test code is located in the "**tests**" directory of each package.
+
+
+
+You can quickly generate unit test code using the "/test" command in [Marscode](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a).
+
+
+
+## 5.7 Intelligent Suggestions
+
+During the writing process, intelligent code suggestions are a standard feature of the programming assistant. Feel free to experience it yourself.
+
+# Commit Code
+
+After completing the documentation, push the code to your remote branch. For example:
+
+```
+git commit -a -m "docs: add custom funnel demo and related docs"
+
+```
+
+VisActor's commit messages follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification, with **docs used for demos**.
+
+`[optional scope]: `
+
+Common `type` values include docs (documentation, log changes), feat (new feature), fix (bug fix), refactor (code refactoring), etc. Please choose according to the actual situation.
+
+Write a short and precise description in English.
+
+Before submitting the commit, we will perform a commit lint check. You can check the specific rules [here](https://github.com/VisActor/VStory/blob/develop/common/autoinstallers/lint/commitlint.config.js).
+
+A common issue is when the upstream (@visactor/vstroy) has new updates, which may cause conflicts when submitting a Pull Request. Therefore, before submitting, merge the commits from other developers with your own commits. Use the following code to switch to the develop branch:
+
+```
+git checkout develop
+
+```
+
+Pull the latest code from the remote:
+
+```
+git pull upstream develop
+
+```
+
+Switch back to your development branch:
+
+```
+git checkout docs/add-funnel-demo
+
+```
+
+Merge the commits from develop into your branch:
+
+```
+git rebase develop
+
+```
+
+Push the updated code to your branch:
+
+```
+git push origin docs/add-funnel-demo
+
+```
+
+# Submit a PR
+
+You can click on the `Compare & pull request` button on your GitHub repository page.
+
+
+
+Or create one through the `contribute` button:
+
+Fill in the modifications for this submission according to the template:
+
+- Check the type of modification
+
+
+
+- Fill in the associated issue
+
+
+
+- If there are complex changes, explain the background and solution
+
+
+
+After filling in the relevant information, click on Create pull request to submit.
+
+An administrator will review the PR and decide whether to approve it. If it is not approved, you will need to make modifications and resubmit.
+
+# Next Steps
+
+Next, you can read about the implementation principles and source code explanations for each module, or join in contributing to these documents.
+
+Join the VisActor family and contribute your efforts!
+
+GitHub: [github.com/VisActor](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FVisActor)
+
+VisActor WeChat Subscription Account (you can join the WeChat group through the subscription account menu):
+
+
+
+VisActor Official Website: [www.visactor.io/](https://link.juejin.cn/?target=https%3A%2F%2Fwww.visactor.io%2Fvtable)
+
+Feishu Group:
+
+
+
+Discord: https://discord.com/invite/3wPyxVyH6m
+
+# This Document is Contributed by the Following Individuals
+
+[玄魂](https://github.com/xuanhun)
diff --git a/docs/assets/contributing/en/6-How to upload image.md b/docs/assets/contributing/en/6-How to upload image.md
new file mode 100644
index 000000000..b04cc83bc
--- /dev/null
+++ b/docs/assets/contributing/en/6-How to upload image.md
@@ -0,0 +1,39 @@
+# How to Upload Images
+Images are often used in tutorial documents to provide visual explanations.
+We recommend using a stable image hosting service to upload images. Here, we introduce the solution of using cdn.jsdelivr.net as an image hosting service.
+
+## 6.1 Upload Images
+First, create a folder in your personal GitHub repository to store the images used in the document.
+For example, the directory https://github.com/xuanhun/articles/tree/main/visactor/img contains a large number of images. Taking the image A3gybJqQLo7vH8xV8I3cA7NknRc.gif as an example, the corresponding GitHub URL is:
+https://github.com/xuanhun/articles/blob/main/visactor/img/A3gybJqQLo7vH8xV8I3cA7NknRc.gif.
+
+In the specific markdown document, we convert it to the cdn.jsdelivr.net URL:
+
+https://cdn.jsdelivr.net/gh/[username]/[repo name]/[image path]
+
+
+
+# Next Steps
+
+You can now read the implementation principles and source code explanations of each module, or join in contributing to these documents.
+
+Join the VisActor family and contribute your efforts!
+
+GitHub: [github.com/VisActor](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FVisActor)
+
+VisActor WeChat Subscription Account (you can join the WeChat group through the subscription account menu):
+
+
+
+VisActor Official Website: [www.visactor.io/](https://link.juejin.cn/?target=https%3A%2F%2Fwww.visactor.io%2Fvtable)
+
+Feishu Group:
+
+
+
+Discord: https://discord.com/invite/3wPyxVyH6m
+
+
+# This document is contributed by the following individuals
+
+[玄魂](https://github.com/xuanhun)
diff --git a/docs/assets/contributing/menu.json b/docs/assets/contributing/menu.json
new file mode 100644
index 000000000..e72fc80b0
--- /dev/null
+++ b/docs/assets/contributing/menu.json
@@ -0,0 +1,47 @@
+{
+ "menu": "contributing",
+ "children": [
+ {
+ "path": "1-Setting-Up-the-Development-Environment",
+ "title": {
+ "zh": "1.搭建开发环境",
+ "en": "1-Setting-Up-the-Development-Environment"
+ }
+ },
+ {
+ "path": "2-Howto Submit an Issue",
+ "title": {
+ "zh": "2.如何提issue",
+ "en": "2-Howto Submit an Issue"
+ }
+ },
+ {
+ "path": "3-How to Contribute Documentation",
+ "title": {
+ "zh": "3.如何贡献文档",
+ "en": "3-How to Contribute Documentation"
+ }
+ },
+ {
+ "path": "4-How to Contribute to a Demo",
+ "title": {
+ "zh": "4.如何贡献demo",
+ "en": "4-How to Contribute to a Demo"
+ }
+ },
+ {
+ "path": "5-How to Contribute Code",
+ "title": {
+ "zh": "5.如何贡献代码",
+ "en": "5-How to Contribute Code"
+ }
+ },
+ {
+ "path": "6-How to upload image",
+ "title": {
+ "zh": "6.如何上传图片资源",
+ "en": "6-How to upload image"
+ }
+ }
+ ]
+}
diff --git a/docs/assets/contributing/zh/1-Setting-Up-the-Development-Environment.md b/docs/assets/contributing/zh/1-Setting-Up-the-Development-Environment.md
new file mode 100644
index 000000000..952623521
--- /dev/null
+++ b/docs/assets/contributing/zh/1-Setting-Up-the-Development-Environment.md
@@ -0,0 +1,122 @@
+---
+title: 1.搭建开发环境
+
+key words: VisActor,VChart,VTable,VStory,VMind,VGrammar,VRender,Visualization,Chart,Data,Table,Graph,Gis,LLM
+---
+
+# Github
+
+## 1.1 注册账号
+
+VisActor 团队通常在 github 上进行开发和 issue 维护,请打开 [Github 网站](https://github.com/),点击右上角 `Sign up` 按钮,注册一个自己的账号,开启你开源之旅的第一步。
+
+如果因为特殊情况,你无法打开 Github 站点,请告知我们并通过 [Gitee](https://gitee.com/VisActor/VRender) 进行项目开发。
+
+## 1.2 Fork 项目
+
+首先需要 fork 这个项目,进入[VRender 项目页面](https://github.com/VisActor/VRender),点击右上角的 Fork 按钮
+
+
+
+你的 github 帐号中会出现 xxxx(你的 github 用户名)/vrender 这个项目
+
+
+
+# 本地开发环境
+
+## 2.1 安装 git
+
+由于代码托管在 github 上,我们使用 git 做版本控制。
+
+Git 是一种版本控制系统,用于跟踪和管理软件开发项目中的代码变更。它帮助开发者记录和管理代码的历史记录,方便团队协作、代码版本控制、合并代码等操作。通过 Git,您可以追踪每个文件的每个版本,并轻松地在不同版本之间进行切换和比较。Git 还提供了分支管理功能,使得可以同时进行多个并行开发任务。
+
+- 访问 Git 官方网站:[https://git-scm.com/](https://git-scm.com/)
+
+- 下载最新版本的 Git 安装程序。
+
+- 运行下载的安装程序,按照安装向导的提示进行安装。
+
+- 安装完成后,你可以通过命令行使用 `git version` 命令确认安装成功。
+
+```
+git version
+**git version 2.39.2 (Apple Git-143)**
+
+```
+
+## 2.2 安装开发工具(推荐 VSCode)
+
+VisActor 整体上属于前端技术栈,能进行前端开发的工具很多,我们推荐使用 VScode。当然,你也可以使用你喜欢的开发工具。
+
+如果你对 VSCode 不是很熟悉的话,建议阅读官方文档:https://vscode.js.cn/docs/setup/setup-overview
+
+## 2.3 安装 豆包 Marscode AI 编程助手
+
+[Marscode AI 编程助手](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a)
+
+豆包 MarsCode 编程助手是豆包旗下的 AI 编程助手,提供以智能代码补全为代表的 AI 功能。它支持主流的编程语言和 IDE,在开发过程中提供单行代码或整个函数的编写建议。此外,它还支持代码解释、单测生成和问题修复等功能,提高了开发效率和质量。 更多信息,请参考[豆包 MarsCode 编程助手的文档](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a)。
+
+
+
+借助 Marscode,VisActor 开发者可以更方便的进行代码理解、文档撰写、功能开发、单元测试等多项任务。在详细的各项任务贡献指南中,会有更详细的案例说明。
+
+
+
+## 2.4 Clone 代码到本地
+
+进入 VRender 文件夹,添加 VRender 的远程地址
+
+```
+git remote add upstream https://github.com/VisActor/VRender.git
+
+```
+
+获取 VRender 最新源码
+
+```
+git pull upstream develop
+
+```
+
+# 初始化项目
+
+首先,全局安装 [@microsoft/rush](https://rushjs.io/pages/intro/get_started/)
+
+```
+$ npm i --global @microsoft/rush
+
+```
+
+接下来执行命令查看 demo
+
+```
+# 安装依赖
+$ rush update
+# 启动 vrender 的 demo 页
+$ rush run -p @visactor/vrender -s start
+# 启动本地文档站点
+$ rush docs
+
+```
+
+# 下一步
+
+到目前为止,你已经做好了开发代码的准备了。接下来请继续阅读下一节教程,开始不同类型的任务。
+
+github :[github.com/VisActor](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FVisActor)
+
+VisActor 微信订阅号留言(可以通过订阅号菜单加入微信群):
+
+
+
+VisActor 官网:[www.visactor.io/](https://link.juejin.cn/?target=https%3A%2F%2Fwww.visactor.io%2Fvtable)
+
+飞书群:
+
+
+
+discord:https://discord.com/invite/3wPyxVyH6m
+
+# 本文档由由以下人员贡献
+
+[玄魂](https://github.com/xuanhun)
diff --git a/docs/assets/contributing/zh/2-Howto Submit an Issue.md b/docs/assets/contributing/zh/2-Howto Submit an Issue.md
new file mode 100644
index 000000000..872434370
--- /dev/null
+++ b/docs/assets/contributing/zh/2-Howto Submit an Issue.md
@@ -0,0 +1,61 @@
+---
+title: 2.如何提issue
+
+key words: VisActor,VChart,VTable,VStory,VMind,VGrammar,VRender,Visualization,Chart,Data,Table,Graph,Gis,LLM
+---
+
+每个项目的 issues 栏目下面,可以进行 issue 的创建、搜索等工作。
+
+比如 VRender issues:https://github.com/VisActor/VRender/issues
+
+
+
+# 确定是否存在同样的 issue
+
+可以通过搜索过滤,查看 issue 细节的方式确定是否已经存在相同的 issue。
+
+# 创建 issue
+
+如果没有找到类似的 issue,你可以点击"New issue"按钮。
+
+
+
+以“**Documentation Request”,**点击“Get Start”按钮,**填写 issue 表单。**
+
+
+
+# **提交 issue**
+
+填写完 issue 表单后,点击"Submit new issue"按钮提交你的 issue。
+
+# **跟进 issue**
+
+提交 issue 后,你可以在仓库的"Issues"选项卡中查看你的 issue 状态。项目开发者可能会要求你提供更多的信息或者告诉你他们正在处理这个 issue。
+
+# **关闭 issue**
+
+如果你的问题得到了解决或者你的需求得到了满足,项目开发者会关闭这个 issue。你也可以自己关闭 issue,如果你认为问题已经解决或者不再需要进一步的帮助。
+
+通过以上步骤,你可以在 GitHub 上成功地为开源项目提 issue。记得在描述问题时尽量详细和清晰,这样有助于项目开发者更快地理解和解决你的问题。
+
+# 下一步
+
+到目前为止,你已经熟悉了 issue 的概念。接下来请继续阅读下一节教程,开始不同类型的任务。
+
+github :[github.com/VisActor](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FVisActor)
+
+VisActor 微信订阅号留言(可以通过订阅号菜单加入微信群):
+
+
+
+VisActor 官网:[www.visactor.io/](https://link.juejin.cn/?target=https%3A%2F%2Fwww.visactor.io%2Fvtable)
+
+飞书群:
+
+
+
+discord:https://discord.com/invite/3wPyxVyH6m
+
+# 本文档由由以下人员贡献
+
+[玄魂](https://github.com/xuanhun)
diff --git a/docs/assets/contributing/zh/3-How to Contribute Documentation.md b/docs/assets/contributing/zh/3-How to Contribute Documentation.md
new file mode 100644
index 000000000..7c185184e
--- /dev/null
+++ b/docs/assets/contributing/zh/3-How to Contribute Documentation.md
@@ -0,0 +1,259 @@
+---
+title: 3.如何贡献文档
+
+key words: VisActor,VChart,VTable,VStory,VMind,VGrammar,VRender,Visualization,Chart,Data,Table,Graph,Gis,LLM
+---
+
+# 创建分支
+
+VRender 默认分支为 develop 分支。无论是功能开发、bug 修复、文档编写,都请新建立一个分支,再合并到 develop 分支上。使用以下代码创建分支:
+
+```
+// 创建文档、demo分支
+git checkout -b docs/add-funnel-demo
+```
+
+# 寻找或者创建 issue
+
+原则上,我们规定每一个 pr 都要有对应的 issue。在开始开发之前,请确认是否有对应的 issue,且 issue 没有被认领。
+
+## 搜索文档 issue
+
+可以通过如下方式搜索文档相关的 issue:
+
+```
+is:open label:docs
+
+```
+
+
+
+其中他有些 feature 会关联 doc 标签,可以进一步看一下该 issue 是不是纯文档任务。
+
+## 创建文档 issue
+
+点击 “NEW ISSUE”,打开 issue 选择页面,选择“**Documentation Request”。**
+
+
+
+填写你要提交的文档 issue 相关信息即可。
+
+
+
+# 认领 issue
+
+如果你想撰写文档或者修改文档 bug,可以在该 issue 下留言认领。管理员会联系你,确认后将 issue assign 给你。
+
+例如:
+
+
+
+# 创建或者修改文档
+
+VRender 文档和 demo 在项目的中的位置如下:
+
+
+
+目前文档类型如下:
+
+- examples:图元示例,对应站点:
+
+https://www.visactor.io/vrender/example
+
+- guide:教程,对应站点:https://www.visactor.io/vrender/guide/asd/VRender_Website_Guide
+
+找到对应的文档位置进行新增或者修改。这里需要注意的是部分文档需要同时维护 “menu.json” 文件。
+
+
+
+该文件对应文档最后在站点上显示的位置和名称等。例如
+
+
+
+# 借助豆包 Marscode AI 编程助手 进行文档写作
+
+[Marscode AI 编程助手](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a)
+
+借助豆包[Marscode AI 编程助手](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a),可以在文档创作的整个流程中提供全方面的帮助。
+
+如果你还没有安装[Marscode AI 编程助手](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a),请从该链接进入下载页面:https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a
+
+在文档写作中,合理使用 context 指令,可以提升内容的准确性。
+
+`**⭐️ #Workspace**`
+
+选择 Workspace 中的全局代码作为上下文,AI 将根据用户 Query 自动寻找相关代码 Context
+
+
+
+`**⭐️ #Files**`
+
+搜索选择代码仓库中的文件作为上下文
+
+
+
+`**⭐️ #Code**`
+
+搜索选择代码仓库中的函数、类作为上下文
+
+
+
+下面举例说明,如何使用[Marscode AI 编程助手](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) 进行文档写作。
+
+## 5.1 提供文档写作思路
+
+这里 **通过 # 唤起 #Workspace ,**然后进行提问,希望它帮忙生成一份开发者文档大纲。
+
+
+
+## 5.2 生成项目结构说明
+
+这里 **通过 # 唤起 #Workspace ,**然后进行提问,希望它帮忙生成一份项目结构说明文档。
+
+
+
+我们仍然可以针对子文件夹,进行进一步的提问。
+
+
+
+## 5.3 生成文件或代码详解
+
+### 5.3.1 生成代码说明
+
+当我们在文件中选择一段代码,可以从悬浮菜单中选择 Explain 命令,[Marscode](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) 会为我们生成详细的代码解释。我们可以在此基础上,进行校对和改编。
+
+
+
+也可以直接在对话框中输入 Explain 命令。
+
+
+
+这里也可以直接使用上面提到的 #Code context 来结合 Explain 和你的指令来进行更细节的任务。
+
+### 5.3.2 生成针对整个文件的说明
+
+Explain 可以和 Context 或者 Files 命令搭配使用,用来生成针对整个文件的说明文档。
+
+
+
+## 5.4 生成示例代码
+
+为了更好的解释说明原理和用法,通常需要给出可以实际运行的 demo,可以利用 [Marscode](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) 的代码生成能力为我们生成示例代码。不过目前各种 AI 的代码生成能力都不能保证准确,还需要进一步的进行验证。
+
+## 5.5 内容检索
+
+通常我们的每个问答,[Marscode](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) 都会给出参考文档,这些文档可以给我们提供更多参考上下文,供进一步分析。
+
+
+
+也可以直接进行文件检索:
+
+
+
+## 5.6 翻译文档
+
+VisActor 的文档需要同时提供中英文,Marscode 可以辅助用来进行翻译。
+
+# 提交代码
+
+文档完成之后,先把代码 push 到你的远程分支。例如:
+
+```
+git commit -a -m "docs: add custom funnel demo and related docs"
+
+```
+
+VisActor 的 commit 提交信息遵循 [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) 规范
+
+`[optional scope]: `
+
+其中常用的`type`包括 docs(文档、日志修改)、feat(新功能)、fix(问题修复)、refactor(代码重构)等,请根据实际情况选择。
+
+请用简短精确的英文描述编写 description
+
+提交 commit 之前,我们会进行 commit lint 检查,具体可以查看[检查规则](https://github.com/VisActor/VRender/blob/develop/common/autoinstallers/lint/commitlint.config.js)。
+
+一个常见的问题是远程的 upstream (@visactor/vrender) 有了新的更新, 从而会导致我们提交的 Pull Request 时会导致冲突。 因此我们可以在提交前先把远程其他开发者的 commit 和我们的 commit 合并。使用以下代码切换到 develop 分支:
+
+```
+git checkout develop
+
+```
+
+使用以下代码拉出远程的最新代码:
+
+```
+git pull upstream develop
+
+```
+
+切换回自己的开发分支:
+
+```
+git checkout docs/add-funnel-demo
+
+```
+
+把 develop 的 commit 合并到自己分支:
+
+```
+git rebase develop
+
+```
+
+把更新代码提交到自己的分支中:
+
+```
+git push origin docs/add-funnel-demo
+
+```
+
+# 提交 PR
+
+你可以在你的 github 代码仓库页面点击 `Compare & pull request` 按钮。
+
+
+
+或通过 `contribute` 按钮创建:
+
+按照模板填写本次提交的修改内容:
+
+- 勾选这是什么类型的修改
+
+
+
+- 填写关联的 issue
+
+
+
+- 若有复杂变更,请说明背景和解决方案
+
+
+
+相关信息填写完成后,点击 Create pull request 提交。
+
+管理员会 review pr 决定是否通过,如果不通过需要进行修改然后重新提交。
+
+# 下一步
+
+不同的文档类型中,demo 文档有一些特殊要求,可以参考“如何贡献 demo”一节。
+
+接下来可以继续尝试不同类型的任务。
+
+github :[github.com/VisActor](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FVisActor)
+
+VisActor 微信订阅号留言(可以通过订阅号菜单加入微信群):
+
+
+
+VisActor 官网:[www.visactor.io/](https://link.juejin.cn/?target=https%3A%2F%2Fwww.visactor.io%2Fvtable)
+
+飞书群:
+
+
+
+discord:https://discord.com/invite/3wPyxVyH6m
+
+# 本文档由由以下人员贡献
+
+[玄魂](https://github.com/xuanhun)
diff --git a/docs/assets/contributing/zh/4-How to Contribute to a Demo.md b/docs/assets/contributing/zh/4-How to Contribute to a Demo.md
new file mode 100644
index 000000000..df433f15a
--- /dev/null
+++ b/docs/assets/contributing/zh/4-How to Contribute to a Demo.md
@@ -0,0 +1,332 @@
+---
+title: 4.如何贡献demo
+
+key words: VisActor,VChart,VTable,VStory,VMind,VGrammar,VRender,Visualization,Chart,Data,Table,Graph,Gis,LLM
+---
+
+# 创建分支
+
+VRender 默认分支为 develop 分支。无论是功能开发、bug 修复、文档编写,都请新建立一个分支,再合并到 develop 分支上。使用以下代码创建分支:
+
+```
+// 创建文档、demo分支
+git checkout -b docs/add-funnel-demo
+
+```
+
+# 寻找或者创建 issue
+
+原则上,我们规定每一个 pr 都要有对应的 issue。在开始开发之前,请确认是否有对应的 issue,且 issue 没有被认领。
+
+## 搜索 demo issue
+
+可以通过如下方式搜索 demo 相关的 issue:
+
+```
+ label:demos
+
+```
+
+
+
+其中他有些 feature 会关联 doc 标签,可以进一步看一下该 issue 是不是纯 demo 任务。
+
+## 创建 demo issue
+
+点击 “NEW ISSUE”,打开 issue 选择页面,选择“**Others”。**
+
+
+
+填写你要提交的文档 issue 相关信息,并打上“demos”标签即可。
+
+
+
+# 认领 issue
+
+如果你想提交 demo 或者修改 demo bug,可以在该 issue 下留言认领。管理员会联系你,确认后将 issue assign 给你。
+
+例如:
+
+
+
+# 创建或者修改 demo
+
+VRender 文档和 demo 在项目的中的位置如下(examples):
+
+
+
+以 basic-arc 的示例文档为例(目前一份示例同时包含中英文版本,分别在 zh & en 的路径下):
+
+
+
+示例 Markdown 内容分为几个部分:
+
+- 元信息:示例内容的属性定义,包括图表分类、封面图、关键词等;
+
+- 标题:一级标题下的正文内容对应了示例的描述信息;
+
+- 关键配置:示例中所包含的关键配置说明,这一部分将在示例页面右侧的“关键配置”中呈现;
+
+- 代码演示:示例执行的具体代码内容,目前只支持原生的 JavaScript 代码。
+
+```js
+const graphics = [];
+graphics.push(
+ VRender.createArc({
+ innerRadius: 0,
+ outerRadius: 120,
+ startAngle: 0,
+ endAngle: Math.PI * 2,
+ x: 200,
+ y: 200,
+ fill: {
+ gradient: 'linear',
+ x0: 0,
+ y0: 0,
+ x1: 1,
+ y1: 0,
+ stops: [
+ { offset: 0, color: 'green' },
+ { offset: 0.5, color: 'orange' },
+ { offset: 1, color: 'red' }
+ ]
+ },
+ fillOpacity: 0.2,
+ background:
+ '',
+ texture: 'circle',
+ textureColor: 'orange',
+ stroke: 'green',
+ lineWidth: 2,
+ cap: false
+ })
+);
+
+graphics.push(
+ VRender.createArc({
+ innerRadius: 120,
+ outerRadius: 130,
+ startAngle: 0,
+ endAngle: Math.PI * 2,
+ x: 200,
+ y: 200,
+ stroke: '#63bbd0',
+ lineWidth: 2,
+ fill: {
+ x: 0.5,
+ y: 0.5,
+ startAngle: 0,
+ endAngle: 6.283185307179586,
+ stops: [
+ { offset: 0, color: '#ed2f6a' },
+ { offset: 0.2, color: '#621d34' },
+ { offset: 0.4, color: '#c08eaf' },
+ { offset: 0.6, color: '#806d9e' },
+ { offset: 0.8, color: '#2b73af' },
+ { offset: 1, color: '#2f90b9' }
+ ],
+ gradient: 'conical'
+ },
+ cap: [false, true],
+ cornerRadius: 26,
+ forceShowCap: true
+ })
+);
+
+const stage = new Stage({
+ container: CONTAINER_ID,
+ autoRender: true
+});
+
+graphics.forEach(g => {
+ stage.defaultLayer.add(g);
+});
+```
+
+其中 Markdown 的元信息的字段定义为:
+
+- group:示例的分类信息,描述了当前示例属于什么图表类别
+
+- title:示例的标
+
+- keywords:示例的关键词
+
+- order:示例在同个分组下的排序依据
+
+- cover:示例的封面图
+
+- tutorial:跳转的教程链接(默认的示例教程将会跳转到示例分组所对应的教程)
+
+目前图表示例的 group 包含多个分类,例如 graphic-arc 、graphic-area 等,对应到 VRender 示例画廊中全部图表下的分类。具体的分类字段可以参照已有的示例文档进行填写。
+
+
+
+完成新的 demo 编写后,可以在 docs/assets/examples/menu.json 文件中添加 demo 路径和标题:
+
+
+
+> Demo 制作过程中需要上传的图片资源,请参考[6.如何上传图片资源](./6-How%20to%20upload%20image)章节
+
+# 借助豆包 Marscode AI 编程助手进行 demo 编写
+
+借助豆包[Marscode AI 编程助手](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a),可以在文档创作的整个流程中提供全方面的帮助。
+
+如果你还没有安装[Marscode AI 编程助手](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a),请从该链接进入下载页面:https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a
+
+在 demo 编写中,合理使用 context 指令,可以提升内容的准确性。
+
+`**⭐️ #Workspace**`
+
+选择 Workspace 中的全局代码作为上下文,AI 将根据用户 Query 自动寻找相关代码 Context
+
+
+
+`**⭐️ #Files**`
+
+搜索选择代码仓库中的文件作为上下文
+
+
+
+`**⭐️ #Code**`
+
+搜索选择代码仓库中的函数、类作为上下文
+
+
+
+下面举例说明,如何使用[Marscode AI 编程助手](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) 进行 demo 编写。
+
+## 5.1 提供文档框架
+
+这里 **通过 # 唤起 #Workspace ,**然后进行提问,选中一份 example 的文档内容,希望它仿照生成一份新的 example 文档。
+
+
+
+我们可以在此基础上继续细节的调整。
+
+## 5.2 生成说明文字
+
+每个 demo 的说明文字可以先用 [Marscode](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) 生成,然后再做校对和调整。比如:
+
+
+
+## 5.3 生成示例代码
+
+为了更好的解释说明原理和用法,通常需要给出可以实际运行的 demo,可以利用 [Marscode](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) 的代码生成能力为我们生成示例代码。不过目前各种 AI 的代码生成能力都不能保证准确,还需要进一步的进行验证。
+
+## 5.4 内容检索
+
+通常我们的每个问答,[Marscode](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) 都会给出参考文档,这些文档可以给我们提供更多参考上下文,供进一步分析。
+
+
+
+也可以直接进行文件检索:
+
+
+
+# 提交代码
+
+文档完成之后,先把代码 push 到你的远程分支。例如:
+
+```
+
+git commit -a -m "docs: add custom funnel demo and related docs"
+
+```
+
+VisActor 的 commit 提交信息遵循 [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) 规范,**demo 使用 docs**
+
+`[optional scope]: `
+
+其中常用的`type`包括 docs(文档、日志修改)、feat(新功能)、fix(问题修复)、refactor(代码重构)等,请根据实际情况选择。
+
+请用简短精确的英文描述编写 description
+
+提交 commit 之前,我们会进行 commit lint 检查,具体可以查看[检查规则](https://github.com/VisActor/VRender/blob/develop/common/autoinstallers/lint/commitlint.config.js)。
+
+一个常见的问题是远程的 upstream (@visactor/vrender) 有了新的更新, 从而会导致我们提交的 Pull Request 时会导致冲突。 因此我们可以在提交前先把远程其他开发者的 commit 和我们的 commit 合并。使用以下代码切换到 develop 分支:
+
+```
+
+git checkout develop
+
+```
+
+使用以下代码拉出远程的最新代码:
+
+```
+
+git pull upstream develop
+
+```
+
+切换回自己的开发分支:
+
+```
+
+git checkout docs/add-funnel-demo
+
+```
+
+把 develop 的 commit 合并到自己分支:
+
+```
+
+git rebase develop
+
+```
+
+把更新代码提交到自己的分支中:
+
+```
+
+git push origin docs/add-funnel-demo
+
+```
+
+# 提交 PR
+
+你可以在你的 github 代码仓库页面点击 `Compare & pull request` 按钮。
+
+
+
+或通过 `contribute` 按钮创建:
+
+按照模板填写本次提交的修改内容:
+
+- 勾选这是什么类型的修改
+
+
+
+- 填写关联的 issue
+
+
+
+- 若有复杂变更,请说明背景和解决方案
+
+
+
+相关信息填写完成后,点击 Create pull request 提交。
+
+管理员会 review pr 决定是否通过,如果不通过需要进行修改然后重新提交。
+
+# 下一步
+
+接下来可以继续尝试不同类型的任务。
+
+github :[github.com/VisActor](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FVisActor)
+
+VisActor 微信订阅号留言(可以通过订阅号菜单加入微信群):
+
+
+
+VisActor 官网:[www.visactor.io/](https://link.juejin.cn/?target=https%3A%2F%2Fwww.visactor.io%2Fvtable)
+
+飞书群:
+
+
+
+discord:https://discord.com/invite/3wPyxVyH6m
+
+# 本文档由由以下人员贡献
+
+[玄魂](https://github.com/xuanhun)
diff --git a/docs/assets/contributing/zh/5-How to Contribute Code.md b/docs/assets/contributing/zh/5-How to Contribute Code.md
new file mode 100644
index 000000000..0edc894b5
--- /dev/null
+++ b/docs/assets/contributing/zh/5-How to Contribute Code.md
@@ -0,0 +1,244 @@
+---
+title: 5.如何贡献代码
+
+key words: VisActor,VChart,VTable,VStory,VMind,VGrammar,VRender,Visualization,Chart,Data,Table,Graph,Gis,LLM
+---
+
+# 创建分支
+
+VRender 默认分支为 develop 分支。无论是功能开发、bug 修复、文档编写,都请新建立一个分支,再合并到 develop 分支上。使用以下代码创建分支:
+
+```
+// 创建文档、demo分支
+git checkout -b docs/add-funnel-demo
+```
+
+# 寻找或者创建 issue
+
+原则上,我们规定每一个 pr 都要有对应的 issue。在开始开发之前,请确认是否有对应的 issue,且 issue 没有被认领。
+
+## 搜索 issue
+
+可以通过如下方式搜索 bug 或者 feature 相关的 issue:
+
+
+
+## 创建代码相关 issue
+
+点击 “NEW ISSUE”,打开 issue 选择页面,选择“**Bug Report” 或者 “Feature Request”。**
+
+
+
+填写你要提交的文档 issue 相关信息,并打上合适的标签即可。
+
+
+
+# 认领 issue
+
+如果你想贡献代码,可以在该 issue 下留言认领。管理员会联系你,确认后将 issue assign 给你。
+
+例如:
+
+
+
+# 编写代码
+
+VRender 生态所有的组件都在同一目录下,按包名进行拆分,开发者需要在自己的代码分支上开发代码,然后进行提交。
+
+# 借助豆包 Marscode AI 编程助手 进行代码编写
+
+借助豆包[Marscode AI 编程助手](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a),可以在代码编写的整个流程中提供全方面的帮助。
+
+如果你还没有安装[Marscode AI 编程助手](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a),请从该链接进入下载页面:https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a
+
+在代码编写中,合理使用 context 指令,可以提升内容的准确性。
+
+`**⭐️ #Workspace**`
+
+选择 Workspace 中的全局代码作为上下文,AI 将根据用户 Query 自动寻找相关代码 Context
+
+
+
+`**⭐️ #Files**`
+
+搜索选择代码仓库中的文件作为上下文
+
+
+
+`**⭐️ #Code**`
+
+搜索选择代码仓库中的函数、类作为上下文
+
+
+
+下面举例说明,如何使用[Marscode AI 编程助手](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) 进行代码编写。
+
+## 5.1 快速熟悉整个仓库
+
+这里 **通过 # 唤起 #Workspace ,**然后进行提问,希望它帮忙生成一份项目结构说明文档。
+
+
+
+我们仍然可以针对子文件夹,进行进一步的提问。
+
+
+
+## 5.2 解释代码
+
+### 5.2.1 生成代码说明
+
+当我们在文件中选择一段代码,可以从悬浮菜单中选择 Explain 命令,[Marscode](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) 会为我们生成详细的代码解释。我们可以在此基础上,进行校对和改编。
+
+
+
+也可以直接在对话框中输入 Explain 命令。
+
+
+
+这里也可以直接使用上面提到的 #Code context 来结合 Explain 和你的指令来进行更细节的任务。
+
+### 5.2.2 生成针对整个文件的说明
+
+Explain 可以和 Context 或者 Files 命令搭配使用,用来生成针对整个文件的说明文档。
+
+
+
+## 5.3 内容检索
+
+通常我们的每个问答,[Marscode](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) 都会给出参考文档,这些文档可以给我们提供更多参考上下文,供进一步分析。
+
+
+
+也可以直接进行文件检索:
+
+
+
+## 5.4 代码生成
+
+在日常编码中经常会碰到使用重复代码的场景,有时候可能并不知道某个功能是否有现成的函数已实现,此时用 `#Workspace` 来进行提问。例如:
+
+
+
+## 5.5 添加注释
+
+使用 "/doc"命令生成代码注释。
+
+
+
+## 5.6 生成单测
+
+VRender 单元测试代码在每个 package 的 “**\*\*tests**” \*\* 目录下。
+
+
+
+使用 [Marscode](https://www.marscode.cn/home?utm_source=developer&utm_medium=oss&utm_campaign=visactor_a) “/test” 指令可以快速的生成单测代码。
+
+
+
+## 5.7 智能提示
+
+编写过程中,智能生成可选代码是编程助手的标配功能,大家自行体验吧。
+
+# 提交代码
+
+文档完成之后,先把代码 push 到你的远程分支。例如:
+
+```
+git commit -a -m "docs: add custom funnel demo and related docs"
+
+```
+
+VisActor 的 commit 提交信息遵循 [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) 规范,**demo 使用 docs**
+
+`[optional scope]: `
+
+其中常用的`type`包括 docs(文档、日志修改)、feat(新功能)、fix(问题修复)、refactor(代码重构)等,请根据实际情况选择。
+
+请用简短精确的英文描述编写 description
+
+提交 commit 之前,我们会进行 commit lint 检查,具体可以查看[检查规则](https://github.com/VisActor/VStory/blob/develop/common/autoinstallers/lint/commitlint.config.js)。
+
+一个常见的问题是远程的 upstream (@visactor/vstroy) 有了新的更新, 从而会导致我们提交的 Pull Request 时会导致冲突。 因此我们可以在提交前先把远程其他开发者的 commit 和我们的 commit 合并。使用以下代码切换到 develop 分支:
+
+```
+git checkout develop
+
+```
+
+使用以下代码拉出远程的最新代码:
+
+```
+git pull upstream develop
+
+```
+
+切换回自己的开发分支:
+
+```
+git checkout docs/add-funnel-demo
+
+```
+
+把 develop 的 commit 合并到自己分支:
+
+```
+git rebase develop
+
+```
+
+把更新代码提交到自己的分支中:
+
+```
+git push origin docs/add-funnel-demo
+
+```
+
+# 提交 PR
+
+你可以在你的 github 代码仓库页面点击 `Compare & pull request` 按钮。
+
+
+
+或通过 `contribute` 按钮创建:
+
+按照模板填写本次提交的修改内容:
+
+- 勾选这是什么类型的修改
+
+
+
+- 填写关联的 issue
+
+
+
+- 若有复杂变更,请说明背景和解决方案
+
+
+
+相关信息填写完成后,点击 Create pull request 提交。
+
+管理员会 review pr 决定是否通过,如果不通过需要进行修改然后重新提交。
+
+# 下一步
+
+接下来你可以阅读每一个模块的实现原理及源码详解,也可以加入贡献这些文档。
+
+请加入 VisActor 大家庭,贡献你的力量吧!
+
+github :[github.com/VisActor](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FVisActor)
+
+VisActor 微信订阅号(可以通过订阅号菜单加入微信群):
+
+
+
+VisActor 官网:[www.visactor.io/](https://link.juejin.cn/?target=https%3A%2F%2Fwww.visactor.io%2Fvtable)
+
+飞书群:
+
+
+
+discord:https://discord.com/invite/3wPyxVyH6m
+
+# 本文档由由以下人员贡献
+
+[玄魂](https://github.com/xuanhun)
diff --git a/docs/assets/contributing/zh/6-How to upload image.md b/docs/assets/contributing/zh/6-How to upload image.md
new file mode 100644
index 000000000..2bea66a8d
--- /dev/null
+++ b/docs/assets/contributing/zh/6-How to upload image.md
@@ -0,0 +1,45 @@
+# 如何上传图片
+教程文档中通常需要使用图片来辅助说明。
+我们推荐使用稳定的图床来上传图片,这里介绍使用 cdn.jsdelivr.net 图床方案。
+
+## 6.1 上传图片
+首先在你的个人github 仓库中创建一个文件夹,用来存放文档中使用的图片。
+比如,https://github.com/xuanhun/articles/tree/main/visactor/img 目录下存储了大量的图片,以其中A3gybJqQLo7vH8xV8I3cA7NknRc.gif 为例, 对应的github url 为:
+https://github.com/xuanhun/articles/blob/main/visactor/img/A3gybJqQLo7vH8xV8I3cA7NknRc.gif。
+
+在具体的md 文档中,我们将其转换为cdn.jsdelivr.net的URL:
+
+https://cdn.jsdelivr.net/gh/[username]/[repo name]/[imgae path]
+
+
+
+# 下一步
+
+接下来你可以阅读每一个模块的实现原理及源码详解,也可以加入贡献这些文档。
+
+请加入VisActor 大家庭,贡献你的力量吧!
+
+
+
+github :[github.com/VisActor](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FVisActor)
+
+VisActor 微信订阅号(可以通过订阅号菜单加入微信群):
+
+
+
+VisActor 官网:[www.visactor.io/](https://link.juejin.cn/?target=https%3A%2F%2Fwww.visactor.io%2Fvtable)
+
+飞书群:
+
+
+
+discord:https://discord.com/invite/3wPyxVyH6m
+
+
+# 本文档由由以下人员贡献
+
+[玄魂](https://github.com/xuanhun)
+
+# 本文档由由以下人员贡献
+
+[玄魂](https://github.com/xuanhun)
\ No newline at end of file
diff --git a/docs/assets/examples/en/graphic-arc/basic-arc.md b/docs/assets/examples/en/graphic-arc/basic-arc.md
index aaece42cf..3e84f829d 100644
--- a/docs/assets/examples/en/graphic-arc/basic-arc.md
+++ b/docs/assets/examples/en/graphic-arc/basic-arc.md
@@ -9,6 +9,14 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/base-arc.png
# arc graphic
+The `Arc` primitive is a basic graphic element commonly used to represent curves or arcs. In computer graphics and graphic design, the Arc primitive is used to depict a portion of a circle or any arbitrary curve, typically defined by a center point, radius, start angle, and end angle.
+
+Key features:
+- Center Point: The central position of the arc, usually a coordinate point.
+- Inner Radius: The distance from the center point to any point on the inner arc, determining the size of the inner arc.
+- Outer Radius: The distance from the center point to any point on the outer arc, determining the size of the outer arc.
+- Start Angle and End Angle: Determine the starting and ending positions of the arc, typically expressed in degrees or radians.
+
## code demo
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/en/graphic-area/basic-area.md b/docs/assets/examples/en/graphic-area/basic-area.md
index 5beafb05f..a67507232 100644
--- a/docs/assets/examples/en/graphic-area/basic-area.md
+++ b/docs/assets/examples/en/graphic-area/basic-area.md
@@ -9,6 +9,10 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/area.jpeg
# area graphic
+The `area` element is used to represent changes in data as a graphical element, commonly used in area charts. Area charts display the size and trend of values by filling the area below the line, emphasizing the overall quantity of data rather than just individual values.
+
+This element is mainly defined by the `points` array, where each item is an object with properties such as x1, x2, y1, and y2 to represent the two points above and below.
+
## code demo
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/en/graphic-circle/basic-circle.md b/docs/assets/examples/en/graphic-circle/basic-circle.md
index 8e3370ba2..81e7566e3 100644
--- a/docs/assets/examples/en/graphic-circle/basic-circle.md
+++ b/docs/assets/examples/en/graphic-circle/basic-circle.md
@@ -9,6 +9,13 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/circle-base.p
# circle graphic
+`Circle` primitive is a basic graphic element used to represent circles or circle-related graphics. In computer graphics, graphic design, and data visualization, the `Circle` primitive is the foundation for building various graphics and charts.
+
+Key features:
+- Center Point: The central position of the circle, usually represented by a coordinate point.
+- Radius: The distance from the center point to any point on the circumference, determining the size of the circle.
+- Boundary: The outer boundary of the circle is a smooth curve, where all points on the boundary are equidistant from the center point.
+
## code demo
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/en/graphic-circle/circle-gradient.md b/docs/assets/examples/en/graphic-circle/circle-gradient.md
index c2adcc8dc..585aaf6b0 100644
--- a/docs/assets/examples/en/graphic-circle/circle-gradient.md
+++ b/docs/assets/examples/en/graphic-circle/circle-gradient.md
@@ -9,6 +9,8 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/circle-gradie
# circle graphic
+The `Circle` primitive supports gradient color effects.
+
## code demo
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/en/graphic-glyph/basic-glyph.md b/docs/assets/examples/en/graphic-glyph/basic-glyph.md
index f362f3a8a..f6c6618ab 100644
--- a/docs/assets/examples/en/graphic-glyph/basic-glyph.md
+++ b/docs/assets/examples/en/graphic-glyph/basic-glyph.md
@@ -8,6 +8,7 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/glyph-basic.p
---
# glyph graphic
+The `glyph` primitive refers to complex graphic elements composed of multiple basic primitives (such as lines, circles, rectangles, etc.). This design approach allows for the creation of richer visual effects and functionality, widely used in graphic design, data visualization, and user interface design.
## code demo
diff --git a/docs/assets/examples/en/graphic-line/basic-line.md b/docs/assets/examples/en/graphic-line/basic-line.md
index dc15c675b..b1ccb8d39 100644
--- a/docs/assets/examples/en/graphic-line/basic-line.md
+++ b/docs/assets/examples/en/graphic-line/basic-line.md
@@ -9,6 +9,13 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/line-basic.pn
# line graphic
+The `Line` primitive is a basic graphical element used to represent the trend of data changes, commonly used in line charts and other types of charts. It forms a line by connecting data points, visually displaying the changes in data over time or other variables.
+
+Key features:
+- Data Points: The Line primitive represents numerical values through a series of coordinate points, typically plotted in a two-dimensional coordinate system.
+- Line Segments: Data points are connected by straight lines or curves to form a continuous line, making it easy to observe the trend of data changes.
+- Visual Effects: The color, thickness, and style of the line can be adjusted as needed to highlight different data series or degrees of change.
+
## code demo
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/en/graphic-path/basic-path.md b/docs/assets/examples/en/graphic-path/basic-path.md
index 496888bf8..92e629fcc 100644
--- a/docs/assets/examples/en/graphic-path/basic-path.md
+++ b/docs/assets/examples/en/graphic-path/basic-path.md
@@ -9,6 +9,8 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/path-basic.jp
# path graphic
+The `Path` element is a basic graphic element used to draw complex shapes and curves, widely used in computer graphics, graphic design, and data visualization. It creates arbitrary shapes by defining a series of line segments and curves, providing flexibility and expressiveness. Its syntax is consistent with the `path` tag in SVG.
+
## code demo
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/en/graphic-rect/basic-rect.md b/docs/assets/examples/en/graphic-rect/basic-rect.md
index dfb84f2fe..453813974 100644
--- a/docs/assets/examples/en/graphic-rect/basic-rect.md
+++ b/docs/assets/examples/en/graphic-rect/basic-rect.md
@@ -9,6 +9,13 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/rect-basic.pn
# rect graphic
+The `Rect` (rectangle) primitive is a basic graphic element used to represent rectangular or square shapes. It is widely used in computer graphics, graphic design, and data visualization due to its simplicity and ease of use.
+
+Key features:
+- Position: Rectangles are typically defined by the coordinates of their top-left corner, determining their position in the coordinate system.
+- Width and Height: The size of a rectangle is determined by its width and height, which can be any positive value, creating rectangles of different sizes.
+- Border and Fill: Rectangles can have border colors, styles, and thickness set, and can also have a fill color to enhance visual effects.
+
## code demo
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/en/graphic-richtext/basic-richtext.md b/docs/assets/examples/en/graphic-richtext/basic-richtext.md
index f2db26427..0ae6d5ff3 100644
--- a/docs/assets/examples/en/graphic-richtext/basic-richtext.md
+++ b/docs/assets/examples/en/graphic-richtext/basic-richtext.md
@@ -9,6 +9,8 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/richtext-basi
# richtext graphic
+The `RichText` element is a type of graphic element used to display and handle diverse text content, widely used in graphical user interfaces, web design, and data visualization. Unlike plain text, rich text elements support various text formats and styles, making text information more rich and vivid.
+
## code demo
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/en/graphic-symbol/basic-symbol.md b/docs/assets/examples/en/graphic-symbol/basic-symbol.md
index aec35f903..ef886e0a0 100644
--- a/docs/assets/examples/en/graphic-symbol/basic-symbol.md
+++ b/docs/assets/examples/en/graphic-symbol/basic-symbol.md
@@ -9,6 +9,8 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/symbol-basic.
# symbol graphic
+The syntax of the `symbol` primitive is similar to the `path` primitive, but it supports some built-in shapes and also supports `size` to control the pixel size.
+
## code demo
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/en/graphic-symbol/symbol-wave.md b/docs/assets/examples/en/graphic-symbol/symbol-wave.md
index 9c9c895df..aa8b8b606 100644
--- a/docs/assets/examples/en/graphic-symbol/symbol-wave.md
+++ b/docs/assets/examples/en/graphic-symbol/symbol-wave.md
@@ -9,6 +9,8 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/wave01.gif
# symbol wave
+3D effect of waves
+
## code demo
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/en/graphic-text/basic-text.md b/docs/assets/examples/en/graphic-text/basic-text.md
index 10ca67d1e..cddb4e1d7 100644
--- a/docs/assets/examples/en/graphic-text/basic-text.md
+++ b/docs/assets/examples/en/graphic-text/basic-text.md
@@ -9,6 +9,13 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/text-basic.pn
# text graphic
+The `Text` element is a basic graphic element used to display simple text information, widely used in computer graphics, user interface design, and data visualization. It is mainly used to render characters and textual content, providing information and identification.
+
+Main features:
+- Font and style: The Text element can be styled with different fonts, sizes, colors, and styles (such as bold, italic) to meet design requirements.
+- Positioning: Text is usually positioned relative to anchor points through its align and baseline, allowing for flexible placement.
+- Line height: The line height can be adjusted to improve the readability and visual effect of the text.
+
## code demo
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/en/graphic-text/text-blend.md b/docs/assets/examples/en/graphic-text/text-blend.md
index 4633ca12a..0b9d6f13a 100644
--- a/docs/assets/examples/en/graphic-text/text-blend.md
+++ b/docs/assets/examples/en/graphic-text/text-blend.md
@@ -9,6 +9,8 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/text-blend.pn
# text graphic
+`text`支持canvas原生的`globalCompositeOperation`系列配置的效果
+
## code demo
```javascript livedemo template=vrender
@@ -24,7 +26,7 @@ const t1 = createText({
fontFamily: 'Lato',
fontWeight: 'bolder',
fill: '#08fff9',
- blend: 'lighten',
+ globalCompositeOperation: 'lighten',
x: x,
y: y
});
@@ -36,7 +38,7 @@ const t2 = createText({
fontSize: 120,
fontFamily: 'Lato',
fontWeight: 'bolder',
- blend: 'lighten',
+ globalCompositeOperation: 'lighten',
fill: '#f00044',
x: x + delta,
y: y + delta
diff --git a/docs/assets/examples/zh/graphic-arc/basic-arc.md b/docs/assets/examples/zh/graphic-arc/basic-arc.md
index e96e71bfb..5c3fd5e63 100644
--- a/docs/assets/examples/zh/graphic-arc/basic-arc.md
+++ b/docs/assets/examples/zh/graphic-arc/basic-arc.md
@@ -9,6 +9,14 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/base-arc.png
# arc 图元
+`Arc`图元是一种基本的图形元素,通常用于表示曲线或弧线。在计算机图形学和图形设计中,Arc图元用于描绘圆弧或任意曲线的部分,通常由中心点、半径、起始角度和结束角度来定义。
+
+主要特征:
+- 中心点:弧线的中心位置,通常是一个坐标点。
+- 内半径:从中心点到内弧线上的任意点的距离,决定了内弧的大小。
+- 外半径:从中心点到外弧线上的任意点的距离,决定了外弧的大小。
+- 起始角度与结束角度:确定弧线的起始和结束位置,通常以度数或弧度表示。
+
## 代码演示
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/zh/graphic-arc/fan.md b/docs/assets/examples/zh/graphic-arc/fan.md
index 64b16c288..54594656f 100644
--- a/docs/assets/examples/zh/graphic-arc/fan.md
+++ b/docs/assets/examples/zh/graphic-arc/fan.md
@@ -9,6 +9,8 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/fan.gif
# arc 扇子
+A demo of a fan
+
## 代码演示
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/zh/graphic-area/basic-area.md b/docs/assets/examples/zh/graphic-area/basic-area.md
index 4ecdcb17d..493eff080 100644
--- a/docs/assets/examples/zh/graphic-area/basic-area.md
+++ b/docs/assets/examples/zh/graphic-area/basic-area.md
@@ -9,6 +9,10 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/area.jpeg
# area 图元
+`area`图元是用于表示数据变化的图形元素,通常在面积图中使用。面积图通过填充线下方的区域来展示数值的大小和变化趋势,强调数据的总体量而不仅仅是单一数值。
+
+该图元主要通过`points`数组定义形状,其中每项是个对象,有x1、x2、y1、y2几个属性用来表示上下的两个点
+
## 代码演示
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/zh/graphic-circle/basic-circle.md b/docs/assets/examples/zh/graphic-circle/basic-circle.md
index c377f4cc0..ab309f29a 100644
--- a/docs/assets/examples/zh/graphic-circle/basic-circle.md
+++ b/docs/assets/examples/zh/graphic-circle/basic-circle.md
@@ -9,6 +9,13 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/circle-base.p
# circle 图元
+`Circle`图元是一种基本的图形元素,用于表示圆形或圆形相关的图形。在计算机图形学、图形设计和数据可视化中,Circle图元是构建各种图形和图表的基础。
+
+主要特征:
+- 中心点:圆形的中心位置,通常用一个坐标点表示。
+- 半径:从中心点到圆周上任意点的距离,决定圆的大小。
+- 边界:圆形的外边界是一个平滑的曲线,所有边界上的点到中心点的距离相等。
+
## 代码演示
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/zh/graphic-circle/circle-gradient.md b/docs/assets/examples/zh/graphic-circle/circle-gradient.md
index be008c65b..abc406282 100644
--- a/docs/assets/examples/zh/graphic-circle/circle-gradient.md
+++ b/docs/assets/examples/zh/graphic-circle/circle-gradient.md
@@ -7,7 +7,9 @@ order: 1-0
cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/circle-gradient.png
---
-# circle 图元
+# circle 渐变色
+
+`Circle`图元支持渐变色的效果
## 代码演示
diff --git a/docs/assets/examples/zh/graphic-glyph/basic-glyph.md b/docs/assets/examples/zh/graphic-glyph/basic-glyph.md
index e5a12555b..9c0d10474 100644
--- a/docs/assets/examples/zh/graphic-glyph/basic-glyph.md
+++ b/docs/assets/examples/zh/graphic-glyph/basic-glyph.md
@@ -9,6 +9,8 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/glyph-basic.p
# glyph 图元
+`glyph`图元是指由多个基本图元(如线、圆、矩形等)组合而成的复杂图形元素。这种设计方法允许创建更丰富的视觉效果和功能性,广泛应用于图形设计、数据可视化和用户界面设计中。
+
## 代码演示
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/zh/graphic-line/basic-line.md b/docs/assets/examples/zh/graphic-line/basic-line.md
index bfadfdf8c..646e0283f 100644
--- a/docs/assets/examples/zh/graphic-line/basic-line.md
+++ b/docs/assets/examples/zh/graphic-line/basic-line.md
@@ -9,6 +9,13 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/line-basic.pn
# line 图元
+`Line`图元是用于表示数据变化趋势的基本图形元素,通常在折线图和其他图表中使用。它通过连接数据点形成线段,直观地展示数据随时间或其他变量的变化。
+
+主要特征:
+- 数据点:Line图元通过一系列坐标点来表示数值,这些点通常在二维坐标系中绘制。
+- 线段:数据点之间通过直线或曲线连接,形成连续的线,便于观察数据的变化趋势。
+- 可视化效果:线条的颜色、粗细和样式可以根据需要进行调整,以突出不同的数据系列或变化程度。
+
## 代码演示
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/zh/graphic-path/basic-path.md b/docs/assets/examples/zh/graphic-path/basic-path.md
index 962533475..542a58d8b 100644
--- a/docs/assets/examples/zh/graphic-path/basic-path.md
+++ b/docs/assets/examples/zh/graphic-path/basic-path.md
@@ -9,6 +9,8 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/path-basic.jp
# path 图元
+`Path`(路径)图元是用于绘制复杂形状和曲线的基本图形元素,广泛应用于计算机图形学、图形设计和数据可视化中。它通过定义一系列的线段和曲线来创建任意形状,具有灵活性和表现力。其语法和`svg`中的`path`标签保持一致
+
## 代码演示
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/zh/graphic-rect/basic-rect.md b/docs/assets/examples/zh/graphic-rect/basic-rect.md
index 08db9785a..042291b6c 100644
--- a/docs/assets/examples/zh/graphic-rect/basic-rect.md
+++ b/docs/assets/examples/zh/graphic-rect/basic-rect.md
@@ -9,6 +9,13 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/rect-basic.pn
# rect 图元
+`Rect`(矩形)图元是一种基本的图形元素,用于表示矩形或正方形形状。它在计算机图形学、图形设计以及数据可视化中广泛应用,因其简单易用而受到喜爱。
+
+主要特征:
+- 位置:矩形通常通过其左上角的坐标来定义,确定矩形在坐标系中的位置。
+- 宽度与高度:矩形的尺寸由其宽度和高度决定,可以是任意正值,从而形成不同大小的矩形。
+- 边框与填充:矩形可以设置边框的颜色、样式和厚度,同时也可以选择填充颜色,以增强视觉效果。
+
## 代码演示
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/zh/graphic-rect/morphing-animate.md b/docs/assets/examples/zh/graphic-rect/morphing-animate.md
index db0673478..bf8c24935 100644
--- a/docs/assets/examples/zh/graphic-rect/morphing-animate.md
+++ b/docs/assets/examples/zh/graphic-rect/morphing-animate.md
@@ -7,7 +7,7 @@ order: 1-0
cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/morphing.gif
---
-# rect 图元
+# morphing-animate
## 代码演示
diff --git a/docs/assets/examples/zh/graphic-richtext/basic-richtext.md b/docs/assets/examples/zh/graphic-richtext/basic-richtext.md
index 95b76e430..227bfcfae 100644
--- a/docs/assets/examples/zh/graphic-richtext/basic-richtext.md
+++ b/docs/assets/examples/zh/graphic-richtext/basic-richtext.md
@@ -9,6 +9,8 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/richtext-basi
# richtext 图元
+`RichText`(富文本)图元是一种用于显示和处理多样化文本内容的图形元素,广泛应用于图形用户界面、网页设计和数据可视化中。与普通文本不同,富文本图元支持多种文本格式和样式,使得文本信息更加丰富和生动。
+
## 代码演示
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/zh/graphic-symbol/basic-symbol.md b/docs/assets/examples/zh/graphic-symbol/basic-symbol.md
index bac3f9da1..d47efd8fe 100644
--- a/docs/assets/examples/zh/graphic-symbol/basic-symbol.md
+++ b/docs/assets/examples/zh/graphic-symbol/basic-symbol.md
@@ -9,6 +9,8 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/symbol-basic.
# symbol 图元
+`symbol`图元的语法和`path`图元类似,但支持一些内置的形状,以及支持`size`来控制像素大小。
+
## 代码演示
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/zh/graphic-symbol/symbol-wave.md b/docs/assets/examples/zh/graphic-symbol/symbol-wave.md
index c8430d87a..64bd1beef 100644
--- a/docs/assets/examples/zh/graphic-symbol/symbol-wave.md
+++ b/docs/assets/examples/zh/graphic-symbol/symbol-wave.md
@@ -9,6 +9,8 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/wave01.gif
# symbol 波浪
+波浪的3d效果
+
## 代码演示
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/zh/graphic-text/basic-text.md b/docs/assets/examples/zh/graphic-text/basic-text.md
index a71ebc506..31e5a89b4 100644
--- a/docs/assets/examples/zh/graphic-text/basic-text.md
+++ b/docs/assets/examples/zh/graphic-text/basic-text.md
@@ -9,6 +9,13 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/text-basic.pn
# text 图元
+`Text`(文本)图元是用于显示简单文本信息的基本图形元素,在计算机图形学、用户界面设计和数据可视化中广泛应用。它主要用于呈现字符和文字内容,提供信息和标识。
+
+主要特征:
+- 字体和样式:文本图元可以设置不同的字体、大小、颜色和样式(如粗体、斜体),以满足设计需求。
+- 定位:文本通常通过其align和baseline相对锚点进行定位,允许灵活放置。
+- 行高:可以调整行高,以改善文本的可读性和视觉效果。
+
## 代码演示
```javascript livedemo template=vrender
diff --git a/docs/assets/examples/zh/graphic-text/text-blend.md b/docs/assets/examples/zh/graphic-text/text-blend.md
index 108bae2d8..fe371d0a6 100644
--- a/docs/assets/examples/zh/graphic-text/text-blend.md
+++ b/docs/assets/examples/zh/graphic-text/text-blend.md
@@ -9,6 +9,8 @@ cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/text-blend.pn
# text 图元混合
+`text`支持canvas原生的`globalCompositeOperation`系列配置的效果
+
## 代码演示
```javascript livedemo template=vrender
@@ -24,7 +26,7 @@ const t1 = createText({
fontFamily: 'Lato',
fontWeight: 'bolder',
fill: '#08fff9',
- blend: 'lighten',
+ globalCompositeOperation: 'lighten',
x: x,
y: y
});
@@ -36,7 +38,7 @@ const t2 = createText({
fontSize: 120,
fontFamily: 'Lato',
fontWeight: 'bolder',
- blend: 'lighten',
+ globalCompositeOperation: 'lighten',
fill: '#f00044',
x: x + delta,
y: y + delta
diff --git a/docs/assets/guide/en/asd/Advanced_Tutorial/Custom_Graphic.md b/docs/assets/guide/en/asd/Advanced_Tutorial/Custom_Graphic.md
new file mode 100644
index 000000000..b72256e82
--- /dev/null
+++ b/docs/assets/guide/en/asd/Advanced_Tutorial/Custom_Graphic.md
@@ -0,0 +1,274 @@
+# Custom Graphic Elements
+
+VRender provides a variety of graphic elements, but sometimes we need to customize graphic elements, such as needing a graphic element that can play [Lottie](https://lottiefiles.com/) animations. In this case, we need to customize graphic elements. Customizing graphic elements in VRender requires the following steps:
+
+1. Define a graphic element class, which is a subclass of `Graphic`. Of course, you can also directly inherit from an existing graphic element class, such as `Rect`, `Circle`, etc.
+2. Implement the drawing logic of the graphic element, which is used for the drawing operations of the graphic element. This class is a subclass of `BaseRender`. Similarly, you can also directly inherit from an existing graphic element rendering class, such as `DefaultCanvasRectRender`, `DefaultCanvasCircleRender`, etc.
+3. Implement the picking logic of the graphic element, which is used for the picking operations of the graphic element. This class implements the `IGraphicPicker` interface. If your graphic element is in the shape of a rectangle, you can directly inherit from `RectPickerBase`. Note that in some scenarios (such as mini-programs), picking based on Canvas is not supported, so you need to implement a set of `MathPicker` logic. However, all picking logics are not complicated, and we will explain them in detail in the following sections.
+
+Next, we will use registering a `Lottie` graphic element as an example to explain how to customize graphic elements. All the code is in the `@visactor/vrender-kits` package, and the effect is demonstrated below.
+
+
+
+Note: Before reading this section, it is recommended to read the [Graphic](./Graphic) section first.
+
+## Dependency Injection
+
+Based on the dependency injection mechanism, we can easily inject various extension functions into VRender. All the functions of custom graphic elements we need to implement rely on dependency injection to achieve. Our dependency injection capability is based on `inversifyJS` transformation, so the API is aligned with `inversifyJS`. If you want to delve deeper, it is recommended to read the [`inversifyJS` documentation](https://inversify.io/).
+
+## Preparation
+
+Before writing the code, let's think about the specific implementation plan:
+
+1. It is easy to determine that the `Lottie` graphic element is a rectangular area graphic element, and the `Lottie` animation is played in this graphic element. Therefore, we can directly inherit from the `Rect` graphic element and add the ability to play `Lottie` animations to it.
+
+2. We also found that the Lottie official provides a player [lottie-web](https://github.com/airbnb/lottie-web), which can parse and play Lottie animations. We can use it directly. Therefore, in the rendering logic, we only need to use the official player to draw the animation on our graphic element.
+
+3. Picking is very simple. We do not need to specifically pick a certain element in the Lottie animation. We only need to pick the entire area where the Lottie animation is played - that is, our graphic element. Therefore, the picking logic can directly reuse the logic of the rectangle.
+
+Before implementing the specific code, let's take a look at how the Lottie official player plays the animation in the [demo](https://codepen.io/collection/nVYWZR/). We found that the code is very simple. We only need to pass the JSON or URL of the Lottie file, and then wrap it in our graphic element.
+
+## Define the Graphic Element Class
+
+First, we need to define the `Lottie` graphic element class, which inherits from `Graphic` and has the `ILottieGraphicAttribute` interface attribute.
+First, we define the `ILottieGraphicAttribute` interface, which is the configuration interface passed to the graphic element, used to define the width, height, position attributes of the graphic element, etc. In the `Lottie` graphic element, this interface needs to add the `data` attribute to save the JSON data of the Lottie animation.
+
+```ts
+type ILottieAttribute = {
+ data: string;
+};
+type ILottieGraphicAttribute = Partial & Partial;
+
+interface ILottie extends IGraphic {
+ lottieInstance?: AnimationItem; // Save the instance of the Lottie player
+ canvas?: any; // Save the canvas instance of the Lottie player
+}
+```
+
+Next, by inheriting from a Rect graphic element, we implement a `Lottie` graphic element. We only need to add the management logic of the external Lottie player to the Lottie graphic element, initialize it at the appropriate time, and destroy this player when the graphic element is destroyed.
+
+```ts
+export class Lottie extends Rect implements ILottie {
+ type: any = 'lottie';
+ declare attribute: ILottieGraphicAttribute;
+ declare lottieInstance?: AnimationItem;
+ declare canvas?: any;
+
+ static NOWORK_ANIMATE_ATTR = NOWORK_ANIMATE_ATTR;
+
+ constructor(params: ILottieGraphicAttribute) {
+ super(params);
+ this.numberType = LOTTIE_NUMBER_TYPE;
+ this.initLottieWeb(this.attribute.data);
+ }
+
+ /* When setting attributes, try to reinitialize the Lottie player */
+ setAttributes(params: Partial, forceUpdateTag?: boolean, context?: any): void {
+ if (params.data) {
+ this.initLottieWeb(params.data);
+ }
+ return super.setAttributes(params, forceUpdateTag, context);
+ }
+
+ setAttribute(key: string, value: any, forceUpdateTag?: boolean, context?: any): void {
+ if (key === 'data') {
+ this.initLottieWeb(value);
+ }
+ return super.setAttribute(key, value, forceUpdateTag, context);
+ }
+
+ getGraphicTheme(): Required {
+ return getTheme(this).rect;
+ }
+
+ /* Initialize the Lottie player */
+ initLottieWeb(data: string) {
+ // Must be in a browser environment
+ if (vglobal.env !== 'browser') {
+ return;
+ }
+ if (this.lottieInstance) {
+ this.releaseLottieInstance();
+ }
+ const theme = this.getGraphicTheme();
+ const { width = theme.width, height = theme.height } = this.attribute;
+ const canvas = vglobal.createCanvas({ width, height, dpr: vglobal.devicePixelRatio });
+ const params: any = {
+ // wrapper: svgContainer,
+ rendererSettings: {
+ context: canvas.getContext('2d')
+ },
+ animType: 'canvas',
+ loop: true
+ };
+ if (typeof data === 'string') {
+ params.path = data;
+ } else {
+ params.animationData = data;
+ }
+ this.lottieInstance = bodymovin.loadAnimation(params);
+ this.canvas = canvas;
+ // Every time a frame is rendered in Lottie, we need to render the graphic element again
+ this.lottieInstance.addEventListener('drawnFrame', this.renderNextFrame);
+ }
+
+ renderNextFrame = () => {
+ this.stage.renderNextFrame();
+ };
+
+ /* Add release logic */
+ release(): void {
+ super.release();
+ this.releaseLottieInstance();
+ }
+
+ releaseLottieInstance() {
+ this.lottieInstance.removeEventListener('drawnFrame', this.renderNextFrame);
+ this.lottieInstance.destroy();
+ this.lottieInstance = null;
+ }
+}
+
+export function createLottie(attributes: ILottieGraphicAttribute): ILottie {
+ return new Lottie(attributes);
+}
+```
+
+## Define the Rendering Logic
+
+Our graphic element has been defined. Next, we need to define the rendering logic of the graphic element. We need to define a `DefaultCanvasLottieRender` class, which inherits from `DefaultCanvasRectRender` and implements the `IGraphicRender` interface.
+Here, we only need to implement the `drawShape` interface. When rendering the rectangle, there are two callback functions `fillCb` and `strokeCb`. In `fillCb`, we need to generate a pattern of the Lottie canvas and draw it on the graphic element.
+
+```ts
+@injectable()
+export class DefaultCanvasLottieRender extends DefaultCanvasRectRender implements IGraphicRender {
+ type: 'glyph';
+ numberType: number = LOTTIE_NUMBER_TYPE;
+
+ drawShape(
+ lottie: ILottie,
+ context: IContext2d,
+ x: number,
+ y: number,
+ drawContext: IDrawContext,
+ params?: IGraphicRenderDrawParams,
+ fillCb?: (
+ ctx: IContext2d,
+ markAttribute: Partial,
+ themeAttribute: IThemeAttribute
+ ) => boolean,
+ strokeCb?: (
+ ctx: IContext2d,
+ markAttribute: Partial,
+ themeAttribute: IThemeAttribute
+ ) => boolean
+ ): void {
+ const _fillCb = fillCb || (() => this._drawShape.call(this, lottie, context, x, y, drawContext, params));
+ super.drawShape(lottie, context, x, y, drawContext, params, _fillCb, strokeCb);
+ }
+
+ _drawShape(
+ lottie: ILottie,
+ context: IContext2d,
+ x: number,
+ y: number,
+ drawContext: IDrawContext,
+ params?: IGraphicRenderDrawParams
+ ): void {
+ const lottieAttribute = this.tempTheme ?? getTheme(lottie, params?.theme).rect;
+ const { x: originX = lottieAttribute.x, y: originY = lottieAttribute.y } = lottie.attribute;
+ context.setCommonStyle(lottie, lottie.attribute, originX - x, originY - y, lottieAttribute);
+ // Set pattern, draw Lottie
+ const canvas = lottie.canvas;
+ if (canvas) {
+ // const _ctx = canvas.getContext('2d');
+ const pattern = context.createPattern(canvas, 'no-repeat');
+ const dpr = context.dpr;
+ pattern.setTransform && pattern.setTransform(new DOMMatrix([1 / dpr, 0, 0, 1 / dpr, x, y]));
+ context.fillStyle = pattern;
+ }
+ context.fill();
+ }
+}
+```
+
+## Custom Picking Logic
+
+Our Lottie graphic element has been implemented. Next, we need to implement the picking logic. We need to define a `DefaultCanvasLottiePicker` class, which inherits from `RectPickerBase` and implements the `IGraphicPicker` interface. The logic here is very simple because our picking is based on picking a rectangle, so after inheriting the picking class of the rectangle, we don't need to do anything.
+
+```ts
+@injectable()
+export class DefaultCanvasLottiePicker extends RectPickerBase implements IGraphicPicker {
+ constructor(@inject(RectRender) public readonly canvasRenderer: IGraphicRender) {
+ super();
+ }
+}
+```
+
+## Registration
+
+Finally, we need to implement the registration logic for graphic element registration, rendering registration, and picking registration.
+
+1. Graphic Element Registration
+
+Graphic elements do not need to be registered. One thing to note is that the `numberType` field of the graphic element needs to be consistent with the `numberType` field of the corresponding rendering class and picking class.
+
+2. Rendering Registration
+
+Rendering logic needs to be registered through dependency injection.
+
+```ts
+let loadLottieModule = false;
+export const lottieModule = new ContainerModule(bind => {
+ if (loadLottieModule) {
+ return;
+ }
+ loadLottieModule = true;
+ // Lottie renderer
+ bind(DefaultCanvasLottieRender).toSelf().inSingletonScope();
+ bind(GraphicRender).toService(DefaultCanvasLottieRender);
+});
+```
+
+3. Picking Registration
+
+Similar to rendering registration, picking classes are also registered through dependency injection.
+
+```ts
+let loadLottiePick = false;
+export const lottieCanvasPickModule = new ContainerModule((bind, unbind, isBound, rebind) => {
+ if (loadLottiePick) {
+ return;
+ }
+ loadLottiePick = true;
+ bind(CanvasLottiePicker).to(DefaultCanvasLottiePicker).inSingletonScope();
+ bind(CanvasPickerContribution).toService(CanvasLottiePicker);
+});
+```
+
+## Usage
+
+Next, we can load the relevant code of the `Lottie` graphic element in the code and use it.
+
+```ts
+container.load(lottieModule);
+container.load(lottieCanvasPickModule);
+
+const lottie = createLottie({
+ data: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/custom-graphic-lottie-animate.json',
+ width: 300,
+ height: 300,
+ x: 100,
+ y: 100,
+ cornerRadius: 20,
+ background: 'pink'
+});
+
+const stage = createStage({
+ canvas: 'main',
+ autoRender: true
+});
+
+stage.defaultLayer.add(lottie);
+window['stage'] = stage;
+```
diff --git a/docs/assets/guide/en/asd/Advanced_Tutorial/Flex_Layout.md b/docs/assets/guide/en/asd/Advanced_Tutorial/Flex_Layout.md
new file mode 100644
index 000000000..d8466bee4
--- /dev/null
+++ b/docs/assets/guide/en/asd/Advanced_Tutorial/Flex_Layout.md
@@ -0,0 +1,141 @@
+# What is BoundsPadding
+
+*Note:* By default, `VRender` does not have a layout similar to the `DOM`. For example, in the `DOM`, you can have two `div` elements, and the second `div` will be placed below the first `div`. However, in `VRender`, if you have two rectangles, the second rectangle will overlay the first one. This is because all positioning in `VRender` is relative positioning, *it relies on the x, y parameters you configure for positioning*, with the coordinate system having the origin at the top left corner, the positive direction of the x-axis to the right, and the positive direction of the y-axis downwards. This difference in layout between `VRender` and the `DOM` is due to this positioning system.
+
+However, we also provide the ability to use `flex` layout in `VRender`. By enabling this feature, we can achieve similar layout capabilities to the `DOM` Flex layout in `VRender`.
+
+## Usage
+
+The layout capabilities in VRender are achieved through plugins. To enable the layout plugin, you need to set it in the stage parameters:
+
+```ts
+const stage = VRender.createStage({
+ container: CONTAINER_ID,
+ autoRender: true,
+ enableLayout: true, // Enable layout capabilities
+});
+```
+
+Once enabled, the layout of the scene tree still defaults to relative positioning, based on the configured `x` and `y` values as well as the relative position of the parent element to determine the position of the current element. However, you can enable `flex` layout by setting the `flex` property on elements. When enabled, the child elements will be laid out according to the rules of `flex` layout.
+
+The configurable properties interface is as follows, following the same rules as the Flex layout rules in browsers:
+
+```ts
+display?: 'relative' | 'inner-block' | 'flex';
+flexDirection?: 'row' | 'row-reverse' | 'column' | 'column-reverse';
+flexWrap?: 'nowrap' | 'wrap';
+justifyContent?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around';
+alignItems?: 'flex-start' | 'flex-end' | 'center';
+alignContent?: 'flex-start' | 'center' | 'space-between' | 'space-around';
+```
+
+The layout will be based on the size of the AABBBounds of the child elements. You can dynamically adjust the AABBBounds of elements using BoundsPadding to achieve the desired layout effect. For more information, you can refer to [BoundsPadding](../FAQ/What_Is_BoundsPadding).
+
+Here is an example demonstration:
+
+```javascript livedemo template=vrender
+const group = VRender.createGroup({ x: 100, y: 100, width: 260, height: 80, background: '#cecece', display: 'flex' });
+
+const g1 = VRender.createGroup({
+ display: 'flex',
+ background: 'green',
+ width: 60,
+ height: 80,
+ direction: 'column',
+ alignItems: 'center',
+ justifyContent: 'space-around'
+});
+
+const img = VRender.createImage({
+ image: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg',
+ width: 50,
+ height: 50
+})
+g1.add(img);
+
+const g2 = VRender.createGroup({
+ display: 'flex',
+ background: 'red',
+ width: 200,
+ height: 80,
+ direction: 'column'
+});
+
+const g21 = VRender.createGroup({
+ display: 'flex',
+ background: 'orange',
+ width: 200,
+ height: 40,
+ direction: 'column',
+ alignItems: 'center',
+ justifyContent: 'center'
+});
+
+const text1 = VRender.createText({ text: 'Virtual Anchor Xiao Hua', fontSize: 13, fontFamily: 'sans-serif', fill: 'black' });
+const icon = VRender.createImage({
+ image: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/location.svg',
+ width: 15,
+ height: 15,
+ boundsPadding: [0, 0, 0, 10]
+});
+const text2 = VRender.createText({ text: 'Dream City', fontSize: 11, fontFamily: 'sans-serif', fill: '#6f7070' });
+
+g21.add(text1);
+g21.add(icon);
+g21.add(text2);
+
+const g22 = VRender.createGroup({
+ display: 'flex',
+ background: 'pink',
+ width: 200,
+ height: 40,
+ direction: 'column',
+ alignItems: 'center'
+});
+
+['Game', 'Anime', 'Food'].forEach(text => {
+ const tag = new VRenderComponent.Tag({
+ visible: true,
+ textStyle: {
+ fontSize: 10,
+ fill: 'rgb(51, 101, 238)',
+ textAlign: 'left',
+ textBaseline: 'top',
+ fontFamily: 'sans-serif'
+ },
+ space: 4,
+ padding: 5,
+ shape: {
+ fill: '#000'
+ },
+ text,
+ panel: {
+ visible: true,
+ fill: '#f4f4f2',
+ cornerRadius: 5
+ },
+ marginLeft: 10,
+ boundsPadding: [0, 0, 0, 10],
+ x: 20,
+ y: 10
+ });
+
+ g22.add(tag);
+});
+
+group.add(g1);
+group.add(g2);
+
+g2.add(g21);
+g2.add(g22);
+
+const stage = new VRender.Stage({
+ container: CONTAINER_ID,
+ autoRender: true,
+ enableLayout: true
+});
+
+stage.defaultLayer.add(group);
+
+window['stage'] = stage;
+```
diff --git a/docs/assets/guide/en/asd/Basic/VRender_basic_tutorial.md b/docs/assets/guide/en/asd/Basic/VRender_basic_tutorial.md
index fbe7eadf3..3e1287c66 100644
--- a/docs/assets/guide/en/asd/Basic/VRender_basic_tutorial.md
+++ b/docs/assets/guide/en/asd/Basic/VRender_basic_tutorial.md
@@ -14,7 +14,7 @@ VRender is a relatively low-level rendering library. We will introduce the usage
## Graphic System
-This section will quickly introduce the graphic system. For more detailed understanding, please refer to [Graphic System (waiting for document writing)](./graphic)
+This section will quickly introduce the graphic system. For more detailed understanding, please refer to [Graphic System](./Basic_Tutorial/Graphic)
### Create Graphics
diff --git a/docs/assets/guide/en/asd/Basic_Tutorial/Animate.md b/docs/assets/guide/en/asd/Basic_Tutorial/Animate.md
index 289fe861f..cd23af9f6 100644
--- a/docs/assets/guide/en/asd/Basic_Tutorial/Animate.md
+++ b/docs/assets/guide/en/asd/Basic_Tutorial/Animate.md
@@ -1,126 +1,132 @@
# Animation
+Animations in VRender are implemented using the `Animate` instance, which provides various interpolation methods such as `to`, `from`, `wait`, `loop`, `bounce`, `reverse`, `startAt`, and more. These methods can be chained together to create complex animation effects. We create an `Animate` instance using the element's API `animate()`. Before diving into this section, it is recommended to have a good understanding of the element's API to better grasp the usage of animations. You can refer to the [Element](./Graphic) section for the documentation of elements.
-# Basic Usage
+## Basic Usage
+
+Here is a basic demo of creating animations:
+
+```javascript livedemo template=vrender
+const rect = VRender.createRect({ x: 100, y: 100, width: 100, height: 100, fill: 'red' });
+
+const stage = new VRender.Stage({
+ container: CONTAINER_ID,
+ autoRender: true
+});
+
+stage.defaultLayer.add(rect);
+
+// Create animation with chained calls
+rect
+ .animate()
+ .to({ height: 200 }, 2000, 'quadIn')
+ .to({ x: 300 }, 200, 'quadIn')
+ .wait(2000)
+ .to({ y: 300 }, 200, 'backInOut')
+```
## Graphic.Animate
-Graphic.animate creates an animation, providing hooks such as `onStart`, `onEnd`, and `onFrame`.
+`graphic.animate` creates an animation and provides hooks like `onStart`, `onEnd`, `onFrame`.
```TypeScript
graphic
.animate({
onStart: () => {
- console.log('开始');
+ console.log('Start');
},
onEnd: () => {
- console.log('结束');
+ console.log('End');
},
onFrame: () => {
- console.log('某一帧');
+ console.log('Frame');
}
})
- .to({ height: 200 }, 2000, 'quadIn')
- .to({ x: 600 }, 200, 'quadIn')
- .wait(200)
- .to({ y: 600 }, 200, 'backInOut')
- .to({ fillColor: 'green' }, 2000, 'quadIn');
```
## Animate.to
-The most common interpolation method, interpolating from the current value to the target value.
+The most common interpolation method, interpolates from the current value to the target value.
```TypeScript
graphic
.animate()
- // 高度变化到200,耗时2000ms,插值函数是quadIn
.to({ height: 200 }, 2000, 'quadIn')
```
## Animate.wait
-Wait time.
+Wait for a specified amount of time.
```TypeScript
graphic
.animate()
- // 等待2000ms
.wait(2000)
```
## Animate.from
-The most common interpolation method, interpolating from the target value to the current value, which has the opposite effect of `to`.
+Similar to `to`, but interpolates from the target value to the current value.
```TypeScript
graphic
.animate()
- // 高度从200插值到当前值,耗时2000ms,插值函数是quadIn
.from({ height: 200 }, 2000, 'quadIn')
```
## Animate.reverse
-Reverse execution.
+Execute the animation in reverse.
```TypeScript
graphic
.animate()
- // 高度从200插值到当前值,耗时2000ms,插值函数是quadIn
.from({ height: 200 }, 2000, 'quadIn')
- // 反向执行,将from变成to的效果
.reverse();
```
## Animate.loop
-Looping.
+Loop the animation.
```TypeScript
graphic
.animate()
.to({ height: 200 }, 2000, 'quadIn')
- // 再走count次,一共走count+1次
.loop(count);
```
## Animate.bounce
-Round-trip, needs to be used in conjunction with `loop`.
+Bounce back and forth, needs to be used with `loop`.
```TypeScript
graphic
.animate()
.to({ height: 200 }, 2000, 'quadIn')
- // 执行一个来回,高度从当前变成2000,再从2000变成当前值
.bounce()
- // 循环count次,一共走count+1个单程
.loop(count);
```
## Animate.startAt
-Start at the `startAt` moment. This moment is not affected by the loop and will only run once.
+Start the animation from a specific time, unaffected by `loop`, only runs once.
```TypeScript
graphic
.animate()
.startAt(2000)
.to({ height: 200 }, 2000, 'quadIn')
- // 执行一个来回,高度从当前变成2000,再从2000变成当前值
.bounce()
- // 循环count次,一共走count+1个单程
.loop(count);
```
-# Sub-animations (subAnimate)
+## Sub Animation (subAnimate)
-Sub-animation divides the animation into different stages, each stage has independent loop, bounce, reverse, startAt, and other states, and different stages are executed sequentially.
+Sub animations divide the animation into different stages, each stage having independent `loop`, `bounce`, `reverse`, `startAt` states, and stages are executed sequentially.
```TypeScript
graphic
.animate()
- // 第一个动画,bounce再循环两次
.startAt(2000)
.to({ height: 200 }, 200, 'quadIn')
.to({ x: 200 }, 200, 'quadIn')
@@ -129,7 +135,6 @@ graphic
.to({ fillColor: 'green' }, 200, 'quadIn')
.bounce(true)
.loop(2)
- // 第二个动画,bounce再循环三次
.subAnimate()
.startAt(2000)
.to({ x: 300 }, 200, 'quadIn')
@@ -138,27 +143,27 @@ graphic
.loop(3);
```
-# Animation Arrangement
+## Animation Arrangement
-Animate supports some animation arrangement-related features, allowing users to link different animations without manually calculating the animation execution time.
+`animate` supports some animation arrangement-related features, allowing users to link different animations without manually calculating the execution time.
## Animate.after
-Execute after a certain animation has ended.
+Execute after a specific animation ends.
```TypeScript
const a1 = graphic.animate()
.startAt(2000)
.to({ height: 200 }, 200, 'quadIn');
const a2 = graphic.animate()
- .after(a1) // 在a1结束之后执行
+ .after(a1)
.startAt(2000)
.to({ height: 200 }, 200, 'quadIn');
```
## Animate.afterAll
-Execute after all animations within the passed animation array have ended.
+Execute after all animations in the provided array end.
```TypeScript
const a1 = graphic.animate()
@@ -167,32 +172,32 @@ const a1 = graphic.animate()
const a2 = graphic.animate()
.to({ height: 200 }, 200, 'quadIn');
const a3 = graphic.animate()
- .afterAll([a1, a2]) // a1和a2都执行完之后再执行
+ .afterAll([a1, a2])
.to({ height: 200 }, 200, 'quadIn');
```
## Animate.parallel
-Execute simultaneously with a certain animation in parallel.
+Execute in parallel with a specific animation.
```TypeScript
const a1 = graphic.animate()
.startAt(2000)
.to({ height: 200 }, 200, 'quadIn');
const a2 = graphic.animate()
- .after(a1) // 在a1结束之后执行
+ .after(a1)
.startAt(2000)
.to({ height: 200 }, 200, 'quadIn');
const a3 = graphic.animate()
- .parallel(a2) // 和a2同时执行
+ .parallel(a2)
.to({ height: 200 }, 200, 'quadIn');
```
-# Custom Interpolation
+## Custom Interpolation
## Animate.AddInterpolate
-Provides custom interpolation capabilities for a particular key. The registered function takes global effect, and returning true indicates successful interpolation.
+Provides the ability to customize interpolation for a specific key, registered functions are globally effective.
```TypeScript
Animate.AddInterpolate('text', (key, ratio, from, to, target, ret) => {
@@ -201,8 +206,8 @@ Animate.AddInterpolate('text', (key, ratio, from, to, target, ret) => {
ret.text = (_from + (_to - _from) * ratio).toString();
return true;
});
- // key传入空字符串,那么所有key都会匹配
- Animate.AddInterpolate('', (key, ratio, from, to, target, ret) => {
+
+Animate.AddInterpolate('', (key, ratio, from, to, target, ret) => {
if (key === 'text') {
const _from = parseInt(from);
const _to = parseInt(to);
@@ -213,9 +218,9 @@ Animate.AddInterpolate('text', (key, ratio, from, to, target, ret) => {
});
```
-## Animate parameter interpolate (higher priority)
+## Animate parameter `interpolate` (higher priority)
-Pass it in when creating the animate. This function only applies to this animate and returns true if the interpolation is successful.
+Pass a function when creating an `animate`, this function only applies to this specific `animate`.
```TypeScript
text.animate({
@@ -229,38 +234,32 @@ text.animate({
}).to({ text: '100' }, 1000, 'quartIn');
```
-# Custom Animation
+## Custom Animation
## Creation
-Extend ACustomAnimate, providing the main methods as follows.
+Inherit `ACustomAnimate` and provide main methods as follows:
```TypeScript
export class IncreateCount extends ACustomAnimate {
- // 这些参数都会被保存到this中
- // this.from = from; this.to = to; this.duration = duration; this.ease = ease;
constructor(from: any, to: any, duration: number, ease: EaseType) {
super(from, 0, duration, ease);
}
- // 结束时的属性
getEndProps(): Record {
return {
text: this.to
};
}
- // 绑定时调用,通常在这里会获取target的最初属性值
onBind(): void {
this.to = parseFloat(this.target.getAnimatePropByName('text'));
}
- // 结束时调用,通常不需要做任何操作
onEnd(): void {
return;
}
- // 插值时调用
onUpdate(end: boolean, ratio: number, out: Record): void {
out.text = this.from + (this.to - this.from) * ratio;
}
@@ -269,8 +268,12 @@ export class IncreateCount extends ACustomAnimate {
## Usage
-Pass in the custom animation in the play method.
+Pass the custom animation in the `play` method.
```TypeScript
-text .animate() .to({ fillColor: 'red' }, 1000, 'quadIn') .play(new IncreateCount(0, 0, 1000, 'quartIn')) .to({ fillColor: 'green' }, 1000, 'quadIn');
-```
\ No newline at end of file
+text
+ .animate()
+ .to({ fillColor: 'red' }, 1000, 'quadIn')
+ .play(new IncreateCount(0, 0, 1000, 'quartIn'))
+ .to({ fillColor: 'green' }, 1000, 'quadIn');
+```
diff --git a/docs/assets/guide/en/asd/Basic_Tutorial/Create_Instance.md b/docs/assets/guide/en/asd/Basic_Tutorial/Create_Instance.md
new file mode 100644
index 000000000..5407d1386
--- /dev/null
+++ b/docs/assets/guide/en/asd/Basic_Tutorial/Create_Instance.md
@@ -0,0 +1,163 @@
+# Creating Instances
+
+Similar to the DOM tree and React virtual node tree, VRender also draws based on a scene tree. VRender mounts this scene tree through layers, which are managed by the Stage. The Stage manages the lifecycle of the entire application, the position and size of views, and the logic for scene drawing and picking.
+
+As shown in the diagram below, a VRender application generally includes a `Stage`, which can have multiple layers (`Layer`) attached to it, and each layer can have multiple graphic elements attached to it.
+
+
+
+## Creating a Stage
+
+There are two ways to create a Stage: one is by using the `new` keyword, and the other is by using the `createStage` method. When creating a Stage, an object is passed as a parameter, and there are many configurable properties within the object. The most commonly used properties are as follows:
+
+- `container`: the mounting container, which needs to be a DOM element and is only available in the browser environment
+- `canvas`: the mounting canvas, which needs to be a Canvas element and is mutually exclusive with the container, can be used in different environments
+- `width`: the width of the canvas
+- `height`: the height of the canvas
+- `autoRender`: whether to automatically render; if set to true, there is no need to manually call the `stage.render()` method as it will render automatically
+- `background`: the background color of the canvas, defaulting to white
+
+```ts
+import { Stage, createStage } from '@visactor/vrender';
+// import { Stage, createStage } from '@visactor/vrender-core';
+
+const stage1 = new Stage({
+ container: document.getElementById('container'),
+ width: 600,
+ height: 600,
+ autoRender: true,
+ background: 'pink'
+})
+
+const stage2 = createStage({
+ container: document.getElementById('container'),
+ width: 600,
+ height: 600,
+ autoRender: true,
+ background: 'pink'
+})
+```
+
+All the parameters that the stage supports are as follows:
+
+```ts
+interface IStageParams {
+ // Viewport width and height
+ viewBox: IBoundsLike;
+ // Total width and height
+ width: number;
+ height: number;
+ dpr: number;
+ // Stage background
+ background: string | IColor;
+ // External canvas
+ canvas: string | HTMLCanvasElement;
+ // Canvas container, if canvas is not passed, a canvas will be created in the container
+ container: string | HTMLElement;
+ // Whether it is a controlled canvas; if not, it will not perform resize operations or modify the canvas style
+ canvasControled: boolean;
+ title: string;
+ // Whether to enable automatic rendering
+ autoRender: boolean;
+ // Whether to enable layout support
+ enableLayout: boolean;
+ // Whether to disable dirty bounds detection
+ disableDirtyBounds: boolean;
+ // Whether to support interactiveLayer, default is true
+ interactiveLayer: boolean;
+ // Whether to support HTML attributes
+ enableHtmlAttribute: string | boolean | HTMLElement;
+ // Whether to support react-dom (pass in ReactDOM)
+ ReactDOM: any;
+ // Whether to support scrollbars
+ enableScroll: boolean;
+ // Whether to support poptip
+ poptip: boolean;
+ // Hook function before rendering
+ beforeRender: (stage: IStage) => void;
+ // Hook function after rendering
+ afterRender: (stage: IStage) => void;
+ // Render style
+ renderStyle?: string;
+ // Custom ticker
+ ticker?: ITicker;
+ // List of enabled plugins
+ pluginList?: string[];
+ // Optimization configuration
+ optimize?: IOptimizeType;
+ /**
+ * Event system related configuration
+ */
+ event?: EventConfig;
+
+ /**
+ * @since 0.17.15
+ * Whether to support touch events; if not supported, touch events will not be listened to
+ */
+ supportsTouchEvents?: boolean;
+
+ /**
+ * @since 0.17.15
+ * Whether to support pointer events; if not supported, mouse events will be listened to
+ */
+ supportsPointerEvents?: boolean;
+
+ context?: IStageCreateContext;
+}
+```
+
+## Creating a Layer
+
+By default, Stage will create a layer `stage.defaultLayer`, which is a container used to mount graphic elements. A Stage can contain any number of layers. In VChart, we often use one layer as the main layer and create another layer to store tooltip elements and components.
+
+To create a layer, you can use the `stage.createLayer()` method.
+
+```ts
+createLayer(canvasId?: string, layerMode?: LayerMode): ILayer;
+```
+
+## Adding Graphic Elements
+
+VRender provides many graphic elements. For details on graphic elements, you can refer to the [Graphic](./Graphic) section. The creation of graphic elements is similar to Stage, providing two ways to create them. Taking a rectangle element as an example:
+
+```ts
+import { Rect, createRect } from '@visactor/vrender';
+
+const rect1 = new Rect({
+ x: 100,
+ y: 100,
+ width: 100,
+ height: 100,
+ fill: 'red'
+});
+
+const rect2 = createRect({
+ x: 100,
+ y: 100,
+ width: 100,
+ height: 100,
+ fill:'red'
+});
+```
+
+By adding the rectangle to `stage.defaultLayer`, it will be displayed.
+
+```javascript livedemo template=vrender
+// Register all necessary content
+const stage = new VRender.Stage({
+ container: CONTAINER_ID,
+ autoRender: true
+});
+
+const rect = VRender.createRect({
+ x: 100,
+ y: 100,
+ width: 100,
+ height: 100,
+ fill:'red'
+});
+
+stage.defaultLayer.add(rect);
+
+window['stage'] = stage;
+```
diff --git a/docs/assets/guide/en/asd/Basic_Tutorial/Cross_platform_Interface.md b/docs/assets/guide/en/asd/Basic_Tutorial/Cross_platform_Interface.md
index f5afe8845..fe29282c4 100644
--- a/docs/assets/guide/en/asd/Basic_Tutorial/Cross_platform_Interface.md
+++ b/docs/assets/guide/en/asd/Basic_Tutorial/Cross_platform_Interface.md
@@ -1,15 +1,85 @@
-# Cross-platform Interface Usage
+# Cross-platform API Usage
-VRender provides a series of default interfaces to shield the impact of cross-platform. Currently, it supports environments such as `Browser`, `Node`, `feishu`, `tt`, and other environments can be supported through extensions.
+This chapter is not necessary for development, but if your application needs to run in different environments (nodejs, browser, mini programs) and you don't want to write compatibility code yourself, such as getting devicePixelRatio (dpr), creating Canvas, or using some utility functions, you can read this chapter.
+VRender provides a series of default interfaces to shield the impact of cross-platform, currently supporting `Browser`, `Node`, `feishu`, `tt`, and other environments can be supported through extensions.
## Global
-Global is a static class that provides global cross-platform APIs. Users can directly use Global as the browser's window, and Global will automatically provide cross-platform compatibility.
+Global is a static class that provides global cross-platform APIs. Users can directly use Global as if it were the window of a browser, and Global will automatically provide cross-platform compatibility.
-Global needs to be manually set to env, and there is no need to add cross-platform methods yourself.
+> Global needs to manually set the environment, no need to add cross-platform methods by yourself
+> Note: When using the nodejs environment, Canopus will not automatically reference node-canvas, so users need to manually pass the node-canvas package
-Note: For the node side, use node-canvas. Canopus will not automatically reference it, and you need to manually pass in the node-canvas package.
+```ts
+type IEnv = 'feishu' | 'tt' | 'browser' | 'node';
+
+interface IDocument {
+ createElement(t: 'canvas'): HTMLCanvasElement;
+ createElement(t: string): HTMLElement;
+}
+
+export interface IGlobal {
+ readonly env: IEnv;
+ // If it is a nodejs environment, params must be passed
+ setEnv: (env: IEnv, params: { canvasPkg?: { canvas: any; loadImage: any } }) => void;
+ createCanvas: () => ICanvas;
+ releaseCanvas: (canvas: ICanvas) => void;
+ getImageData: (ctx?: IContext2d) => ImageData;
+ // Load image
+ loadImage: (url: string, type: 'Image' | 'ImageData') => HTMLImageElement | Image | ImageData;
+ // RAF
+ requestAnimationFrame: IRequestAnimationFrame;
+ // Get devicePixelRatio
+ devicePixelRatio: number;
+ document: IDocument;
+}
+```
## GraphicUtil
-Unlike Global, GraphicUtil provides cross-platform and graphic-related APIs, including Transform API, MeasureText API.
\ No newline at end of file
+Unlike Global, GraphicUtil provides cross-platform and graphic-related APIs, including Transform API and MeasureText API.
+
+The Transform module is convenient for users to manually transform based on an existing transformation configuration to another space.
+
+```ts
+interface IGraphicUtil {
+ canvas?: ICanvas;
+ context?: IContext2d | null;
+ textMeasure: ITextMeasure;
+ measureText: (text: string, tc: TextOptionsType) => { width: number; height: number };
+ bindTextMeasure: (tm: ITextMeasure) => void;
+ createTextMeasureInstance: (
+ textSpec?: Partial,
+ option?: Partial,
+ getCanvasForMeasure?: () => any
+ ) => TextMeasure;
+ drawGraphicToCanvas: (
+ graphic: IGraphic,
+ stage: IStage
+ ) => HTMLCanvasElement | null | Promise;
+}
+
+interface TransformType {
+ x: number, y: number,
+ scaleX: number, scaleY: number,
+ // shearMatrix is the matrix of shear transformation (if there is shear transformation)
+ angle: number, shearMatrix?: Matrix
+}
+
+interface Transform {
+ // Provide the initial transformation configuration
+ init: (origin: TransformType) => Transform;
+ translate: (x: number, y: number) => Transform;
+ translateTo: (x: number, y: number) => Transform;
+ scale: (sx: number, sy: number, center: IPoint) => Transform;
+ scaleTo: (sx: number, sy: number, center: IPoint) => Transform;
+ rotate: (angle: number, center: IPoint) => Transform;
+ rotateTo: (angle: number, center: IPoint) => Transform;
+ // Syntactic sugar, can be used for interaction, passing in the change data each time
+ interactive: (dx: number, dy: number, dsx: number, dsy: number, drx: number, dry: number) => Transform;
+ // Extend padding pixels for outer stroke, inner stroke
+ extend: (origin: TransformType, padding: number) => Transform;
+ // Generate all transforms into one transform
+ simplify: (target?: TransformType) => TransformType;
+}
+```
diff --git a/docs/assets/guide/en/asd/Basic_Tutorial/Event.md b/docs/assets/guide/en/asd/Basic_Tutorial/Event.md
new file mode 100644
index 000000000..50b4f3ece
--- /dev/null
+++ b/docs/assets/guide/en/asd/Basic_Tutorial/Event.md
@@ -0,0 +1,124 @@
+# Events
+
+VRender designs the DOM event model and API as a reference standard, providing a series of default element-based events. The underlying VRender is compatible with different browser versions, providing unified events. Supported event types include pointer events, mouse events, touch events, and wheel events.
+
+* Pointer Events
+ - pointerdown
+ - pointerup
+ - pointerupoutside
+ - pointertap
+ - pointerover
+ - pointerenter
+ - pointerleave
+ - pointerout
+* Left Mouse Button Operations
+ - mousedown
+ - mouseup
+ - mouseupoutside (the shape is different when the mouse is up and down)
+* Right Mouse Button Operations
+ - rightdown
+ - rightup
+ - rightupoutside (the shape is different when the mouse is up and down)
+* Mouse Operations
+ - click
+ - mousemove
+ - mouseover
+ - mouseout
+ - mouseenter
+ - mouseleave
+* Scrolling
+ - wheel
+* Touch Events
+ - touchstart
+ - touchend
+ - touchendoutside
+ - touchmove
+ - touchcancel
+ - tap
+* Wildcard Event
+ - *
+
+## Listening and Delegating
+VRender can directly listen to and handle events for elements, supporting the `addEventListener` and `removeEventListener` methods, with parameters consistent with the DOM. It supports using in the capturing phase or bubbling phase:
+
+```ts
+interface AddEventListenerOptions extends EventListenerOptions {
+ once?: boolean;
+ passive?: boolean;
+ signal?: AbortSignal;
+}
+
+addEventListener(
+ type: string,
+ listener: EventListenerOrEventListenerObject | LooseFunction,
+ options?: AddEventListenerOptions | boolean
+): void;
+
+removeEventListener(
+ type: string,
+ listener: EventListenerOrEventListenerObject | LooseFunction,
+ options?: AddEventListenerOptions | boolean
+): void;
+
+type on = addEventListener;
+type off = removeEventListener;
+
+// Listen only once
+once(
+ type: string,
+ listener: EventListenerOrEventListenerObject | LooseFunction,
+ options?: AddEventListenerOptions | boolean
+): void;
+
+```
+
+```javascript livedemo template=vrender
+// Register all necessary content
+const stage = new VRender.Stage({
+ container: CONTAINER_ID,
+ autoRender: true
+});
+
+const rect = VRender.createRect({
+ x: 100,
+ y: 100,
+ width: 100,
+ height: 100,
+ fill:'red'
+});
+
+rect.addEventListener('click', () => {
+ rect.setAttribute('fill', 'blue');
+})
+
+stage.defaultLayer.add(rect);
+
+window['stage'] = stage;
+```
+
+Additionally, VRender also supports event delegation, where any node can directly delegate to child elements. You can determine the element that actually triggers the event through the `target` in the Event. Below is an example of event delegation.
+
+```javascript livedemo template=vrender
+// Register all necessary content
+const stage = new VRender.Stage({
+ container: CONTAINER_ID,
+ autoRender: true
+});
+
+const group = VRender.createGroup({x: 100, y: 100, width: 200, height: 200, fill: 'pink'});
+const rect = VRender.createRect({ x: 50, y: 50, fill: 'green', width: 100, height: 100 });
+group.add(rect);
+group.addEventListener('click', (e) => {
+ if (e.target === group) {
+ group.setAttribute('fill', 'orange');
+ } else {
+ rect.setAttribute('fill', 'red');
+ }
+});
+
+stage.defaultLayer.add(group);
+
+window['stage'] = stage;
+```
+
+【Note】: It is important to note that the event objects thrown by VRender are reusable, so be careful not to save event objects for later use, as the event object will change. This is particularly important to note in asynchronous processes.
diff --git a/docs/assets/guide/en/asd/Basic_Tutorial/Graphic.md b/docs/assets/guide/en/asd/Basic_Tutorial/Graphic.md
new file mode 100644
index 000000000..bce76534f
--- /dev/null
+++ b/docs/assets/guide/en/asd/Basic_Tutorial/Graphic.md
@@ -0,0 +1,556 @@
+# Primitives
+
+## Introduction
+
+VRender has many primitives, but their usage is similar because they all inherit from the `Graphic` class. They all have almost the same properties and methods, just accepting different parameters. The inheritance diagram of primitives is shown in the following figure.
+
+
+
+1. Firstly, they all inherit from `Node`, so all primitives have a tree structure.
+2. Then they all inherit from `Graphic`, so they all have basic properties and methods of primitives such as `x`, `y`, `fill`, etc.
+3. `Graphic` differentiates basic primitive types such as `Text`, `Rect`, `Arc`, etc., and also has a special type `Group`. `Group` can contain other primitives, and `Group` can also be added as a primitive to another `Group`.
+4. The `Stage` and `Layer` introduced in the [Creating Instances](./Create_Instance) chapter inherit from `Group`, and the component types in `VRender` (components are special composite primitives with logic) also inherit from `Group`. For more information on components, refer to the [Component](./Component) chapter.
+5. Based on the basic component types, `VRender` implements various components such as `poptip`, `axis`, `label`, and so on.
+
+The code for creating primitives is as follows:
+
+```ts
+import { Rect, createRect, Text, createText } from '@visactor/vrender';
+
+const rectAttribute = {
+ x: 100,
+ y: 100,
+ width: 100,
+ height: 100,
+ fill: 'red'
+}
+const rect1 = new Rect(rectAttribute);
+const rect2 = createRect(rectAttribute);
+
+const textAttribute = {
+ x: 100,
+ y: 100,
+ text: 'hello world',
+ fill:'red'
+}
+const text1 = new Text(textAttribute);
+const text2 = createText(textAttribute);
+
+// ...other primitives
+```
+
+## Tree Structure (Node)
+
+Primitives inherit from `Node`, so they all have a tree structure, and primitives also provide some methods for manipulating the tree structure. The tree structure is implemented based on a linked list, but it provides methods like `forEachChildren` for traversing the tree structure, `insertBefore`, `insertAfter`, `insertInto`, `appendChild`, `removeChild`, `removeAllChild`, etc., for manipulating the tree structure.
+
+```ts
+interface INode extends Releaseable, IEventElement {
+ _prev?: INode;
+ _next?: INode;
+ _uid: number;
+ id?: number | string;
+ name?: string;
+ type?: string;
+ // Parent element
+ parent: INode | null;
+ // Number of child elements (including self)
+ count: number;
+ // Number of child elements (excluding self)
+ childrenCount: number;
+ // First child element
+ firstChild: INode | null;
+ // Last child element
+ lastChild: INode | null;
+ // Get all child elements
+ getChildren: () => INode[];
+ // Get the child element at the idx position
+ getChildAt: (idx: number) => INode | null;
+ // Syntactic sugar for getChildAt
+ at: (idx: number) => INode | null;
+ // Insert before
+ insertBefore: (newNode: INode, referenceNode: INode) => INode | null;
+ // Insert after
+ insertAfter: (newNode: INode, referenceNode: INode) => INode | null;
+ // Insert at idx position
+ insertInto: (ele: INode, idx: number) => INode | null;
+ // Traverse child elements
+ forEachChildren: (cb: (n: INode, i: number) => void | boolean, reverse?: boolean) => void;
+ // Traverse child elements asynchronously
+ forEachChildrenAsync: (cb: (n: INode, i: number) => Promise | void | boolean, reverse?: boolean) => Promise;
+ // Append to the last child element
+ appendChild: (node: INode, highPerformance?: boolean) => INode | null;
+ // Syntactic sugar for appendChild
+ add: (node: INode, highPerformance?: boolean) => INode | null;
+ // Delete itself
+ delete: () => void;
+ // Delete child nodes
+ removeChild: (node: INode, highPerformance?: boolean) => INode | null;
+ // Delete all child nodes
+ removeAllChild: (deep?: boolean) => void;
+ // Check if it is a child node of node
+ isChildOf: (node: INode) => boolean;
+ // Check if it is a parent node of node
+ isParentOf: (node: INode) => boolean;
+ // Check if it is a descendant node of node
+ isDescendantsOf: (node: INode) => boolean;
+ // Check if it is an ancestor node of node
+ isAncestorsOf: (node: INode) => boolean;
+ // Trigger event
+ dispatchEvent: (event: Event) => boolean;
+ // Return a boolean value to indicate whether the passed node is a descendant node of this node.
+ containNode: (node: INode) => boolean;
+ // Set a certain property for all descendants of this node
+ setAllDescendantsProps: (propsName: string, propsValue: any) => any;
+ // Find elements based on custom logic, returning a single graphic element
+ find: (callback: (node: INode, index: number) => boolean, deep: boolean) => INode | null;
+ // Find elements based on custom logic, returning a collection of matching elements
+ findAll: (callback: (node: INode, index: number) => boolean, deep: boolean) => INode[];
+ // Find the graphic element corresponding to the user-set id
+ getElementById: (id: string | number) => INode | null;
+ // Syntactic sugar for getElementById
+ findChildById: (id: string | number) => INode | null;
+ // Find the graphic element corresponding to the user-set uid
+ findChildByUid: (uid: number) => INode | null;
+ // Find graphic elements corresponding to the user-set name
+ getElementsByName: (name: string) => INode[];
+ // Syntactic sugar for getElementsByName
+ findChildrenByName: (name: string) => INode[];
+ // Find graphic elements corresponding to the user-set type
+ getElementsByType: (type: string) => INode[];
+}
+```
+
+## Primitive Structure (Graphic)
+
+Through the tree structure, we can build a scene tree, but we also need some properties to describe some attributes of primitives, such as position, size, color, etc. Therefore, the `Graphic` base class is used to describe some attributes of primitives. All primitives can use
+
+```ts
+interface IGraphic = Partial>
+ extends INode,
+ IAnimateTarget {
+ // Primitive type
+ type?: GraphicType;
+ // Primitive type (numeric type)
+ numberType?: number;
+ // Bound to stage
+ stage?: IStage;
+ // Bound to layer
+ layer?: ILayer;
+ // Shadow node
+ shadowRoot?: IShadowRoot;
+
+ // Whether it is valid
+ valid: boolean;
+ // Whether it is a container node (inherits from Group)
+ isContainer?: boolean;
+ // Whether it is in 3D mode (whether to apply 3D perspective)
+ in3dMode?: boolean;
+ // Attribute parameters
+ attribute: Partial;
+
+ /** Used to implement morph animation scenes, converted to bezier curve rendering */
+ pathProxy?: ICustomPath2D | ((attrs: T) => ICustomPath2D);
+
+ // Method to get state graphic attributes
+ stateProxy?: (stateName: string, targetStates?: string[]) => Partial;
+
+ /* State-related methods */
+ toggleState: (stateName: string, hasAnimation?: boolean) => void;
+ removeState: (stateName: string, hasAnimation?: boolean) => void;
+ clearStates: (hasAnimation?: boolean) => void;
+ useStates: (states: string[], hasAnimation?: boolean) => void;
+ addState: (stateName: string, keepCurrentStates?: boolean, hasAnimation?: boolean) => void;
+ hasState: (stateName?: string) => boolean;
+ getState: (stateName: string) => Partial;
+ // Callback before attribute update
+ onBeforeAttributeUpdate?: (
+ val: any,
+ attributes: Partial,
+ key: null | string | string[],
+ context?: ISetAttributeContext
+ ) => T | undefined;
+
+
+ readonly AABBBounds: IAABBBounds; // Used to get the current node's AABB bounding box
+ readonly OBBBounds: IOBBBounds; // Get the OBB bounding box, used for rotation to prevent overlap
+ readonly globalAABBBounds: IAABBBounds; // Global AABB bounding box
+ readonly transMatrix: IMatrix; // Transformation matrix, dynamically calculated
+ readonly globalTransMatrix: IMatrix; // Transformation matrix, dynamically calculated
+
+ /**
+ * Whether it contains a point (the point needs to be in global coordinates)
+ */
+ containsPoint: (x: number, y: number, mode?: IContainPointMode, picker?: IPickerService) => boolean;
+ // Set whether it is in 2D mode or 3D mode
+ setMode: (mode: '3d' | '2d') => void;
+ // Whether it is valid
+ isValid: () => boolean;
+
+ // Transformation based on the current transform, literally, try not to use it if you are an ordinary user~
+ translate: (x: number, y: number) => this;
+ translateTo: (x: number, y: number) => this;
+ scale: (scaleX: number, scaleY: number, scaleCenter?: IPointLike) => this;
+ scaleTo: (scaleX: number, scaleY: number) => this;
+ rotate: (angle: number, rotateCenter?: IPointLike) => this;
+ rotateTo: (angle: number) => this;
+ skewTo: (b: number, c: number) => this;
+ // Set Tag, no need to call by default
+ addUpdateBoundTag: () => void;
+ addUpdateShapeAndBoundsTag: () => void;
+ addUpdateLayoutTag: () => void;
+ addUpdatePositionTag: () => void;
+ addUpdateGlobalPositionTag: () => void;
+ // For render to handle shape cache tags
+ shouldUpdateShape: () => boolean;
+ clearUpdateShapeTag: () => void;
+ shouldUpdateAABBBounds: () => boolean;
+ shouldSelfChangeUpdateAABBBounds: () => boolean;
+ shouldUpdateGlobalMatrix: () => boolean;
+
+ // Set attributes
+ setAttributes: (params: Partial, forceUpdateTag?: boolean, context?: ISetAttributeContext) => void;
+
+ // Set a single attribute
+ setAttribute: (key: string, value: any, forceUpdateTag?: boolean, context?: ISetAttributeContext) => void;
+
+ // Add shadow node
+ attachShadow: () => IShadowRoot;
+ // Detach shadow node
+ detachShadow: () => void;
+
+ // Export JSON configuration
+ toJson: () => IGraphicJson;
+
+ /** Create pathProxy */
+ createPathProxy: (path?: string) => void;
+ /** Convert the graphic to CustomPath2D */
+ toCustomPath?: () => ICustomPath2D;
+
+ // Clone object
+ clone: () => IGraphic;
+ // Stop animation
+ stopAnimates: (stopChildren?: boolean) => void;
+ // Get attributes that do not need to be animated
+ getNoWorkAnimateAttr: () => Record;
+ // Get theme
+ getGraphicTheme: () => T;
+}
+```
+
+The configurations involved here are many, but the most commonly used ones are
+- `attribute`: attributes of the primitive
+- `setAttributes`: set attribute object
+- `AABBBounds`: get the AABB bounding box
+- `transMatrix`: get the transformation matrix
+- `type`: primitive type
+
+### attribute
+There are many primitive attributes, which can be referred to in the [configuration document](/vrender/option/Arc). Almost all configurations of primitives are in this object. This includes position, color, style, etc. Next, we will explain in detail some of these common configurations. For specific configurations, refer to the documentation of the specific primitive type.
+
+#### Position Transformation Configuration
+
+Firstly, the position transformation configurations are common:
+
+```ts
+type ITransform = {
+ x: number;
+ y: number;
+ z: number;
+ dx: number;
+ dy: number;
+ dz: number;
+ scrollX: number;
+ scrollY: number;
+ scaleX: number;
+ scaleY: number;
+ scaleZ: number;
+ angle: number; // Rotation angle
+ alpha: number; // Rotation angle around the x-axis
+ beta: number; // Rotation angle around the y-axis
+ scaleCenter: [number | string, number | string]; // Scaling center
+ anchor: [number | string, number | string]; // Anchor position based on AABB, used for simple positioning of certain paths
+ anchor3d: [number | string, number | string, number] | [number | string, number | string]; // 3D anchor position
+ postMatrix: IMatrix; // Post-processing matrix, will be multiplied by the previous transformation matrix
+}
+```
+Once these transformation configurations are set, you can get the local transformation matrix through `transMatrix` and the global transformation matrix through `globalTransMatrix`.
+
+#### Fill, Stroke, Common Configuration
+
+Next are common primitive configurations, including fill (`IFillStyle`), stroke (`IStrokeStyle`), and common (`ICommonStyle`) configurations.
+
+```ts
+// Gradient color configuration
+interface IGradientStop {
+ offset: number;
+ color: string;
+}
+
+interface ILinearGradient {
+ gradient: 'linear';
+ x0?: number;
+ y0?: number;
+ x1?: number;
+ y1?: number;
+ stops: IGradientStop[];
+}
+
+interface IRadialGradient {
+ gradient: 'radial';
+ x0?: number;
+ y0?: number;
+ x1?: number;
+ y1?: number;
+ r0?: number;
+ r1?: number;
+ stops: IGradientStop[];
+}
+
+interface IConicalGradient {
+ gradient: 'conical';
+ startAngle?: number;
+ endAngle?: number;
+ x?: number;
+ y?: number;
+ stops: IGradientStop[];
+}
+```
+
+```ts
+type IColor = string | ILinearGradient | IRadialGradient | IConicalGradient;
+export type IFillType = boolean | string | IColor;
+
+type IFillStyle = {
+ fillOpacity: number;
+ /* Shadow */
+ shadowBlur: number;
+ shadowColor: string;
+ shadowOffsetX: number;
+ shadowOffsetY: number;
+ // Fill color
+ fill: IFillType;
+};
+
+export type IBorderStyle = Omit & {
+ distance: number | string;
+ visible?: boolean;
+};
+
+type IStrokeStyle = {
+ outerBorder: Partial; // Outer stroke
+ innerBorder: Partial; // Inner stroke
+ strokeOpacity: number;
+ lineDash: number[];
+ lineDashOffset: number;
+ lineWidth: number;
+ lineCap: CanvasLineCap;
+ lineJoin: CanvasLineJoin;
+ miterLimit: number;
+ // Stroke bounds buffer, used to control the buffer of bounds
+ strokeBoundsBuffer: number;
+ /**
+ * stroke - true Full stroke
+ * stroke - false No stroke
+ * stroke is a numeric type, applicable to shapes like rect\arc, used to configure partial strokes, where
+ *
+ * 0b00000 - No stroke
+ * 0b000001 - top
+ * 0b000010 - right
+ * 0b000100 - bottom
+ * 0b001000 - left
+ * Correspondingly:
+ * 0b000011 - top + right
+ * 0b000111 - top + right + bottom
+ * 0b001111 - Full stroke
+ *
+ * stroke - boolean[] Applicable to shapes like rect\arc, used to configure partial strokes, where
+ */
+ stroke: IStrokeType[] | IStrokeType;
+};
+```
+
+```ts
+type ICommonStyle = {
+ // Additional buffer added to the pick for stroke mode, used to control the pick range of the stroke area externally
+ pickStrokeBuffer: number;
+ // Bounds padding
+ boundsPadding: number | number[];
+ /**
+ * Selection mode, accurate mode, rough mode (bounding box mode), custom mode
+ */
+ pickMode: 'accurate' | 'imprecise' | 'custom';
+ // Accurate mode, rough mode for bounds
+ boundsMode: 'accurate' | 'imprecise';
+ /**
+ * Whether event picking is supported, default is true.
+ * @default true
+ */
+ pickable: boolean;
+ /**
+ * Whether fill picking is supported, default is true.
+ * @experimental
+ * @default true
+ */
+ fillPickable: boolean;
+ /**
+ * Whether stroke picking is supported, default is true.
+ * @experimental
+ * @default true
+ */
+ strokePickable: boolean;
+ /**
+ * For group nodes, whether to pick events of its child elements, default is true.
+ * If group `pickable` is turned off and `childrenPickable` is turned on, then the child nodes of the group still participate in event picking
+ * @default true
+ */
+ childrenPickable: boolean;
+
+ /**
+ * Whether the element is visible.
+ * @default true
+ */
+ visible: boolean;
+ zIndex: number;
+ layout: any; // Layout configuration (only effective when the layout plugin is enabled)
+ /**
+ * Whether to hide the element (just not draw when rendering)
+ */
+ renderable: boolean;
+ /**
+ * Whether to control the direction in 3D
+ * false: Do not control the direction
+ * true: Always control the direction towards the camera
+ */
+ keepDirIn3d?: boolean;
+ // Order of shadow nodes
+ shadowRootIdx: number;
+ // Shadow node picking mode
+ shadowPickMode?: 'full' | 'graphic';
+ // Global zIndex, will be brought to the top layer, only effective when the interaction layer is enabled
+ globalZIndex: number;
+ // Functionality consistent with Canvas's globalCompositeOperation
+ globalCompositeOperation: CanvasRenderingContext2D['globalCompositeOperation'] | '';
+ // 完全支持滚动 | 完全不支持滚动 | 支持x方向的滚动 | 支持y方向的滚动,(仅在开启滚动时生效)
+ overflow: 'scroll' | 'hidden' | 'scroll-x' | 'scroll-y';
+ // 绘制fill和stroke的顺序,为0表示fill先绘制,1表示stroke先绘制
+ fillStrokeOrder: number;
+};
+```
+```javascript livedemo template=vrender
+// Register all required content
+const stage = new VRender.Stage({
+ container: CONTAINER_ID,
+ autoRender: true
+});
+
+const rect = VRender.createRect({
+ x: 100,
+ y: 100,
+ width: 100,
+ height: 100,
+ fill:'red'
+});
+
+const text = VRender.createText({
+ x: 100,
+ y: 300,
+ text: 'This is a demo, feel free to modify',
+ fill: 'red'
+});
+
+stage.defaultLayer.add(rect);
+stage.defaultLayer.add(text);
+
+window['stage'] = stage;
+```
+
+## Group Structure
+
+The Group structure is similar to regular graphic elements, with just a few additional methods at the API level:
+
+```ts
+interface IGroup extends IGraphic {
+ // Hide all child nodes
+ hideAll: () => void;
+ // Show all child nodes
+ showAll: () => void;
+
+ // Add child nodes during incremental rendering
+ incrementalAppendChild: (node: INode, highPerformance?: boolean) => INode | null;
+ // Clear child nodes during incremental rendering
+ incrementalClearChild: () => void;
+ // Create or update (if exists) child nodes
+ createOrUpdateChild: (
+ graphicName: string,
+ attributes: GraphicAttributeMap[T],
+ graphicType: T
+ ) => INode;
+}
+```
+
+### Attribute
+The attributes of a Group are similar to those of regular graphic elements. For more details, please refer to the [configuration document](/vrender/option/Group). The Group has some additional layout and clipping related configurations, summarized as follows:
+
+1. The Group itself has a shape and can be drawn. By default, the Group is a rectangle and can be drawn with specified width and height. However, a path array can also be passed in to define the shape of the Group.
+2. The Group can clip elements that exceed its shape.
+3. The visibility of the Group itself can be controlled using the `visible` attribute, and the visibility of all child nodes can be controlled using `visibleAll`.
+4. The layout of child nodes can be controlled using the `display` attribute, supporting flex layout (requires enabling the flex layout plugin).
+5. The opacity of child nodes can be controlled using the `baseOpacity` attribute.
+
+```ts
+type IGroupAttribute = {
+ path: IGraphic[]; // Custom shape
+ width: number;
+ height: number;
+ cornerRadius: number | number[];
+ // Whether to clip
+ clip: boolean;
+ visibleAll: boolean;
+ /* Flex layout related (requires enabling flex layout plugin) */
+ display?: 'relative' | 'inner-block' | 'flex';
+ flexDirection?: 'row' | 'row-reverse' | 'column' | 'column-reverse';
+ flexWrap?: 'nowrap' | 'wrap';
+ justifyContent?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around';
+ alignItems?: 'flex-start' | 'flex-end' | 'center';
+ alignContent?: 'flex-start' | 'center' | 'space-between' | 'space-around';
+ // Base opacity used to control the opacity of all child nodes under the group
+ baseOpacity?: number;
+};
+```
+
+```javascript livedemo template=vrender
+// Register all required content
+const stage = new VRender.Stage({
+ container: CONTAINER_ID,
+ autoRender: true
+});
+
+const rect = VRender.createRect({
+ x: 100,
+ y: 100,
+ width: 100,
+ height: 100,
+ fill:'red'
+});
+
+const group = VRender.createGroup({
+ x: 100,
+ y: 100,
+ baseOpacity: 0.5
+});
+const text = VRender.createText({
+ x: 0,
+ y: 0,
+ text: 'The group controls the opacity and position of all child nodes',
+ fill: 'red'
+});
+group.add(rect);
+group.add(text);
+
+stage.defaultLayer.add(group);
+
+window['stage'] = stage;
+```
+
+## Components
+All components are included in the `@visactor/vrender-component` package. For more details, please refer to the [component document](/vrender/component). The usage of components is similar to regular graphic elements, as components inherit from Group and support Group configurations. The only difference lies in the attributes, which are specific to each component. Please refer to the component document for more details.
diff --git a/docs/assets/guide/en/asd/FAQ/What_Is_BoundsPadding.md b/docs/assets/guide/en/asd/FAQ/What_Is_BoundsPadding.md
new file mode 100644
index 000000000..c03c288d7
--- /dev/null
+++ b/docs/assets/guide/en/asd/FAQ/What_Is_BoundsPadding.md
@@ -0,0 +1,119 @@
+# What is BoundsPadding
+
+*Note:* By default, `VRender` does not have a layout similar to the `DOM`. For example, in the `DOM`, you can have two `div` elements, and the second `div` will be placed below the first `div`. However, in `VRender`, if you have two rectangles, the second rectangle will overlay the first one. This is because all positioning in `VRender` is relative positioning, *it relies on the x, y parameters you configure for positioning*, with the coordinate system having the origin at the top left corner, the positive direction of the x-axis to the right, and the positive direction of the y-axis downwards. This difference in layout between `VRender` and the `DOM` is due to this positioning system.
+
+All graphics elements have their own `AABBBounds`. If you find the `AABBBounds` too small and need to enlarge it, you can configure `BoundsPadding`.
+
+```ts
+// Padding for the bounding box
+// If it is a number, then padding on all four sides is this value
+// If it is a [number, number], then it is padding for top/bottom and left/right respectively
+// If it is a [number, number, number, number], then it is padding for top, right, bottom, and left respectively
+boundsPadding: number | number[];
+```
+
+
+
+## Special Cases
+
+As mentioned earlier, BoundsPadding affects the elements' AABBBounds, which normally does not affect the position because the position is fixed through configurations like `x` and `y`, not based on the position relationship between elements.
+
+However!! When you enable the `flex` layout plugin and set `display: flex` in an element, that element will apply flex layout. Its child elements will then be laid out based on its AABBBounds. In this case, BoundsPadding will affect the position of the child elements.
+
+For more details on Flex layout, please refer to the [Flex Layout](./Flex_Layout) section.
+
+## Examples
+
+Next, we will show two examples, one without using flex layout where BoundsPadding does not affect the element position, and one using flex layout where BoundsPadding affects the element position.
+
+### Without using flex layout, BoundsPadding does not affect element position
+
+```javascript livedemo template=vrender
+const rect = VRender.createRect({ x: 100, y: 100, width: 100, height: 100, fill: 'red' });
+
+const stage = new VRender.Stage({
+ container: CONTAINER_ID,
+ autoRender: true
+});
+
+const textLimit = VRender.createText({
+ x: 0,
+ y: 0,
+ fill: 'pink',
+ text: '这是没有BoundsPadding的文字包围盒',
+ wordBreak: 'keep-all',
+ maxLineWidth: 120,
+ textAlign: 'left',
+ textBaseline: 'middle',
+ whiteSpace: 'normal',
+ _debug_bounds: true,
+ background: 'green'
+});
+const textLimit2 = VRender.createText({
+ x: 0,
+ y: 60,
+ fill: 'pink',
+ text: '这是有BoundsPadding的文字包围盒',
+ wordBreak: 'keep-all',
+ maxLineWidth: 120,
+ textAlign: 'left',
+ textBaseline: 'middle',
+ whiteSpace: 'normal',
+ boundsPadding: [20, 10],
+ _debug_bounds: true,
+ background: 'green'
+});
+
+const group = VRender.createGroup({x: 100, y: 100, width: 200, height: 200});
+group.add(textLimit);
+group.add(textLimit2);
+
+stage.defaultLayer.add(group);
+```
+
+
+### Using flex layout, BoundsPadding affects element position
+
+```javascript livedemo template=vrender
+const rect = VRender.createRect({ x: 100, y: 100, width: 100, height: 100, fill: 'red' });
+
+const stage = new VRender.Stage({
+ container: CONTAINER_ID,
+ autoRender: true,
+ enableLayout: true
+});
+
+const textLimit = VRender.createText({
+ x: 0,
+ y: 0,
+ fill: 'pink',
+ text: '这是没有BoundsPadding的文字包围盒',
+ wordBreak: 'keep-all',
+ maxLineWidth: 120,
+ textAlign: 'left',
+ textBaseline: 'middle',
+ whiteSpace: 'normal',
+ _debug_bounds: true,
+ background: 'green'
+});
+const textLimit2 = VRender.createText({
+ x: 0,
+ y: 60,
+ fill: 'pink',
+ text: '这是有BoundsPadding的文字包围盒',
+ wordBreak: 'keep-all',
+ maxLineWidth: 120,
+ textAlign: 'left',
+ textBaseline: 'middle',
+ whiteSpace: 'normal',
+ boundsPadding: [20, 10],
+ _debug_bounds: true,
+ background: 'green'
+});
+
+const group = VRender.createGroup({x: 100, y: 100, width: 200, height: 200, display: 'flex'});
+group.add(textLimit);
+group.add(textLimit2);
+
+stage.defaultLayer.add(group);
+```
diff --git a/docs/assets/guide/en/asd/VRender_Components/VRender_Components_Introduction.md b/docs/assets/guide/en/asd/VRender_Components/VRender_Components_Introduction.md
new file mode 100644
index 000000000..db644f9e3
--- /dev/null
+++ b/docs/assets/guide/en/asd/VRender_Components/VRender_Components_Introduction.md
@@ -0,0 +1,207 @@
+# Components
+
+Components are special primitives and also special Groups. VRender provides a series of built-in components, such as `datazoom`, `axes`, `label`, `legend`, `poptip`, etc., which can help us quickly achieve some common interactive effects. Externally, custom components can be implemented by inheriting `AbstractComponent` from `@visactor/vrender-components`.
+
+
+
+## Introduction
+
+Components are implemented based on the `Group` primitive, so the component itself is a special Group primitive. All configurations that can be supported by Group primitives can be configured to components, but of course, in general, components will abstract a set of special configurations for defining the special configurations of the component. Components internally contain other primitives and the logic of the component. For example, the `axes` component contains `line` primitives, `text` primitives, `path` primitives, etc. Different types of `axes` components have different styles, and the labels in the `axes` component implement anti-overlapping logic based on various strategies.
+
+## Using Components
+
+Since components are implemented based on the `Group` primitive, when using them, simply use them as a Group, but this Group will internally manage its own child elements and have its own state.
+
+Below is an example of using an `axes` component:
+```ts
+const lineAxis = new LineAxis({
+ x: 68,
+ y: 30,
+ start: {
+ x: 0,
+ y: 0
+ },
+ end: {
+ x: 0,
+ y: 400
+ },
+ pickable: true,
+ visible: true,
+ orient: 'left',
+ line: {
+ visible: false
+ },
+ label: {
+ visible: true,
+ inside: false,
+ space: 12,
+ autoLimit: true,
+ style: {
+ fontSize: 12,
+ fill: '#89909d',
+ fontWeight: 'normal',
+ fillOpacity: 1
+ },
+ formatMethod: null
+ },
+ tick: {
+ visible: false
+ },
+ subTick: {
+ visible: false
+ },
+ title: {
+ visible: false,
+ text: 'visits',
+ maxWidth: null
+ },
+ panel: {
+ visible: false
+ },
+ verticalFactor: 1,
+ items: [
+ [
+ {
+ id: 0,
+ label: 0,
+ value: 1,
+ rawValue: 0
+ },
+ {
+ id: 500,
+ label: 500,
+ value: 0.780952380952381,
+ rawValue: 500
+ },
+ {
+ id: 1000,
+ label: 1000,
+ value: 0.5619047619047619,
+ rawValue: 1000
+ },
+ {
+ id: 1500,
+ label: 1500,
+ value: 0.3428571428571428,
+ rawValue: 1500
+ },
+ {
+ id: 2000,
+ label: 2000,
+ value: 0.12380952380952383,
+ rawValue: 2000
+ },
+ {
+ id: 24000,
+ label: 24000,
+ value: -0.0042307692307692315,
+ rawValue: 24000
+ }
+ ]
+ ],
+});
+
+const stage = createStage({
+ canvas: 'main',
+ autoRender: true
+});
+
+stage.defaultLayer.add(lineAxis);
+window['stage'] = stage;
+```
+
+## Custom Components
+
+Custom components are simpler than custom primitives, as they do not need to customize render and pick, because the primitives they use are already existing primitives. We only need to implement our own logic inside the component, similar to `react`.
+
+1. Inherit `AbstractComponent`
+2. Define your own `attribute`, because components often contain different child element modules, such as axis labels, axis ticks, axis lines, etc., so you can define an attribute interface to define the properties of the component.
+3. Override the `render` function, which will be called every time valid attributes are updated. In the `render` function, create and manage child elements.
+
+Here is a simple example. For example, if we want to implement a component that is a box containing circles, and when the number of circles is passed in, it can draw that number of circles:
+```ts
+import { AbstractComponent } from '@visactor/vrender-components';
+class CircleBox extends AbstractComponent> {
+ name = 'circleBox';
+
+ static defaultAttributes: Partial = {
+ circleStyle: {
+ fill: 'red',
+ },
+ circleCount: 10,
+ width: 300,
+ height: 300
+ };
+
+ constructor(attributes: PopTipAttributes, options?: ComponentOptions) {
+ super(options?.skipDefault ? attributes : merge({}, CircleBox.defaultAttributes, attributes));
+ }
+
+ // Called every time valid attributes are updated
+ protected render() {
+ const { circleCount, circleStyle, width, height } = this.attribute;
+
+ const minWH = Math.min(width, height);
+ const count = Math.ceil(Math.sqrt(circleCount));
+ const radius = Math.floor(minWH) / count / 2;
+ for (let i = 0; i < circleCount; i++) {
+ // Calculate the position of the child element
+ const x = (i % count) * radius * 2 + radius;
+ const y = Math.floor(i / count) * radius * 2 + radius;
+ // Add or create child elements
+ const circle = this.createOrUpdateChild(`circle-${i}`, { ...circleStyle, radius, x, y }, 'circle');
+ }
+ }
+}
+```
+
+After customizing, you can use it just like other components.
+
+```javascript livedemo template=vrender
+VRenderComponent.loadPoptip();
+
+class CircleBox extends VRenderComponent.AbstractComponent {
+ name = 'circleBox';
+
+ static defaultAttributes = {
+ circleStyle: {
+ fill: 'red',
+ },
+ stroke: 'blue',
+ circleCount: 10,
+ width: 300,
+ height: 300
+ };
+
+ constructor(attributes, options) {
+ super(options?.skipDefault ? attributes : ({...CircleBox.defaultAttributes, ...attributes}));
+ }
+
+ // 每次合法属性更新都会调用
+ render() {
+ const { circleCount, circleStyle, width, height } = this.attribute;
+
+ const minWH = Math.min(width, height);
+ const count = Math.ceil(Math.sqrt(circleCount));
+ const radius = Math.floor(minWH) / count / 2;
+ for (let i = 0; i < circleCount; i++) {
+ // 计算子元素的位置
+ const x = (i % count) * radius * 2 + radius;
+ const y = Math.floor(i / count) * radius * 2 + radius;
+ // 添加或创建子元素
+ const circle = this.createOrUpdateChild(`circle-${i}`, { ...circleStyle, radius, x, y }, 'circle');
+ }
+ }
+}
+
+const textLimit = new CircleBox({x: 10, y: 10, circleCount: 100});
+const stage = VRender.createStage({
+ container: CONTAINER_ID,
+ autoRender: true,
+ pluginList: ['poptipForText'] // 启用poptipForText插件
+});
+
+stage.defaultLayer.add(textLimit);
+
+window['stage'] = stage;
+```
diff --git a/docs/assets/guide/en/asd/VRender_Components/VRender_Components_PopTip.md b/docs/assets/guide/en/asd/VRender_Components/VRender_Components_PopTip.md
new file mode 100644
index 000000000..18590cb22
--- /dev/null
+++ b/docs/assets/guide/en/asd/VRender_Components/VRender_Components_PopTip.md
@@ -0,0 +1,118 @@
+# PopTip Component
+
+The PopTip component is a pop-up box component provided by VRender, similar to common PopTip components, used to display additional information during user interaction. The PopTip component supports multiple interaction methods, allows customization of styles and content, and supports various layout options.
+
+
+
+## Introduction
+
+The PopTip component consists of the following parts:
+- `title`: The title of the PopTip component.
+- `content`: The content of the PopTip component.
+- `panel`: The panel of the PopTip component.
+
+
+
+In terms of configuration, you can set the width and height of the PopTip, customize the title and content styles, and configure layout properties such as the spacing between the title and content (`space`), and padding.
+
+The corresponding configuration interface is as follows:
+```ts
+type PopTipAttributes = {
+ /** Position, refer to arco design */
+ position?: 'auto' | 'top' | 'tl' | 'tr' | 'bottom' | 'bl' | 'br' | 'left' | 'lt' | 'lb' | 'right' | 'rt' | 'rb';
+ /**
+ * Title content, use an array if line breaks are needed, e.g., ['abc', '123']
+ */
+ title?: string | string[] | number | number[];
+ /** Title style */
+ titleStyle?: Partial;
+ titleFormatMethod?: (t: string | string[] | number | number[]) => string | string[] | number | number[];
+ /**
+ * Content text, use an array if line breaks are needed, e.g., ['abc', '123']
+ */
+ content?: string | string[] | number | number[];
+ /** Content text style */
+ contentStyle?: Partial;
+ // Content formatting function
+ contentFormatMethod?: (t: string | string[] | number | number[]) => string | string[] | number | number[];
+ /**
+ * Spacing between title and content
+ */
+ space?: number;
+ /**
+ * Internal padding
+ */
+ padding?: Padding;
+ /**
+ * Background panel configuration for the label, TODO: support symbol shape
+ */
+ panel?: BackgroundAttributes & ISymbolGraphicAttribute & { space?: number };
+
+ /**
+ * Minimum width, in pixels
+ * @default 30
+ */
+ minWidth?: number;
+ /**
+ * Maximum width, in pixels. When the text exceeds the maximum width, it will be automatically truncated.
+ */
+ maxWidth?: number;
+
+ // Maximum width percentage
+ maxWidthPercent?: number;
+
+ visible?: boolean;
+ visibleFunc?: (graphic: IGraphic) => boolean;
+ state?: StateStyle;
+ dx?: number;
+ dy?: number;
+} & Omit
+```
+
+As shown in the image, `PopTip` supports multiple positioning options, which can be configured using the `position` property. Additionally, there is an `auto` positioning option that automatically adjusts the positioning based on the PopTip's location to avoid being obscured. The positions are attempted in the following order: `['top', 'tl', 'tr', 'bottom', 'bl', 'br', 'left', 'lt', 'lb', 'right', 'rt', 'rb']`.
+
+
+
+The `PopTip` will expand based on its content by default. You can set the minimum and maximum width using `minWidth` and `maxWidth`, respectively. If you are unsure about the specific pixel configurations, you can use `maxWidthPercent` to set a maximum width percentage based on the aspect ratio.
+
+## Usage
+
+The PopTip can be used as a regular component and added to the scene tree using the `xxx.add` method, similar to other components.
+
+Additionally, we provide a plugin (`poptipForText`) for usage. This plugin automatically identifies truncated text in the canvas. When the mouse hovers over the text, it will display a PopTip to show the complete content of the text.
+1. You can configure the style of the PopTip displayed through the `attribute.poptip` property on the graphic element. The type of the `attribute.poptip` property is consistent with the parameters accepted by the PopTip component. The PopTip will automatically fill in the content to display the complete text.
+2. By adding `poptipForText` to the `pluginList` property of the `stage` when creating it, you can enable this plugin.
+
+```javascript livedemo template=vrender
+VRenderComponent.loadPoptip();
+
+const textLimit = VRender.createText({
+ x: 100,
+ y: 100,
+ fill: 'black',
+ text: 'this is textaaaaaaaaaaaaaaaaa aaa this isisisisisis abc',
+ wordBreak: 'keep-all',
+ maxLineWidth: 100,
+ stroke: 'green',
+ textAlign: 'left',
+ textBaseline: 'middle',
+ whiteSpace: 'normal',
+ // Configure PopTipAttributes here
+ poptip: {
+ panel: {
+ fill: 'pink'
+ }
+ }
+});
+const stage = VRender.createStage({
+ container: CONTAINER_ID,
+ autoRender: true,
+ pluginList: ['poptipForText'] // Enable poptipForText plugin
+});
+
+stage.defaultLayer.add(textLimit);
+
+window['stage'] = stage;
+```
+
+In this usage scenario, it may be inconvenient to configure all styles in the `attribute.poptip`. Therefore, the PopTip plugin provides theme configuration. You can import `setPoptipTheme` from the `@visactor/vrender-components` package to configure the theme. The theme properties configuration is consistent with the PopTip component's attribute configuration interface. The plugin will prioritize the `poptip` property defined on the graphic element. If not defined, it will use the theme configuration, and finally the component's own configuration.
diff --git a/docs/assets/guide/en/asd/VRender_Website_Guide.md b/docs/assets/guide/en/asd/VRender_Website_Guide.md
index c1d2b05cd..c35746d5e 100644
--- a/docs/assets/guide/en/asd/VRender_Website_Guide.md
+++ b/docs/assets/guide/en/asd/VRender_Website_Guide.md
@@ -1,57 +1,47 @@
# Site Guide
-This document is mainly to help you make better use of the VRender site and help you get the content you want on the site as quickly as possible.
+This document is mainly designed to help you better utilize the VRender site, enabling you to quickly access the content you are looking for on the site.
## Quick Start
### Getting Started
-First, you need to complete the VRender [Getting Started](./Getting_Started) chapter which will teach you how to configure and use the environment needed for VRender, as well as how to use VRender to build your first graphic application.
+Firstly, you need to complete the VRender [Getting Started](./Getting_Started) section, which will guide you on how to set up and use the environment required for VRender, as well as how to build your first graphic application using VRender.
### Understanding VRender
-After completing [Getting Started](./Getting_Started), you can delve deeper into the various primitives and interactions supported by studying the [Primitives and Interactions](./Your_First_Chart) chapter, then learn the [Animation] chapter to understand how to create an animation, and then learn the [Extensions and Plugins]() chapter to understand how to enhance the functionality of the application through extensions and plugins.
+After completing the [Getting Started](./Getting_Started) section, you can delve deeper into understanding the various primitives and interactions supported by studying the [Basic Tutorial](./Basic/Vrender_basic_tutorial) section. Then, proceed to learn about creating animations in the [Animation](./Animation) section, and enhancing your application's functionality through extensions and plugins in the [Extensions and Plugins](./Extensions_and_Plugins) section.
## Documentation
-VRender's documentation provides detailed information about features and configurations. Depending on your needs, you can view the following sections:
+The documentation of VRender provides detailed information on features and configurations. Depending on your needs, you can refer to the following sections:
-- [Tutorials](./todo): Introduces the basic concepts of VRender and various usage methods.
+- [Tutorials](./Getting_Started): Introduces the basic concepts of VRender and various usage methods.
-- [Configuration Items](./todo): Provides all configuration items for VRender and their detailed explanations.
-
-- [API](./todo): Provides detailed descriptions of all available interfaces for VRender.
+- [Options](/vrender/option/Arc): Provides all the configuration options of VRender along with detailed explanations.
## Examples
-The examples page offers many practical application cases of VRender, from simple primitives to complex animations and custom extensions. Each example provides a detailed description, key configuration information, and source code. You can also modify the example and view the effect through the online editor.
-
-
-
-## How to use the search function
-
-The site offers a powerful search feature that allows you to quickly find related usage information. Click the search box at the top of the site, enter a keyword, and you will see matching results in the dropdown list. Select the appropriate result to view the relevant content.
-
-
+The examples page offers numerous practical application cases of VRender, ranging from simple primitives to complex animations and custom extensions. Each example provides detailed descriptions, key configuration information, and source code. You can also modify examples using the online editor and view the effects.
-## How to Ask Questions & Suggestions
+## How to Use the Search Function
-We are happy to help! If you encounter any problems while learning VRender, you can ask questions in the following ways:
+This site provides a powerful search function, allowing you to quickly find relevant usage information. Click on the search box at the top of the site, enter keywords, and you will see matching results in the dropdown list. Select the appropriate result to view the related content.
-1. Submit an issue in the GitHub repository: Visit [VRender GitHub](https://github.com/VisActor/VChart/issues/new/choose), describe in detail the problem you encountered, and our team will respond and resolve as soon as possible.
+## How to Ask Questions & Provide Suggestions
-2. Submit a discussion in the GitHub repository: Visit [VRender Discussion](https://github.com/VisActor/VChart/discussions), we are very welcome to propose your ideas and suggestions here, and our team will respond and resolve as soon as possible.
+We are happy to assist you! If you encounter any issues while learning VRender, you can ask questions in the following ways:
-## How to Report Errors
+1. Submit an issue on the GitHub repository: Visit [VRender GitHub](https://github.com/VisActor/VChart/issues/new/choose), provide a detailed description of the problem you are facing, and our team will respond and resolve it promptly.
-If you find any problems in the documentation or examples, or think some parts can be improved, please let us know. You can provide us with error correction information in the following ways:
+2. Start a discussion on the GitHub repository: Visit [VRender Discussion](https://github.com/VisActor/VChart/discussions), where you are welcome to share your ideas and suggestions. Our team will respond and address them promptly.
-1. Submit a pull request in the GitHub repository: Correct the content and submit, and a team member will review and merge.
+## How to Report Corrections
-2. Submit an issue: Point out the problem in the documentation, and team members will verify and correct it as soon as possible.
+If you find any issues in the document examples or believe that certain parts can be improved, please let us know. You can report corrections to us in the following ways:
-Thank you for your help! We will continue to improve the documentation and provide a better learning experience for all VRender users.
+1. Submit a pull request on the GitHub repository: Correct the content and submit it, and a team member will review and merge it.
-# Subpage Directory
+2. Submit an issue: Point out the issues in the document, and the team will verify and rectify them promptly.
-Currently unable to display this content outside the Feishu document.
\ No newline at end of file
+Thank you for your assistance! We will continuously improve the documentation to provide a better learning experience for all VRender users.
diff --git a/docs/assets/guide/menu.json b/docs/assets/guide/menu.json
index 7818f22df..657416cbd 100644
--- a/docs/assets/guide/menu.json
+++ b/docs/assets/guide/menu.json
@@ -60,10 +60,24 @@
},
"children": [
{
- "path": "Events_and_Animation",
+ "path": "Create_Instance",
"title": {
- "zh": "了解事件和动画",
- "en": "Events and Animation"
+ "zh": "创建实例",
+ "en": "Create Instance"
+ }
+ },
+ {
+ "path": "Graphic",
+ "title": {
+ "zh": "图元",
+ "en": "Graphic"
+ }
+ },
+ {
+ "path": "Event",
+ "title": {
+ "zh": "事件",
+ "en": "Event"
}
},
{
@@ -95,6 +109,68 @@
}
}
]
+ },
+ {
+ "path": "Advanced_Tutorial",
+ "title": {
+ "zh": "进阶教程",
+ "en": "Advanced Tutorial"
+ },
+ "children": [
+ {
+ "path": "Custom_Graphic",
+ "title": {
+ "zh": "自定义图元",
+ "en": "Custom Graphic"
+ }
+ },
+ {
+ "path": "Flex_Layout",
+ "title": {
+ "zh": "flex 布局",
+ "en": "Flex Layout"
+ }
+ }
+ ]
+ },
+ {
+ "path": "VRender_Components",
+ "title": {
+ "zh": "VRender组件",
+ "en": "VRender Components"
+ },
+ "children": [
+ {
+ "path": "VRender_Components_Introduction",
+ "title": {
+ "zh": "VRender组件介绍",
+ "en": "VRender Components Introduction"
+ }
+ },
+ {
+ "path": "VRender_Components_PopTip",
+ "title": {
+ "zh": "PopTip 组件",
+ "en": "PopTip Component"
+ }
+ }
+ ]
+ },
+ {
+ "path": "FAQ",
+ "title": {
+ "zh": "FAQ",
+ "en": "FAQ"
+ },
+ "children": [
+ {
+ "path": "What_Is_BoundsPadding",
+ "title": {
+ "zh": "boundsPadding是什么",
+ "en": "What Is BoundsPadding"
+ }
+ }
+ ]
}
]
}
diff --git a/docs/assets/guide/zh/asd/Advanced_Tutorial/Custom_Graphic.md b/docs/assets/guide/zh/asd/Advanced_Tutorial/Custom_Graphic.md
new file mode 100644
index 000000000..24d6b7afb
--- /dev/null
+++ b/docs/assets/guide/zh/asd/Advanced_Tutorial/Custom_Graphic.md
@@ -0,0 +1,273 @@
+# 自定义图元
+
+VRender提供了很多图元,但是有时候我们需要自定义图元,比如需要有一个能够播放[Lottie](https://lottiefiles.com/)动画的图元等,这时候就需要自定义图元了。VRender自定义图元需要以下几个步骤:
+
+1. 定义一个图元类,该类为`Graphic`的子类,当然你也可以直接继承一个现有的图元类,比如`Rect`、`Circle`等。
+2. 实现图元的绘制逻辑,该方法用于对图元的绘制操作。该类为`BaseRender`的子类,同样,你也可以直接继承一个现有的图元绘制类,比如`DefaultCanvasRectRender`、`DefaultCanvasCircleRender`等。
+3. 实现图元的拾取逻辑,该方法用于对图元的拾取操作。该类实现了`IGraphicPicker`接口同样,如果你的图元就是矩形的形状,那么可以直接继承`RectPickerBase`。注意的是,在某些场景中(小程序),并不支持基于Canvas的拾取,所以你需要实现一套`MathPicker`逻辑。当然,所有的pick逻辑都并不复杂,我们在下面章节中具体介绍。
+
+接下来,我们将以注册一个`Lottie`图元为例,介绍如何自定义图元。所有的代码均在`@visactor/vrender-kits`包中,效果演示如下。
+
+
+
+注意:在阅读此章节前,建议先阅读[图元](./Graphic)章节。
+
+## 依赖注入
+
+基于依赖注入机制,我们可以很方便的将各种扩展功能注入到VRender中,我们这里的自定义图元的所有功能都需要依赖注入才能实现。我们的依赖注入能力是基于`inversifyJS`改造的,所以API和`inversifyJS`对齐。如果想深入了解的话,建议去看[`inversifyJS`的文档](https://inversify.io/)。
+
+## 准备工作
+
+在开始写代码之前,我们思考一下具体的实现方案:
+
+1. 不难判断,`Lottie`图元是一个矩形区域的图元,而`Lottie`动画在这个图元里进行播放,所以我们可以直接继承`Rect`图元,给其加上播放`Lottie`动画的能力。
+
+2. 我们又发现,Lottie官方提供了一个播放器[lottie-web](https://github.com/airbnb/lottie-web),能够进行Lottie动画的解析和播放,我们可以直接使用,所以在渲染逻辑中,只需要借助官方的播放器,将画面绘制到我们的图元上即可。
+
+3. 拾取就很简单了,我们并不需要去具体的拾取到Lottie动画中的某个元素,只需要拾取整个Lottie动画播放的区域 -- 也就是我们的图元即可,所以拾取逻辑可直接复用Rect的逻辑
+
+在具体实现之前,我们先看一下Lottie官方提供的播放器是如何播放的[demo](https://codepen.io/collection/nVYWZR/)发现代码很简单,我们只需要将Lottie文件的JSON或者URL传入,然后在我们的图元中进行封装代理一下即可。
+
+## 定义图元类
+
+首先我们需要定义`Lottie`这个图元类,该类继承自`Graphic`,并且拥有`ILottieGraphicAttribute`接口的attribute。
+我们首先定义`ILottieGraphicAttribute`接口,它是传给图元的配置接口,用于定义图元的宽高位置属性等。在`Lottie`图元中该接口除了Rect的属性外,还需要增加`data`属性,用于保存Lottie动画的json数据。
+
+```ts
+type ILottieAttribute = {
+ data: string;
+};
+type ILottieGraphicAttribute = Partial & Partial;
+
+interface ILottie extends IGraphic {
+ lottieInstance?: AnimationItem; // 保存Lottie播放器的实例
+ canvas?: any; // 保存Lottie播放器的canvas实例
+}
+```
+
+接下来我们通过继承一个Rect图元,实现一个`Lottie`图元。我们只需要给Lottie图元中添加对外部的Lottie播放器的管理逻辑即可,在适当的时候初始化,在图元销毁的时候销毁这个播放器。
+```ts
+export class Lottie extends Rect implements ILottie {
+ type: any = 'lottie';
+ declare attribute: ILottieGraphicAttribute;
+ declare lottieInstance?: AnimationItem;
+ declare canvas?: any;
+
+ static NOWORK_ANIMATE_ATTR = NOWORK_ANIMATE_ATTR;
+
+ constructor(params: ILottieGraphicAttribute) {
+ super(params);
+ this.numberType = LOTTIE_NUMBER_TYPE;
+ this.initLottieWeb(this.attribute.data);
+ }
+
+ /* 设置属性的时候,尝试重新初始化Lottie的播放器 */
+ setAttributes(params: Partial, forceUpdateTag?: boolean, context?: any): void {
+ if (params.data) {
+ this.initLottieWeb(params.data);
+ }
+ return super.setAttributes(params, forceUpdateTag, context);
+ }
+
+ setAttribute(key: string, value: any, forceUpdateTag?: boolean, context?: any): void {
+ if (key === 'data') {
+ this.initLottieWeb(value);
+ }
+ return super.setAttribute(key, value, forceUpdateTag, context);
+ }
+
+ getGraphicTheme(): Required {
+ return getTheme(this).rect;
+ }
+
+ /* 初始化Lottie的播放器 */
+ initLottieWeb(data: string) {
+ // 必须是浏览器环境才行
+ if (vglobal.env !== 'browser') {
+ return;
+ }
+ if (this.lottieInstance) {
+ this.releaseLottieInstance();
+ }
+ const theme = this.getGraphicTheme();
+ const { width = theme.width, height = theme.height } = this.attribute;
+ const canvas = vglobal.createCanvas({ width, height, dpr: vglobal.devicePixelRatio });
+ const params: any = {
+ // wrapper: svgContainer,
+ rendererSettings: {
+ context: canvas.getContext('2d')
+ },
+ animType: 'canvas',
+ loop: true
+ };
+ if (typeof data === 'string') {
+ params.path = data;
+ } else {
+ params.animationData = data;
+ }
+ this.lottieInstance = bodymovin.loadAnimation(params);
+ this.canvas = canvas;
+ // 在每次Lottie渲染一帧的时候,我们都需要重新渲染一次图元
+ this.lottieInstance.addEventListener('drawnFrame', this.renderNextFrame);
+ }
+
+ renderNextFrame = () => {
+ this.stage.renderNextFrame();
+ };
+
+ /* 添加销毁逻辑 */
+ release(): void {
+ super.release();
+ this.releaseLottieInstance();
+ }
+
+ releaseLottieInstance() {
+ this.lottieInstance.removeEventListener('drawnFrame', this.renderNextFrame);
+ this.lottieInstance.destroy();
+ this.lottieInstance = null;
+ }
+}
+
+export function createLottie(attributes: ILottieGraphicAttribute): ILottie {
+ return new Lottie(attributes);
+}
+```
+
+## 定义渲染逻辑
+
+我们的图元已经定义好了,接下来我们需要定义图元的渲染逻辑。我们需要定义一个`DefaultCanvasLottieRender`类,该类继承自`DefaultCanvasRectRender`,并且实现`IGraphicRender`接口。
+在这里我们只需要实现一下drawShape接口,Rect渲染的时候接fillCb和strokeCb两个回调函数,在fillCb中,我们需要将Lottie的canvas生成一份pattern绘制到图元上。
+
+```ts
+@injectable()
+export class DefaultCanvasLottieRender extends DefaultCanvasRectRender implements IGraphicRender {
+ type: 'glyph';
+ numberType: number = LOTTIE_NUMBER_TYPE;
+
+ drawShape(
+ lottie: ILottie,
+ context: IContext2d,
+ x: number,
+ y: number,
+ drawContext: IDrawContext,
+ params?: IGraphicRenderDrawParams,
+ fillCb?: (
+ ctx: IContext2d,
+ markAttribute: Partial,
+ themeAttribute: IThemeAttribute
+ ) => boolean,
+ strokeCb?: (
+ ctx: IContext2d,
+ markAttribute: Partial,
+ themeAttribute: IThemeAttribute
+ ) => boolean
+ ): void {
+ const _fillCb = fillCb || (() => this._drawShape.call(this, lottie, context, x, y, drawContext, params));
+ super.drawShape(lottie, context, x, y, drawContext, params, _fillCb, strokeCb);
+ }
+
+ _drawShape(
+ lottie: ILottie,
+ context: IContext2d,
+ x: number,
+ y: number,
+ drawContext: IDrawContext,
+ params?: IGraphicRenderDrawParams
+ ): void {
+ const lottieAttribute = this.tempTheme ?? getTheme(lottie, params?.theme).rect;
+ const { x: originX = lottieAttribute.x, y: originY = lottieAttribute.y } = lottie.attribute;
+ context.setCommonStyle(lottie, lottie.attribute, originX - x, originY - y, lottieAttribute);
+ // 设置pattern,绘制lottie
+ const canvas = lottie.canvas;
+ if (canvas) {
+ // const _ctx = canvas.getContext('2d');
+ const pattern = context.createPattern(canvas, 'no-repeat');
+ const dpr = context.dpr;
+ pattern.setTransform && pattern.setTransform(new DOMMatrix([1 / dpr, 0, 0, 1 / dpr, x, y]));
+ context.fillStyle = pattern;
+ }
+ context.fill();
+ }
+}
+```
+
+## 自定义拾取逻辑
+
+我们的Lottie图元已经实现了,接下来我们需要实现一下拾取逻辑。我们需要定义一个`DefaultCanvasLottiePicker`类,该类继承自`RectPickerBase`,并且实现`IGraphicPicker`接口。这里的逻辑就非常简单了,因为我们的拾取就是按照矩形来拾取的,所以继承了矩形的拾取类之后什么都不用做
+
+```ts
+@injectable()
+export class DefaultCanvasLottiePicker extends RectPickerBase implements IGraphicPicker {
+ constructor(@inject(RectRender) public readonly canvasRenderer: IGraphicRender) {
+ super();
+ }
+}
+```
+
+## 注册
+
+最后我们需要实现图元注册、渲染注册、拾取注册的逻辑。
+
+1. 图元注册
+
+图元是无需注册的,我们需要注意的一点就是图元的`numberType`字段要和对应的渲染类和拾取类的`numberType`字段一致。
+
+2. 渲染注册
+
+渲染逻辑是需要通过依赖注入的方式注册的
+
+```ts
+let loadLottieModule = false;
+export const lottieModule = new ContainerModule(bind => {
+ if (loadLottieModule) {
+ return;
+ }
+ loadLottieModule = true;
+ // lottie渲染器
+ bind(DefaultCanvasLottieRender).toSelf().inSingletonScope();
+ bind(GraphicRender).toService(DefaultCanvasLottieRender);
+});
+```
+
+3. 拾取注册
+
+和渲染注册一样,拾取类也是通过依赖注入的方式注册的
+
+```ts
+let loadLottiePick = false;
+export const lottieCanvasPickModule = new ContainerModule((bind, unbind, isBound, rebind) => {
+ if (loadLottiePick) {
+ return;
+ }
+ loadLottiePick = true;
+ bind(CanvasLottiePicker).to(DefaultCanvasLottiePicker).inSingletonScope();
+ bind(CanvasPickerContribution).toService(CanvasLottiePicker);
+});
+```
+
+## 使用
+
+接下来,我们在代码中就可以加载`Lottie`图元相关代码,然后使用了。
+
+```ts
+container.load(lottieModule);
+container.load(lottieCanvasPickModule);
+
+const lottie = createLottie({
+ data: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vrender/custom-graphic-lottie-animate.json',
+ width: 300,
+ height: 300,
+ x: 100,
+ y: 100,
+ cornerRadius: 20,
+ background: 'pink'
+});
+
+const stage = createStage({
+ canvas: 'main',
+ autoRender: true
+});
+
+stage.defaultLayer.add(lottie);
+window['stage'] = stage;
+```
diff --git a/docs/assets/guide/zh/asd/Advanced_Tutorial/Flex_Layout.md b/docs/assets/guide/zh/asd/Advanced_Tutorial/Flex_Layout.md
new file mode 100644
index 000000000..70bdb7420
--- /dev/null
+++ b/docs/assets/guide/zh/asd/Advanced_Tutorial/Flex_Layout.md
@@ -0,0 +1,141 @@
+# flex布局
+
+*【注意】*通常情况下,`VRender`默认并不具备类似`DOM`的布局,比如在`DOM`中,你可以写两个`div`,那么第二个`div`会放置在第一个div的下方。但如果在`VRender`中,你写了两个矩形,那么第二个矩形会覆盖在第一个矩形之上。这是因为`VRender`中所有的定位都是相对定位的,*它依靠你配置的x、y等参数定位*,坐标系是左上角为原点,向右为`x`轴正方向,向下为`y`轴正方向。这就导致了`VRender`的布局和`DOM`的布局是不一样的。
+
+但是我们还提供了`flex`布局的能力,开启该功能之后,我们也可以在`VRender`中实现类似`DOM` Flex布局的能力。
+
+## 使用
+
+VRender中布局的能力是通过插件功能实现的,首先我们需要再stage的参数中开启该插件:
+
+```ts
+const stage = VRender.createStage({
+ container: CONTAINER_ID,
+ autoRender: true,
+ enableLayout: true, // 开启布局能力
+});
+```
+
+开启后场景树的布局依然默认是相对定位进行布局的,依靠配置的x、y以及父元素的相对位置,来确定当前元素的位置。但是我们可以通过给元素配置`flex`属性来开启`flex`布局,开启后,子元素就会按照`flex`布局的规则进行布局。
+
+可配置的属性接口如下,规则和浏览器的Flex布局规则一致
+
+```ts
+display?: 'relative' | 'inner-block' | 'flex';
+flexDirection?: 'row' | 'row-reverse' | 'column' | 'column-reverse';
+flexWrap?: 'nowrap' | 'wrap';
+justifyContent?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around';
+alignItems?: 'flex-start' | 'flex-end' | 'center';
+alignContent?: 'flex-start' | 'center' | 'space-between' | 'space-around';
+```
+
+其会根据子元素的AABBBounds的大小来进行布局,你可以通过BoundsPadding来动态调整元素的AABBBounds,来达到你想要的布局效果。相关内容可以参考[BoundsPadding](../FAQ/What_Is_BoundsPadding)。
+
+我们这里给出一个示例演示:
+
+```javascript livedemo template=vrender
+const group = VRender.createGroup({ x: 100, y: 100, width: 260, height: 80, background: '#cecece', display: 'flex' });
+
+const g1 = VRender.createGroup({
+ display: 'flex',
+ background: 'green',
+ width: 60,
+ height: 80,
+ direction: 'column',
+ alignItems: 'center',
+ justifyContent: 'space-around'
+});
+
+const img = VRender.createImage({
+ image: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg',
+ width: 50,
+ height: 50
+})
+g1.add(img);
+
+const g2 = VRender.createGroup({
+ display: 'flex',
+ background: 'red',
+ width: 200,
+ height: 80,
+ direction: 'column'
+});
+
+const g21 = VRender.createGroup({
+ display: 'flex',
+ background: 'orange',
+ width: 200,
+ height: 40,
+ direction: 'column',
+ alignItems: 'center',
+ justifyContent: 'center'
+});
+
+const text1 = VRender.createText({ text: '虚拟主播小花', fontSize: 13, fontFamily: 'sans-serif', fill: 'black' });
+const icon = VRender.createImage({
+ image: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/location.svg',
+ width: 15,
+ height: 15,
+ boundsPadding: [0, 0, 0, 10]
+});
+const text2 = VRender.createText({ text: '梦幻之都', fontSize: 11, fontFamily: 'sans-serif', fill: '#6f7070' });
+
+g21.add(text1);
+g21.add(icon);
+g21.add(text2);
+
+const g22 = VRender.createGroup({
+ display: 'flex',
+ background: 'pink',
+ width: 200,
+ height: 40,
+ direction: 'column',
+ alignItems: 'center'
+});
+
+['游戏', '动漫', '美食'].forEach(text => {
+ const tag = new VRenderComponent.Tag({
+ visible: true,
+ textStyle: {
+ fontSize: 10,
+ fill: 'rgb(51, 101, 238)',
+ textAlign: 'left',
+ textBaseline: 'top',
+ fontFamily: 'sans-serif'
+ },
+ space: 4,
+ padding: 5,
+ shape: {
+ fill: '#000'
+ },
+ text,
+ panel: {
+ visible: true,
+ fill: '#f4f4f2',
+ cornerRadius: 5
+ },
+ marginLeft: 10,
+ boundsPadding: [0, 0, 0, 10],
+ x: 20,
+ y: 10
+ });
+
+ g22.add(tag);
+});
+
+group.add(g1);
+group.add(g2);
+
+g2.add(g21);
+g2.add(g22);
+
+const stage = new VRender.Stage({
+ container: CONTAINER_ID,
+ autoRender: true,
+ enableLayout: true
+});
+
+stage.defaultLayer.add(group);
+
+window['stage'] = stage;
+```
diff --git a/docs/assets/guide/zh/asd/Basic/Vrender_basic_tutorial.md b/docs/assets/guide/zh/asd/Basic/Vrender_basic_tutorial.md
index cadf231cc..72b9ea8e3 100644
--- a/docs/assets/guide/zh/asd/Basic/Vrender_basic_tutorial.md
+++ b/docs/assets/guide/zh/asd/Basic/Vrender_basic_tutorial.md
@@ -14,7 +14,7 @@ VRender 是一个比较底层的渲染库,我们将以使用场景作为区分
## 图元系统
-此章节会快速介绍图元系统,如需详细了解,请参考[图元系统(等待编写文档)](./graphic)
+此章节会快速介绍图元系统,如需详细了解,请参考[图元系统](./Basic_Tutorial/Graphic)
### 创建图元
diff --git a/docs/assets/guide/zh/asd/Basic_Tutorial/Animate.md b/docs/assets/guide/zh/asd/Basic_Tutorial/Animate.md
index eff1c3181..d64a5a83a 100644
--- a/docs/assets/guide/zh/asd/Basic_Tutorial/Animate.md
+++ b/docs/assets/guide/zh/asd/Basic_Tutorial/Animate.md
@@ -1,7 +1,30 @@
# 动画
+VRender的动画是通过`Animate`实例来实现的,`Animate`提供了多种插值方法,包括`to`、`from`、`wait`、`loop`、`bounce`、`reverse`、`startAt`等方法,这些方法可以链式调用,实现复杂的动画效果。我们通过图元的API`animate()`来创建的一个`Animate`实例。在阅读此章节之前,建议先了解图元的API,才能更好地理解动画的使用。图元的文档可以参考[图元](./Graphic)章节。
# 基本使用
+创建动画的基本demo:
+
+```javascript livedemo template=vrender
+const rect = VRender.createRect({ x: 100, y: 100, width: 100, height: 100, fill: 'red' });
+
+const stage = new VRender.Stage({
+ container: CONTAINER_ID,
+ autoRender: true
+});
+
+stage.defaultLayer.add(rect);
+
+// 创建动画,链式调用
+rect
+ .animate()
+ // 终点属性,时间,插值函数
+ .to({ height: 200 }, 2000, 'quadIn')
+ .to({ x: 300 }, 200, 'quadIn')
+ .wait(2000)
+ .to({ y: 300 }, 200, 'backInOut')
+```
+
## Graphic.Animate
graphic.animate创建一个动画,提供`onStart`,`onEnd`,`onFrame`等钩子
@@ -19,11 +42,6 @@ graphic
console.log('某一帧');
}
})
- .to({ height: 200 }, 2000, 'quadIn')
- .to({ x: 600 }, 200, 'quadIn')
- .wait(200)
- .to({ y: 600 }, 200, 'backInOut')
- .to({ fillColor: 'green' }, 2000, 'quadIn');
```
## Animate.to
@@ -273,4 +291,4 @@ export class IncreateCount extends ACustomAnimate {
```TypeScript
text .animate() .to({ fillColor: 'red' }, 1000, 'quadIn') .play(new IncreateCount(0, 0, 1000, 'quartIn')) .to({ fillColor: 'green' }, 1000, 'quadIn');
-```
\ No newline at end of file
+```
diff --git a/docs/assets/guide/zh/asd/Basic_Tutorial/Create_Instance.md b/docs/assets/guide/zh/asd/Basic_Tutorial/Create_Instance.md
new file mode 100644
index 000000000..e2eb99cce
--- /dev/null
+++ b/docs/assets/guide/zh/asd/Basic_Tutorial/Create_Instance.md
@@ -0,0 +1,163 @@
+# 创建实例
+
+和Dom树、React虚拟节点树类似,VRender也是基于一颗场景树进行绘制的,,VRender通过图层来挂载这颗场景树,图层则由Stage进行管理,Stage管理整个应用的生命周期,视图的位置和大小,场景的绘制、拾取等逻辑。
+
+如下图所示,一个VRender应用一般包括一个`Stage`,`Stage`下可挂载多个图层(`Layer`),图层下可以挂载多个图形元素。
+
+
+
+## 创建Stage
+
+Stage有两种方式创建,一种是通过`new`关键字创建,另一种是通过`createStage`方法创建。Stage接受的一个对象为参数,对象中可配置的属性有很多,其中最常用的属性如下:
+
+- `container`:挂载的容器,需要是Dom元素,仅在浏览器环境可用
+- `canvas`:挂载的画布,需要是Canvas元素,和container互斥,可以在不同环境中使用
+- `width`:画布的宽度
+- `height`:画布的高度
+- `autoRender`:是否自动渲染,如果配置为true,那么无需手动调用`stage.render()`方法,会自动渲染
+- `background`:画布的背景色,默认为白色
+
+```ts
+import { Stage, createStage } from '@visactor/vrender';
+// import { Stage, createStage } from '@visactor/vrender-core';
+
+const stage1 = new Stage({
+ container: document.getElementById('container'),
+ width: 600,
+ height: 600,
+ autoRender: true,
+ background: 'pink'
+})
+
+const stage2 = createStage({
+ container: document.getElementById('container'),
+ width: 600,
+ height: 600,
+ autoRender: true,
+ background: 'pink'
+})
+```
+
+stage支持的所有参数配置如下:
+
+```ts
+interface IStageParams {
+ // 视口的宽高
+ viewBox: IBoundsLike;
+ // 总的宽高
+ width: number;
+ height: number;
+ dpr: number;
+ // stage的背景
+ background: string | IColor;
+ // 外部的canvas
+ canvas: string | HTMLCanvasElement;
+ // canvas的container容器,如果不传入canvas,那就会在容器中创建canvas
+ container: string | HTMLElement;
+ // 是否是受控制的canvas,如果不是的话,不会进行resize等操作,也不会修改canvas的样式
+ canvasControled: boolean;
+ title: string;
+ // 是否开启自动渲染
+ autoRender: boolean;
+ // 是否开启布局支持
+ enableLayout: boolean;
+ // 是否关闭脏矩形检测
+ disableDirtyBounds: boolean;
+ // 是否支持interactiveLayer,默认为true
+ interactiveLayer: boolean;
+ // 是否支持HTML属性
+ enableHtmlAttribute: string | boolean | HTMLElement;
+ // 是否支持react-dom(传入ReactDOM)
+ ReactDOM: any;
+ // 是否支持滚动条
+ enableScroll: boolean;
+ // 是否支持poptip
+ poptip: boolean;
+ // 绘制之前的钩子函数
+ beforeRender: (stage: IStage) => void;
+ // 绘制之后的钩子函数
+ afterRender: (stage: IStage) => void;
+ // 渲染风格
+ renderStyle?: string;
+ // 自定义ticker
+ ticker?: ITicker;
+ // 开启的插件列表
+ pluginList?: string[];
+ // 优化配置
+ optimize?: IOptimizeType;
+ /**
+ * 事件系统相关配置
+ */
+ event?: EventConfig;
+
+ /**
+ * @since 0.17.15
+ * 是否支持touch事件,不支持就不监听touch事件
+ */
+ supportsTouchEvents?: boolean;
+
+ /**
+ * @since 0.17.15
+ * 是否支持pointer事件,不支持就监听mouse事件
+ */
+ supportsPointerEvents?: boolean;
+
+ context?: IStageCreateContext;
+}
+```
+
+## 创建图层
+
+Stage默认会创建一个图层`stage.defaultLayer`,图层是一个容器,用于挂载图形元素,一个`Stage`中可以包含任意多个图层,在`VChart`中我们常常使用一个图层作为主图层,再创建一个图层用于存放`tooltip`等图元和组件。
+
+创建图层的方法为`stage.createLayer()`。
+
+```ts
+createLayer(canvasId?: string, layerMode?: LayerMode): ILayer;
+```
+
+## 添加图元
+
+VRender提供了很多图元,图元的具体介绍可以查看[图元](./Graphic)章节。图元的创建和Stage类似,也是提供了两种创建方式,我们以矩形图元为例:
+
+```ts
+import { Rect, createRect } from '@visactor/vrender';
+
+const rect1 = new Rect({
+ x: 100,
+ y: 100,
+ width: 100,
+ height: 100,
+ fill: 'red'
+});
+
+const rect2 = createRect({
+ x: 100,
+ y: 100,
+ width: 100,
+ height: 100,
+ fill:'red'
+});
+```
+
+我们将矩形添加到`stage.defaultLayer`上,就可以展示了
+
+```javascript livedemo template=vrender
+// 注册所有需要的内容
+const stage = new VRender.Stage({
+ container: CONTAINER_ID,
+ autoRender: true
+});
+
+const rect = VRender.createRect({
+ x: 100,
+ y: 100,
+ width: 100,
+ height: 100,
+ fill:'red'
+});
+
+stage.defaultLayer.add(rect);
+
+window['stage'] = stage;
+```
diff --git a/docs/assets/guide/zh/asd/Basic_Tutorial/Cross_platform_Interface.md b/docs/assets/guide/zh/asd/Basic_Tutorial/Cross_platform_Interface.md
index e4954f352..b4b3c34e4 100644
--- a/docs/assets/guide/zh/asd/Basic_Tutorial/Cross_platform_Interface.md
+++ b/docs/assets/guide/zh/asd/Basic_Tutorial/Cross_platform_Interface.md
@@ -1,15 +1,85 @@
# 跨端接口使用
+本章节并不是开发中必须的,如果你的应用需要再不同环境中运行(nodejs,浏览器,小程序)。又不想自己去写兼容,比如获取dpr(devicePixelRatio),创建Canvas,或者想用一些工具函数,就可以阅读本章节。
VRender提供了一系列默认的接口,用于屏蔽跨端的影响,目前会支持`Browser`、`Node`、`feishu`、`tt`等环境,其余环境可以通过扩展支持。
## Global
Global是一个静态类,提供了全局的跨端API,用户可以直接拿Global当浏览器的window用,Global会自动提供跨平台的兼容
-Global需要手动设置env,不需要自行添加跨端方法
+> Global需要手动设置env,不需要自行添加跨端方法
+> 注意:node端使用node-canvas,Canopus不会自动引用,需要用户手动将node-canvas包传入
-注意:node端使用node-canvas,Canopus不会自动引用,需要用户手动将node-canvas包传入
+```ts
+type IEnv = 'feishu' | 'tt' | 'browser' | 'node';
+
+interface IDocument {
+ createElement(t: 'canvas'): HTMLCanvasElement;
+ createElement(t: string): HTMLElement;
+}
+
+export interface IGlobal {
+ readonly env: IEnv;
+ // 如果是nodejs环境,那么必传params
+ setEnv: (env: IEnv, params: { canvasPkg?: { canvas: any; loadImage: any } }) => void;
+ createCanvas: () => ICanvas;
+ releaseCanvas: (canvas: ICanvas) => void;
+ getImageData: (ctx?: IContext2d) => ImageData;
+ // 加载图片
+ loadImage: (url: string, type: 'Image' | 'ImageData') => HTMLImageElement | Image | ImageData;
+ // RAF
+ requestAnimationFrame: IRequestAnimationFrame;
+ // 获取dpr
+ devicePixelRatio: number;
+ document: IDocument;
+}
+```
## GraphicUtil
-与Global不同的是,GraphicUtil提供的是跨端方面以及图形方面的API,其中包括Transform API,MeasureText API
\ No newline at end of file
+与Global不同的是,GraphicUtil提供的是跨端方面以及图形方面的API,其中包括Transform API,MeasureText API
+
+Transform模块是方便用户手动进行变换,基于一个已有的变换配置,变换到另一个空间
+
+```ts
+interface IGraphicUtil {
+ canvas?: ICanvas;
+ context?: IContext2d | null;
+ textMeasure: ITextMeasure;
+ measureText: (text: string, tc: TextOptionsType) => { width: number; height: number };
+ bindTextMeasure: (tm: ITextMeasure) => void;
+ createTextMeasureInstance: (
+ textSpec?: Partial,
+ option?: Partial,
+ getCanvasForMeasure?: () => any
+ ) => TextMeasure;
+ drawGraphicToCanvas: (
+ graphic: IGraphic,
+ stage: IStage
+ ) => HTMLCanvasElement | null | Promise;
+}
+
+interface TransformType {
+ x: number, y: number,
+ scaleX: number, scaleY: number,
+ // shearMatrix是剪切变换的矩阵(如果存在剪切变换的话)
+ angle: number, shearMatrix?: Matrix
+}
+
+interface Transform {
+ // 提供初始的变换配置
+ init: (origin: TransformType) => Transform;
+ translate: (x: number, y: number) => Transform;
+ translateTo: (x: number, y: number) => Transform;
+ scale: (sx: number, sy: number, center: IPoint) => Transform;
+ scaleTo: (sx: number, sy: number, center: IPoint) => Transform;
+ rotate: (angle: number, center: IPoint) => Transform;
+ rotateTo: (angle: number, center: IPoint) => Transform;
+ // 语法糖,交互的时候可用,每次传入这次的变化数据
+ interactive: (dx: number, dy: number, dsx: number, dsy: number, drx: number, dry: number) => Transform;
+ // 扩展padding像素,用于外描边,内描边
+ extend: (origin: TransformType, padding: number) => Transform;
+ // 将所有的transform生成为一次的transform
+ simplify: (target?: TransformType) => TransformType;
+}
+```
diff --git a/docs/assets/guide/zh/asd/Basic_Tutorial/Event.md b/docs/assets/guide/zh/asd/Basic_Tutorial/Event.md
new file mode 100644
index 000000000..f838815f7
--- /dev/null
+++ b/docs/assets/guide/zh/asd/Basic_Tutorial/Event.md
@@ -0,0 +1,124 @@
+# 事件
+
+VRender将 DOM 事件模型及 API 作为参照标准进行设计,提供了一系列默认基于图元的事件,VRender底层会兼容不同浏览器版本,提供统一的事件。支持的事件类型包括pointer事件、mouse事件、touch事件、wheel事件
+
+* pointer事件
+ - pointerdown
+ - pointerup
+ - pointerupoutside
+ - pointertap
+ - pointerover
+ - pointerenter
+ - pointerleave
+ - pointerout
+* 左键操作
+ - mousedown
+ - mouseup
+ - mouseupoutside 鼠标抬起与按下时图形不同
+* 右键操作
+ - rightdown
+ - rightup
+ - rightupoutside 鼠标抬起与按下时图形不同
+* 鼠标操作
+ - click
+ - mousemove
+ - mouseover
+ - mouseout
+ - mouseenter
+ - mouseleave
+* 滚动
+ - wheel
+* touch事件
+ - touchstart
+ - touchend
+ - touchendoutside
+ - touchmove
+ - touchcancel
+ - tap
+* 通配事件
+ - *
+
+## 监听与代理
+VRender可以直接针对图元进行事件的监听和处理,支持`addEventListener`和`removeEventListener`方法,,且方法的参数和Dom一致,支持在捕获流程或者冒泡流程中使用:
+
+```ts
+interface AddEventListenerOptions extends EventListenerOptions {
+ once?: boolean;
+ passive?: boolean;
+ signal?: AbortSignal;
+}
+
+addEventListener(
+ type: string,
+ listener: EventListenerOrEventListenerObject | LooseFunction,
+ options?: AddEventListenerOptions | boolean
+): void;
+
+removeEventListener(
+ type: string,
+ listener: EventListenerOrEventListenerObject | LooseFunction,
+ options?: AddEventListenerOptions | boolean
+): void;
+
+type on = addEventListener;
+type off = removeEventListener;
+
+// 仅监听一次
+once(
+ type: string,
+ listener: EventListenerOrEventListenerObject | LooseFunction,
+ options?: AddEventListenerOptions | boolean
+): void;
+
+```
+
+```javascript livedemo template=vrender
+// 注册所有需要的内容
+const stage = new VRender.Stage({
+ container: CONTAINER_ID,
+ autoRender: true
+});
+
+const rect = VRender.createRect({
+ x: 100,
+ y: 100,
+ width: 100,
+ height: 100,
+ fill:'red'
+});
+
+rect.addEventListener('click', () => {
+ rect.setAttribute('fill', 'blue');
+})
+
+stage.defaultLayer.add(rect);
+
+window['stage'] = stage;
+```
+
+同时,VRender也支持事件的代理,任意节点都可以直接代理到子图元上,可以通过Event里的target来判断真正出发事件的元素是什么,下面是一个事件代理的例子。
+
+```javascript livedemo template=vrender
+// 注册所有需要的内容
+const stage = new VRender.Stage({
+ container: CONTAINER_ID,
+ autoRender: true
+});
+
+const group = VRender.createGroup({x: 100, y: 100, width: 200, height: 200, fill: 'pink'});
+const rect = VRender.createRect({ x: 50, y: 50, fill: 'green', width: 100, height: 100 });
+group.add(rect);
+group.addEventListener('click', (e) => {
+ if (e.target === group) {
+ group.setAttribute('fill', 'orange');
+ } else {
+ rect.setAttribute('fill', 'red');
+ }
+});
+
+stage.defaultLayer.add(group);
+
+window['stage'] = stage;
+```
+
+【注意】:需要注意的是,VRender抛出的事件对象是复用的,所以注意不要保存事件对象在后续使用,因为事件对象会变的。这点在异步流程中需要特别注意
diff --git a/docs/assets/guide/zh/asd/Basic_Tutorial/Graphic.md b/docs/assets/guide/zh/asd/Basic_Tutorial/Graphic.md
new file mode 100644
index 000000000..a8eb6305f
--- /dev/null
+++ b/docs/assets/guide/zh/asd/Basic_Tutorial/Graphic.md
@@ -0,0 +1,556 @@
+# 图元
+
+## 介绍
+
+VRender有很多图元,但是它们的使用方法都差不多,因为他们都继承自`Graphic`类,它们都有几乎相同的属性和方法,只是接受的参数不同。下图所示为图元的继承关系图。
+
+
+
+1. 首先他们都继承自`Node`,所以所有图元都有树的结构。
+2. 然后他们都继承自`Graphic`,所以他们都有图元的基础属性和方法比如`x`、`y`、`fill`等。
+3. `Graphic`分化出了基本的图元类型比如`Text`、`Rect`、`Arc`等,也有一个特殊的类型`Group`,`Group`可以包含其他的图元,`Group`也可以作为一个图元添加到另一个`Group`图元中。
+4. [创建实例](./Create_Instance)章节中介绍的`Stage`和`Layer`就继承自`Group`,同时`VRender`中的组件类型(组件是带有逻辑的特殊组合图元)也继承自`Group`,组件内容可以参考[组件](./Component)章节。
+5. VRender中基于基础的组件类型实现了各种各样的组件,比如`poptip`、`axis`、`label`等等。
+
+图元的创建代码如下:
+
+```ts
+import { Rect, createRect, Text, createText } from '@visactor/vrender';
+
+const rectAttribute = {
+ x: 100,
+ y: 100,
+ width: 100,
+ height: 100,
+ fill: 'red'
+}
+const rect1 = new Rect(rectAttribute);
+const rect2 = createRect(rectAttribute);
+
+const textAttribute = {
+ x: 100,
+ y: 100,
+ text: 'hello world',
+ fill:'red'
+}
+const text1 = new Text(textAttribute);
+const text2 = createText(textAttribute);
+
+// ...其他图元
+```
+## 树结构(Node)
+
+图元都继承自Node,所以都有树的结构,图元也提供了一些方法用于操作树结构。树结构是基于链表实现的,但提供了`forEachChildren`方法用于遍历树结构,`insertBefore`,`insertAfter`,`insertInto`,`appendChild`,`removeChild`,`removeAllChild`等方法用于操作树结构。
+
+```ts
+interface INode extends Releaseable, IEventElement {
+ _prev?: INode;
+ _next?: INode;
+ _uid: number;
+ id?: number | string;
+ name?: string;
+ type?: string;
+ // 父元素
+ parent: INode | null;
+ // 子元素数量(包含自己)
+ count: number;
+ // 子元素数量(不包含自己)
+ childrenCount: number;
+ // 第一个子元素
+ firstChild: INode | null;
+ // 最后一个子元素
+ lastChild: INode | null;
+ // 获取所有子元素
+ getChildren: () => INode[];
+ // 获取在idx位置的子元素
+ getChildAt: (idx: number) => INode | null;
+ // getChildAt语法糖
+ at: (idx: number) => INode | null;
+ // 向前插入
+ insertBefore: (newNode: INode, referenceNode: INode) => INode | null;
+ // 向后插入
+ insertAfter: (newNode: INode, referenceNode: INode) => INode | null;
+ // 插入到idx位置
+ insertInto: (ele: INode, idx: number) => INode | null;
+ // 遍历子元素
+ forEachChildren: (cb: (n: INode, i: number) => void | boolean, reverse?: boolean) => void;
+ // 遍历子元素,异步
+ forEachChildrenAsync: (cb: (n: INode, i: number) => Promise | void | boolean, reverse?: boolean) => Promise;
+ // 添加到最后一个子元素
+ appendChild: (node: INode, highPerformance?: boolean) => INode | null;
+ // appendChild语法糖
+ add: (node: INode, highPerformance?: boolean) => INode | null;
+ // 删除自己
+ delete: () => void;
+ // 删除子节点
+ removeChild: (node: INode, highPerformance?: boolean) => INode | null;
+ // 删除所有子节点
+ removeAllChild: (deep?: boolean) => void;
+ // 判断是否是node的子节点
+ isChildOf: (node: INode) => boolean;
+ // 判断是否是node的父节点
+ isParentOf: (node: INode) => boolean;
+ // 判断是否是node的后代节点
+ isDescendantsOf: (node: INode) => boolean;
+ // 判断是否是node的祖先节点
+ isAncestorsOf: (node: INode) => boolean;
+ // 触发事件
+ dispatchEvent: (event: Event) => boolean;
+ // 返回的是一个布尔值,来表示传入的节点是否为该节点的后代节点。
+ containNode: (node: INode) => boolean;
+ // 设置该节点的所有后代节点某个属性
+ setAllDescendantsProps: (propsName: string, propsValue: any) => any;
+ // 根据自定义逻辑查找元素,返回单一图形元素
+ find: (callback: (node: INode, index: number) => boolean, deep: boolean) => INode | null;
+ // 根据自定义逻辑查找元素,返回匹配的元素集合
+ findAll: (callback: (node: INode, index: number) => boolean, deep: boolean) => INode[];
+ // 通过用户设置的 id 查找对应的图形元素
+ getElementById: (id: string | number) => INode | null;
+ // getElementById语法糖
+ findChildById: (id: string | number) => INode | null;
+ // 通过用户设置的 uid 查找对应的图形元素
+ findChildByUid: (uid: number) => INode | null;
+ // 通过用户设置的 name 查找对应的图形元素
+ getElementsByName: (name: string) => INode[];
+ // getElementsByName语法糖
+ findChildrenByName: (name: string) => INode[];
+ // 通过用户设置的 type 查找对应的图形元素
+ getElementsByType: (type: string) => INode[];
+}
+```
+
+## 图元结构(Graphic)
+
+通过树结构,我们可以构建一个场景树了,但是我们还需要一些属性来描述图元的一些属性,比如位置、大小、颜色等等,所以`Graphic`基类来描述图元的一些属性。所有图元同样可以使用
+
+```ts
+interface IGraphic = Partial>
+ extends INode,
+ IAnimateTarget {
+ // 图元类型
+ type?: GraphicType;
+ // 图元类型(数字类型)
+ numberType?: number;
+ // 绑定到的stage
+ stage?: IStage;
+ // 绑定到的layer
+ layer?: ILayer;
+ // 影子节点
+ shadowRoot?: IShadowRoot;
+
+ // 是否合法
+ valid: boolean;
+ // 是否是容器节点(继承自Group)
+ isContainer?: boolean;
+ // 是否是3d模式(是否应用3d视角)
+ in3dMode?: boolean;
+ // 属性参数
+ attribute: Partial;
+
+ /** 用于实现morph动画场景,转换成bezier曲线渲染 */
+ pathProxy?: ICustomPath2D | ((attrs: T) => ICustomPath2D);
+
+ // 获取state图形属性的方法
+ stateProxy?: (stateName: string, targetStates?: string[]) => Partial;
+
+ /* 状态相关方法 */
+ toggleState: (stateName: string, hasAnimation?: boolean) => void;
+ removeState: (stateName: string, hasAnimation?: boolean) => void;
+ clearStates: (hasAnimation?: boolean) => void;
+ useStates: (states: string[], hasAnimation?: boolean) => void;
+ addState: (stateName: string, keepCurrentStates?: boolean, hasAnimation?: boolean) => void;
+ hasState: (stateName?: string) => boolean;
+ getState: (stateName: string) => Partial;
+ // 属性更新前回调
+ onBeforeAttributeUpdate?: (
+ val: any,
+ attributes: Partial,
+ key: null | string | string[],
+ context?: ISetAttributeContext
+ ) => T | undefined;
+
+
+ readonly AABBBounds: IAABBBounds; // 用于获取当前节点的AABB包围盒
+ readonly OBBBounds: IOBBBounds; // 获取OBB包围盒,旋转防重叠需要用
+ readonly globalAABBBounds: IAABBBounds; // 全局AABB包围盒
+ readonly transMatrix: IMatrix; // 变换矩阵,动态计算
+ readonly globalTransMatrix: IMatrix; // 变换矩阵,动态计算
+
+ /**
+ * 是否包含某个点(点需要是全局坐标系)
+ */
+ containsPoint: (x: number, y: number, mode?: IContainPointMode, picker?: IPickerService) => boolean;
+ // 设置是2d模式还是3d模式
+ setMode: (mode: '3d' | '2d') => void;
+ // 是否合法
+ isValid: () => boolean;
+
+ // 基于当前transform的变换,字面意思,普通用户尽量别用,拿捏不住的~
+ translate: (x: number, y: number) => this;
+ translateTo: (x: number, y: number) => this;
+ scale: (scaleX: number, scaleY: number, scaleCenter?: IPointLike) => this;
+ scaleTo: (scaleX: number, scaleY: number) => this;
+ rotate: (angle: number, rotateCenter?: IPointLike) => this;
+ rotateTo: (angle: number) => this;
+ skewTo: (b: number, c: number) => this;
+ // 设置Tag,默认不用调用
+ addUpdateBoundTag: () => void;
+ addUpdateShapeAndBoundsTag: () => void;
+ addUpdateLayoutTag: () => void;
+ addUpdatePositionTag: () => void;
+ addUpdateGlobalPositionTag: () => void;
+ // 供render处理shape缓存tag
+ shouldUpdateShape: () => boolean;
+ clearUpdateShapeTag: () => void;
+ shouldUpdateAABBBounds: () => boolean;
+ shouldSelfChangeUpdateAABBBounds: () => boolean;
+ shouldUpdateGlobalMatrix: () => boolean;
+
+ // 设置属性
+ setAttributes: (params: Partial, forceUpdateTag?: boolean, context?: ISetAttributeContext) => void;
+
+ // 设置单个属性
+ setAttribute: (key: string, value: any, forceUpdateTag?: boolean, context?: ISetAttributeContext) => void;
+
+ // 添加影子节点
+ attachShadow: () => IShadowRoot;
+ // 卸载影子节点
+ detachShadow: () => void;
+
+ // 导出JSON配置
+ toJson: () => IGraphicJson;
+
+ /** 创建pathProxy */
+ createPathProxy: (path?: string) => void;
+ /** 将图形转换成CustomPath2D */
+ toCustomPath?: () => ICustomPath2D;
+
+ // 克隆对象
+ clone: () => IGraphic;
+ // 停止动画
+ stopAnimates: (stopChildren?: boolean) => void;
+ // 获取不用走动画的属性
+ getNoWorkAnimateAttr: () => Record;
+ // 获取主题
+ getGraphicTheme: () => T;
+}
+```
+
+这里涉及到的配置很多,但我们最常用的主要是
+- `attribute`:图元的属性
+- `setAttributes`:设置属性对象
+- `AABBBounds`:获取AABB包围盒
+- `transMatrix`:获取变换矩阵
+- `type`:图元类型
+
+### attribute
+图元属性有很多,具体可以参考[配置文档](/vrender/option/Arc),几乎图元的所有配置都在这个对象中。包括位置、颜色、样式等等。接下来我们来详细讲解一下这些通用的配置。其中特殊的配置还需要参考[配置文档](/vrender/option/Arc)中具体的图元类型
+
+#### 位置变换配置
+
+首先位置变换配置都是通用的:
+
+```ts
+type ITransform = {
+ x: number;
+ y: number;
+ z: number;
+ dx: number;
+ dy: number;
+ dz: number;
+ scrollX: number;
+ scrollY: number;
+ scaleX: number;
+ scaleY: number;
+ scaleZ: number;
+ angle: number; // 旋转角度
+ alpha: number; // x轴的转角
+ beta: number; // y轴的转角
+ scaleCenter: [number | string, number | string]; // 缩放中心
+ anchor: [number | string, number | string]; // 基于AABB的锚点位置,用于简单的定位某些path
+ anchor3d: [number | string, number | string, number] | [number | string, number | string]; // 3d的锚点位置
+ postMatrix: IMatrix; // 后处理矩阵,会在最后乘以之前的变换矩阵
+}
+```
+这些变换配置配置好之后,可以通过`transMatrix`获取到局部变换矩阵,通过`globalTransMatrix`获取到全局变换矩阵。
+
+#### 填充、描边、通用配置
+
+接下来是通用的图元配置,包括填充(`IFillStyle`)、描边(`IStrokeStyle`)、通用(`ICommonStyle`)配置。
+
+```ts
+// 渐变色配置
+interface IGradientStop {
+ offset: number;
+ color: string;
+}
+
+interface ILinearGradient {
+ gradient: 'linear';
+ x0?: number;
+ y0?: number;
+ x1?: number;
+ y1?: number;
+ stops: IGradientStop[];
+}
+
+interface IRadialGradient {
+ gradient: 'radial';
+ x0?: number;
+ y0?: number;
+ x1?: number;
+ y1?: number;
+ r0?: number;
+ r1?: number;
+ stops: IGradientStop[];
+}
+
+interface IConicalGradient {
+ gradient: 'conical';
+ startAngle?: number;
+ endAngle?: number;
+ x?: number;
+ y?: number;
+ stops: IGradientStop[];
+}
+```
+
+```ts
+type IColor = string | ILinearGradient | IRadialGradient | IConicalGradient;
+export type IFillType = boolean | string | IColor;
+
+type IFillStyle = {
+ fillOpacity: number;
+ /* 阴影 */
+ shadowBlur: number;
+ shadowColor: string;
+ shadowOffsetX: number;
+ shadowOffsetY: number;
+ // 填充色
+ fill: IFillType;
+};
+
+export type IBorderStyle = Omit & {
+ distance: number | string;
+ visible?: boolean;
+};
+
+type IStrokeStyle = {
+ outerBorder: Partial; // 外描边
+ innerBorder: Partial; // 内描边
+ strokeOpacity: number;
+ lineDash: number[];
+ lineDashOffset: number;
+ lineWidth: number;
+ lineCap: CanvasLineCap;
+ lineJoin: CanvasLineJoin;
+ miterLimit: number;
+ // 描边的boundsBuffer,用于控制bounds的buffer
+ strokeBoundsBuffer: number;
+ /**
+ * stroke - true 全描边
+ * stroke - false 不描边
+ * stroke 为数值类型,适用于rect\arc等图形,用于配置部分描边的场景,其中
+ *
+ * 0b00000 - 不描边
+ * 0b000001 - top
+ * 0b000010 - right
+ * 0b000100 - bottom
+ * 0b001000 - left
+ * 相应的:
+ * 0b000011 - top + right
+ * 0b000111 - top + right + bottom
+ * 0b001111 - 全描边
+ *
+ * stroke - boolean[],适用于rect\arc等图形,用于配置部分描边的场景
+ */
+ stroke: IStrokeType[] | IStrokeType;
+};
+```
+
+```ts
+type ICommonStyle = {
+ // 给stroke模式的pick额外加的buffer,用于外界控制stroke区域的pick范围
+ pickStrokeBuffer: number;
+ // 包围盒的padding
+ boundsPadding: number | number[];
+ /**
+ * 选择模式,精确模式,粗糙模式(包围盒模式),自定义模式
+ */
+ pickMode: 'accurate' | 'imprecise' | 'custom';
+ // 包围盒模式,精确模式,粗糙模式
+ boundsMode: 'accurate' | 'imprecise';
+ /**
+ * 是否支持事件拾取,默认为 true。
+ * @default true
+ */
+ pickable: boolean;
+ /**
+ * 是否支持fill拾取,默认为 true。
+ * @experimental
+ * @default true
+ */
+ fillPickable: boolean;
+ /**
+ * 是否支持stroke拾取,默认为 true。
+ * @experimental
+ * @default true
+ */
+ strokePickable: boolean;
+ /**
+ * 对于 group 节点,是否支持其子元素的事件拾取,默认为 true。
+ * 如果 group `pickable` 关闭,`childrenPickable` 开启,那么 group 的子节点仍参与事件拾取
+ * @default true
+ */
+ childrenPickable: boolean;
+
+ /**
+ * 元素是否可见。
+ * @default true
+ */
+ visible: boolean;
+ zIndex: number;
+ layout: any; // 布局配置(仅在启用布局插件时生效)
+ /**
+ * 是否隐藏元素(只是绘制的时候不绘制)
+ */
+ renderable: boolean;
+ /**
+ * 是否在3d中控制方向
+ * false: 不控制方向
+ * true: 始终控制方向朝摄像机
+ */
+ keepDirIn3d?: boolean;
+ // 影子节点的顺序
+ shadowRootIdx: number;
+ // 影子节点的拾取模式
+ shadowPickMode?: 'full' | 'graphic';
+ // 全局zIndex,会提到最上层,仅在开启交互层生效
+ globalZIndex: number;
+ // 功能和Canvas的globalCompositeOperation一致
+ globalCompositeOperation: CanvasRenderingContext2D['globalCompositeOperation'] | '';
+ // 完全支持滚动 | 完全不支持滚动 | 支持x方向的滚动 | 支持y方向的滚动,(仅在开启滚动时生效)
+ overflow: 'scroll' | 'hidden' | 'scroll-x' | 'scroll-y';
+ // 绘制fill和stroke的顺序,为0表示fill先绘制,1表示stroke先绘制
+ fillStrokeOrder: number;
+};
+```
+
+```javascript livedemo template=vrender
+// 注册所有需要的内容
+const stage = new VRender.Stage({
+ container: CONTAINER_ID,
+ autoRender: true
+});
+
+const rect = VRender.createRect({
+ x: 100,
+ y: 100,
+ width: 100,
+ height: 100,
+ fill:'red'
+});
+
+const text = VRender.createText({
+ x: 100,
+ y: 300,
+ text: '这是一个demo,随意修改',
+ fill: 'red'
+});
+
+stage.defaultLayer.add(rect);
+stage.defaultLayer.add(text);
+
+window['stage'] = stage;
+```
+
+## Group结构(Group)
+
+Group和普通的图元结构类似,在API层面仅仅多了几个方法:
+
+```ts
+interface IGroup extends IGraphic {
+ // 隐藏所有子节点
+ hideAll: () => void;
+ // 显示所有子节点
+ showAll: () => void;
+
+ // 增量渲染时添加子节点
+ incrementalAppendChild: (node: INode, highPerformance?: boolean) => INode | null;
+ // 增量渲染时清除子节点
+ incrementalClearChild: () => void;
+ // 创建或更新(如果存在)子节点
+ createOrUpdateChild: (
+ graphicName: string,
+ attributes: GraphicAttributeMap[T],
+ graphicType: T
+ ) => INode;
+}
+```
+
+### attribute
+Group的属性和普通图元的属性类似,具体可以参考[配置文档](/vrender/option/Group)。Group会多一些布局、裁剪相关的配置,大致特点如下。
+
+1. Group自身是具有形状且能够绘制的,Group默认是矩形,可以传入宽高来绘制。但是也可以传入path数组,传入的path数组就是Group的形状
+2. Group可以基于自身的形状进行裁剪,所有超过Group的形状的图元都会被裁剪。
+3. Group可以通过visible控制自身是否显示,通过visibleAll控制所有子节点是否显示。
+4. Group可以通过display控制子节点的布局方式,支持flex布局(但需要开启flex布局插件)。
+5. Group可以通过baseOpacity控制子节点的透明度。
+
+```ts
+type IGroupAttribute = {
+ path: IGraphic[]; // 自定义形状
+ width: number;
+ height: number;
+ cornerRadius: number | number[];
+ // 是否裁剪
+ clip: boolean;
+ visibleAll: boolean;
+ /* flex布局相关(需要开启flex布局插件) */
+ display?: 'relative' | 'inner-block' | 'flex';
+ flexDirection?: 'row' | 'row-reverse' | 'column' | 'column-reverse';
+ flexWrap?: 'nowrap' | 'wrap';
+ justifyContent?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around';
+ alignItems?: 'flex-start' | 'flex-end' | 'center';
+ alignContent?: 'flex-start' | 'center' | 'space-between' | 'space-around';
+ // 基准的透明度,用于控制group下面整体图元的透明度
+ baseOpacity?: number;
+};
+```
+
+```javascript livedemo template=vrender
+// 注册所有需要的内容
+const stage = new VRender.Stage({
+ container: CONTAINER_ID,
+ autoRender: true
+});
+
+const rect = VRender.createRect({
+ x: 100,
+ y: 100,
+ width: 100,
+ height: 100,
+ fill:'red'
+});
+
+const group = VRender.createGroup({
+ x: 100,
+ y: 100,
+ baseOpacity: 0.5
+});
+const text = VRender.createText({
+ x: 0,
+ y: 0,
+ text: 'group控制了所有子节点的透明度和位置',
+ fill: 'red'
+});
+group.add(rect);
+group.add(text);
+
+stage.defaultLayer.add(group);
+
+window['stage'] = stage;
+```
+
+## 组件
+所有组件都在`@visactor/vrender-component`包中,具体可以参考[组件文档](/vrender/component)。组件的使用和普通图元类似,且组件都继承自Group,可支持Group的配置,组件差异就只在于attribute中,具体参考组件文档。
diff --git a/docs/assets/guide/zh/asd/FAQ/What_Is_BoundsPadding.md b/docs/assets/guide/zh/asd/FAQ/What_Is_BoundsPadding.md
new file mode 100644
index 000000000..c79388bc9
--- /dev/null
+++ b/docs/assets/guide/zh/asd/FAQ/What_Is_BoundsPadding.md
@@ -0,0 +1,119 @@
+# 什么是BoundsPadding
+
+*【注意】*通常情况下,`VRender`默认并不具备类似`DOM`的布局,比如在`DOM`中,你可以写两个`div`,那么第二个`div`会放置在第一个div的下方。但如果在`VRender`中,你写了两个矩形,那么第二个矩形会覆盖在第一个矩形之上。这是因为`VRender`中所有的定位都是相对定位的,*它依靠你配置的x、y等参数定位*,坐标系是左上角为原点,向右为`x`轴正方向,向下为`y`轴正方向。这就导致了`VRender`的布局和`DOM`的布局是不一样的。
+
+所有图元都有自己的`AABBBounds`,如果觉得`AABBBounds`太小,需要扩大,就可以配置`BoundsPadding`。
+
+```ts
+// 包围盒的padding
+// 如果是number,那么四个方向的padding都是这个值
+// 如果是[number, number] ,那么分别是上下和左右的padding
+// 如果是[number, number, number, number],那么分别是上右下左的padding
+boundsPadding: number | number[];
+```
+
+
+
+## 特殊情况
+
+刚才说了,BoundsPadding是影响元素的AABBBounds,正常情况下并不影响位置,因为位置是固定通过xy等配置配出来的,并不是基于元素和元素之间的位置关系来计算的布局。
+
+但是!!,当你开启了`flex`布局插件,并在元素中设置了`display: flex`之后,该元素就会应用上flex布局。其子元素就会基于它的AABBBounds来进行布局。在这种情况下,BoundsPadding就会影响到子元素的位置。
+
+关于Flex布局的详细介绍,请参考[Flex布局](./Flex_Layout)章节。
+
+## 示例
+
+接下来我们展示两个实例,一个是没有开启flex布局的情况,一个是开启flex布局的情况。
+
+### 不使用flex布局,BoundsPadding不影响元素位置
+
+```javascript livedemo template=vrender
+const rect = VRender.createRect({ x: 100, y: 100, width: 100, height: 100, fill: 'red' });
+
+const stage = new VRender.Stage({
+ container: CONTAINER_ID,
+ autoRender: true
+});
+
+const textLimit = VRender.createText({
+ x: 0,
+ y: 0,
+ fill: 'pink',
+ text: '这是没有BoundsPadding的文字包围盒',
+ wordBreak: 'keep-all',
+ maxLineWidth: 120,
+ textAlign: 'left',
+ textBaseline: 'middle',
+ whiteSpace: 'normal',
+ _debug_bounds: true,
+ background: 'green'
+});
+const textLimit2 = VRender.createText({
+ x: 0,
+ y: 60,
+ fill: 'pink',
+ text: '这是有BoundsPadding的文字包围盒',
+ wordBreak: 'keep-all',
+ maxLineWidth: 120,
+ textAlign: 'left',
+ textBaseline: 'middle',
+ whiteSpace: 'normal',
+ boundsPadding: [20, 10],
+ _debug_bounds: true,
+ background: 'green'
+});
+
+const group = VRender.createGroup({x: 100, y: 100, width: 200, height: 200});
+group.add(textLimit);
+group.add(textLimit2);
+
+stage.defaultLayer.add(group);
+```
+
+
+### 使用flex布局,BoundsPadding影响元素位置
+
+```javascript livedemo template=vrender
+const rect = VRender.createRect({ x: 100, y: 100, width: 100, height: 100, fill: 'red' });
+
+const stage = new VRender.Stage({
+ container: CONTAINER_ID,
+ autoRender: true,
+ enableLayout: true
+});
+
+const textLimit = VRender.createText({
+ x: 0,
+ y: 0,
+ fill: 'pink',
+ text: '这是没有BoundsPadding的文字包围盒',
+ wordBreak: 'keep-all',
+ maxLineWidth: 120,
+ textAlign: 'left',
+ textBaseline: 'middle',
+ whiteSpace: 'normal',
+ _debug_bounds: true,
+ background: 'green'
+});
+const textLimit2 = VRender.createText({
+ x: 0,
+ y: 60,
+ fill: 'pink',
+ text: '这是有BoundsPadding的文字包围盒',
+ wordBreak: 'keep-all',
+ maxLineWidth: 120,
+ textAlign: 'left',
+ textBaseline: 'middle',
+ whiteSpace: 'normal',
+ boundsPadding: [20, 10],
+ _debug_bounds: true,
+ background: 'green'
+});
+
+const group = VRender.createGroup({x: 100, y: 100, width: 200, height: 200, display: 'flex'});
+group.add(textLimit);
+group.add(textLimit2);
+
+stage.defaultLayer.add(group);
+```
diff --git a/docs/assets/guide/zh/asd/Getting_Started.md b/docs/assets/guide/zh/asd/Getting_Started.md
index 327f53f25..7afb0a681 100644
--- a/docs/assets/guide/zh/asd/Getting_Started.md
+++ b/docs/assets/guide/zh/asd/Getting_Started.md
@@ -104,4 +104,4 @@ circle.addEventListener('click', () => {
stage.defaultLayer.add(circle);
```
-希望这篇教程对你学习如何使用 VChart 有所帮助。现在,你可以尝试绘制不同类型的图表,并通过深入了解 VChart 的各种配置选项,定制出更加丰富多样的图表效果。勇敢开始你的 VChart 之旅吧!
\ No newline at end of file
+希望这篇教程对你学习如何使用 VRender 有所帮助。现在,你可以尝试绘制不同类型的图元,并通过深入了解 VRender 的各种配置选项,定制出更加丰富多样的绘图效果。勇敢开始你的 VRender 之旅吧!
diff --git a/docs/assets/guide/zh/asd/VRender_Components/VRender_Components_Introduction.md b/docs/assets/guide/zh/asd/VRender_Components/VRender_Components_Introduction.md
new file mode 100644
index 000000000..360d75886
--- /dev/null
+++ b/docs/assets/guide/zh/asd/VRender_Components/VRender_Components_Introduction.md
@@ -0,0 +1,207 @@
+# 组件
+
+组件是特殊的图元,也是特殊的Group。VRender提供了一系列内置的组件,比如`datazoom`、`axes`、`label`、`legend`、`poptip`等,这些组件可以帮助我们快速实现一些常见的交互效果。外部也可以通过继承`@visactor/vrender-components`中的`AbstractComponent`来实现自定义组件。
+
+
+
+## 介绍
+
+组件是基于`Group`图元实现的,所以组件本身是一个特殊的Group图元,所有Group可以支持的配置都可以配置给组件中,但当然,一般情况下组件会抽象出一套专门的配置,用于定义该组件的特殊配置,组件内部既包含其他图元,也包含组件的逻辑,`axes`组件中包含了`line`图元、`text`图元、`path`图元等。不同类型的`axes`组件有不同的样式,同时`axes`组件中的标签实现了基于各种策略的防重叠逻辑。
+
+## 使用组件
+
+因为组件是基于`Group`图元实现的,所以在使用的时候,直接将其作为一个Group使用即可,只是这个Group内部自己会管理自己的子元素,有自己的状态。
+
+如下为使用一个`axes`组件的例子:
+```ts
+const lineAxis = new LineAxis({
+ x: 68,
+ y: 30,
+ start: {
+ x: 0,
+ y: 0
+ },
+ end: {
+ x: 0,
+ y: 400
+ },
+ pickable: true,
+ visible: true,
+ orient: 'left',
+ line: {
+ visible: false
+ },
+ label: {
+ visible: true,
+ inside: false,
+ space: 12,
+ autoLimit: true,
+ style: {
+ fontSize: 12,
+ fill: '#89909d',
+ fontWeight: 'normal',
+ fillOpacity: 1
+ },
+ formatMethod: null
+ },
+ tick: {
+ visible: false
+ },
+ subTick: {
+ visible: false
+ },
+ title: {
+ visible: false,
+ text: 'visits',
+ maxWidth: null
+ },
+ panel: {
+ visible: false
+ },
+ verticalFactor: 1,
+ items: [
+ [
+ {
+ id: 0,
+ label: 0,
+ value: 1,
+ rawValue: 0
+ },
+ {
+ id: 500,
+ label: 500,
+ value: 0.780952380952381,
+ rawValue: 500
+ },
+ {
+ id: 1000,
+ label: 1000,
+ value: 0.5619047619047619,
+ rawValue: 1000
+ },
+ {
+ id: 1500,
+ label: 1500,
+ value: 0.3428571428571428,
+ rawValue: 1500
+ },
+ {
+ id: 2000,
+ label: 2000,
+ value: 0.12380952380952383,
+ rawValue: 2000
+ },
+ {
+ id: 24000,
+ label: 24000,
+ value: -0.0042307692307692315,
+ rawValue: 24000
+ }
+ ]
+ ],
+});
+
+const stage = createStage({
+ canvas: 'main',
+ autoRender: true
+});
+
+stage.defaultLayer.add(lineAxis);
+window['stage'] = stage;
+```
+
+## 自定义组件
+
+自定义组件比自定义图元更简单,不需要自定义render和pick,因为其使用的图元都是已有的图元,我们只需要在组件内实现自己的逻辑即可,就和`react`类似。
+
+1. 继承`AbstractComponent`
+2. 定义自身的`attribute`,因为组件往往包含不同子元素模块,比如轴有轴标签、轴tick、轴线等,所以可以定义一个attribute接口,用于定义组件的属性。
+3. 重载`render`函数,每次有效的属性更新都会调用`render`函数,在`render`函数里去创建和管理子元素。
+
+一个简单的例子如下,比如我们要实现一个组件,它是一个装着圆形的盒子,传入圆形的数量,他就能画这个数量的圆形:
+```ts
+import { AbstractComponent } from '@visactor/vrender-components';
+class CircleBox extends AbstractComponent> {
+ name = 'circleBox';
+
+ static defaultAttributes: Partial = {
+ circleStyle: {
+ fill: 'red',
+ },
+ circleCount: 10,
+ width: 300,
+ height: 300
+ };
+
+ constructor(attributes: PopTipAttributes, options?: ComponentOptions) {
+ super(options?.skipDefault ? attributes : merge({}, CircleBox.defaultAttributes, attributes));
+ }
+
+ // 每次合法属性更新都会调用
+ protected render() {
+ const { circleCount, circleStyle, width, height } = this.attribute;
+
+ const minWH = Math.min(width, height);
+ const count = Math.ceil(Math.sqrt(circleCount));
+ const radius = Math.floor(minWH) / count / 2;
+ for (let i = 0; i < circleCount; i++) {
+ // 计算子元素的位置
+ const x = (i % count) * radius * 2 + radius;
+ const y = Math.floor(i / count) * radius * 2 + radius;
+ // 添加或创建子元素
+ const circle = this.createOrUpdateChild(`circle-${i}`, { ...circleStyle, radius, x, y }, 'circle');
+ }
+ }
+}
+```
+
+自定义完成后,就可以像使用其他组件一样使用了。
+
+```javascript livedemo template=vrender
+VRenderComponent.loadPoptip();
+
+class CircleBox extends VRenderComponent.AbstractComponent {
+ name = 'circleBox';
+
+ static defaultAttributes = {
+ circleStyle: {
+ fill: 'red',
+ },
+ stroke: 'blue',
+ circleCount: 10,
+ width: 300,
+ height: 300
+ };
+
+ constructor(attributes, options) {
+ super(options?.skipDefault ? attributes : ({...CircleBox.defaultAttributes, ...attributes}));
+ }
+
+ // 每次合法属性更新都会调用
+ render() {
+ const { circleCount, circleStyle, width, height } = this.attribute;
+
+ const minWH = Math.min(width, height);
+ const count = Math.ceil(Math.sqrt(circleCount));
+ const radius = Math.floor(minWH) / count / 2;
+ for (let i = 0; i < circleCount; i++) {
+ // 计算子元素的位置
+ const x = (i % count) * radius * 2 + radius;
+ const y = Math.floor(i / count) * radius * 2 + radius;
+ // 添加或创建子元素
+ const circle = this.createOrUpdateChild(`circle-${i}`, { ...circleStyle, radius, x, y }, 'circle');
+ }
+ }
+}
+
+const textLimit = new CircleBox({x: 10, y: 10, circleCount: 100});
+const stage = VRender.createStage({
+ container: CONTAINER_ID,
+ autoRender: true,
+ pluginList: ['poptipForText'] // 启用poptipForText插件
+});
+
+stage.defaultLayer.add(textLimit);
+
+window['stage'] = stage;
+```
diff --git a/docs/assets/guide/zh/asd/VRender_Components/VRender_Components_PopTip.md b/docs/assets/guide/zh/asd/VRender_Components/VRender_Components_PopTip.md
new file mode 100644
index 000000000..0635a8ae3
--- /dev/null
+++ b/docs/assets/guide/zh/asd/VRender_Components/VRender_Components_PopTip.md
@@ -0,0 +1,118 @@
+# PopTip组件
+
+PopTip组件是VRender提供的一个弹出框组件,和常见的PopTip组件类似,用于在用户交互时展示一些额外信息。PopTip组件支持多种交互方式,PopTip组件可以自定义样式和内容,支持多种布局方式。
+
+
+
+## 介绍
+
+PopTip组件由以下几个部分构成:
+- `title`:PopTip组件的标题。
+- `content`:PopTip组件的内容。
+- `panel`:PopTip组件面板。
+
+
+
+在配置层面,可配置PopTip的宽高,以及标题样式、内容样式均可配置,以及标题与内容的间距(space)、padding等布局属性也可以配置
+
+对应的配置接口如下:
+```ts
+type PopTipAttributes = {
+ /** 位置,参考arco design */
+ position?: 'auto' | 'top' | 'tl' | 'tr' | 'bottom' | 'bl' | 'br' | 'left' | 'lt' | 'lb' | 'right' | 'rt' | 'rb';
+ /**
+ * 标题内容,如果需要进行换行,则使用数组形式,如 ['abc', '123']
+ */
+ title?: string | string[] | number | number[];
+ /** 标题样式 */
+ titleStyle?: Partial;
+ titleFormatMethod?: (t: string | string[] | number | number[]) => string | string[] | number | number[];
+ /**
+ * 内容文本,如果需要进行换行,则使用数组形式,如 ['abc', '123']
+ */
+ content?: string | string[] | number | number[];
+ /** 内容文本样式 */
+ contentStyle?: Partial;
+ // 内容格式化函数
+ contentFormatMethod?: (t: string | string[] | number | number[]) => string | string[] | number | number[];
+ /**
+ * 标题与内容的间距
+ */
+ space?: number;
+ /**
+ * 内部边距
+ */
+ padding?: Padding;
+ /**
+ * 标签的背景面板配置, TODO: 支持symbol形状
+ */
+ panel?: BackgroundAttributes & ISymbolGraphicAttribute & { space?: number };
+
+ /**
+ * 最小宽度,像素值
+ * @default 30
+ */
+ minWidth?: number;
+ /**
+ * 最大宽度,像素值。当文字超过最大宽度时,会自动省略。
+ */
+ maxWidth?: number;
+
+ // 最大宽度比例
+ maxWidthPercent?: number;
+
+ visible?: boolean;
+ visibleFunc?: (graphic: IGraphic) => boolean;
+ state?: StateStyle;
+ dx?: number;
+ dy?: number;
+} & Omit
+```
+
+如图所示`PopTip`具有多种定位方式,你可以通过`position`属性来配置。同时也有一个`auto`的定位方式,它会根据PopTip的位置自动调整定位方式,避免PopTip被遮挡,尝试的位置顺序为`['top', 'tl', 'tr', 'bottom', 'bl', 'br', 'left', 'lt', 'lb', 'right', 'rt', 'rb']`。
+
+
+
+`PopTip`会被内容默认撑开,可以通过配置`minWidth`和`maxWidth`设置其最大最小宽度,同时,如果不知道具体配置多大的像素,可以使用`maxWidthPercent`配置按照画面比例确定一个最大的宽度百分比。
+
+## 使用
+
+PopTip可以作为一般的组件使用,和普通的组件一样,可以通过`xxx.add`方法添加到场景树中,这种方法不具体介绍了。
+
+同时我们还提供了插件(`poptipForText`)的形式进行使用。该插件会自动的识别画布中被截断的文字,当鼠标hover到该文字上的时候,会自动的展示PopTip去显示文字的完整内容。
+1. 我们可以通过在该图元上的`attribute.poptip`属性进行展示的PopTip的样式配置,`attribute.poptip`属性的类型和PopTip组件接收的参数一致。而PopTip会自动填充上content的内容,用于展示文字的全部内容
+2. 通过创建的时候给`stage`的`pluginList`属性添加`poptipForText`,就可以启用这个插件
+
+```javascript livedemo template=vrender
+VRenderComponent.loadPoptip();
+
+const textLimit = VRender.createText({
+ x: 100,
+ y: 100,
+ fill: 'black',
+ text: 'this is textaaaaaaaaaaaaaaaaa aaa this isisisisisis abc',
+ wordBreak: 'keep-all',
+ maxLineWidth: 100,
+ stroke: 'green',
+ textAlign: 'left',
+ textBaseline: 'middle',
+ whiteSpace: 'normal',
+ // 这里可配置PopTipAttributes属性
+ poptip: {
+ panel: {
+ fill: 'pink'
+ }
+ }
+});
+const stage = VRender.createStage({
+ container: CONTAINER_ID,
+ autoRender: true,
+ pluginList: ['poptipForText'] // 启用poptipForText插件
+});
+
+stage.defaultLayer.add(textLimit);
+
+window['stage'] = stage;
+```
+
+这种使用场景中,我们的样式如果都在`attribute.poptip`中配置的话,会不太方便。所以PopTip插件提供了主题的配置,我们可以通过从`@visactor/vrender-components`包中导入`setPoptipTheme`属性来配置主题,主题的属性配置和PopTip组件的属性配置接口是一致的。插件会优先使用图元上定义的`poptip`属性,如果没有定义,则会使用主题配置,最后才会使用组件自身的配置。
diff --git a/docs/assets/guide/zh/asd/VRender_Website_Guide.md b/docs/assets/guide/zh/asd/VRender_Website_Guide.md
index c4c04f114..989d831b3 100644
--- a/docs/assets/guide/zh/asd/VRender_Website_Guide.md
+++ b/docs/assets/guide/zh/asd/VRender_Website_Guide.md
@@ -10,7 +10,7 @@
### 了解 VRender
-在你完成[快速上手](./Getting_Started)后,你可以通过学习[图元与交互](./Your_First_Chart)章节来深入了解支持的各种图元与交互,然后学习[动画]章节来了解如何创建一个动画,然后学习[扩展和插件]()章节来了解如何通过扩展和插件增强应用程序的功能
+在你完成[快速上手](./Getting_Started)后,你可以通过学习[基础指引](./Basic/Vrender_basic_tutorial)章节来深入了解支持的各种图元与交互,然后学习[动画]章节来了解如何创建一个动画,然后学习[扩展和插件]()章节来了解如何通过扩展和插件增强应用程序的功能
## 文档
@@ -45,7 +45,3 @@ VRender 的文档提供了有关功能和配置的详细信息。根据你的需
2. 提交一个 issue:指出文档中存在的问题团队成员会尽快核实并修正。
感谢你的帮助!我们将不断完善文档,为所有 VRender 用户提供更好的学习体验。
-
-# 子页面目录
-
-暂时无法在飞书文档外展示此内容
diff --git a/docs/demos/package.json b/docs/demos/package.json
index 825c7b6e0..d7e382216 100644
--- a/docs/demos/package.json
+++ b/docs/demos/package.json
@@ -12,7 +12,7 @@
"@internal/eslint-config": "workspace:*",
"@internal/ts-config": "workspace:*",
"@visactor/vrender-kits": "workspace:0.14.8",
- "@visactor/vutils": "~0.19.1",
+ "@visactor/vutils": "~0.19.2",
"d3-scale-chromatic": "^3.0.0",
"lodash": "4.17.21",
"dat.gui": "^0.7.9",
diff --git a/docs/menu.json b/docs/menu.json
index a114391b5..ee16bda7e 100644
--- a/docs/menu.json
+++ b/docs/menu.json
@@ -11,5 +11,10 @@
"menu": "option",
"type": "markdown-template",
"entry": "option"
+ },
+ {
+ "menu": "contributing",
+ "type": "markdown",
+ "entry": "contributing"
}
]
diff --git a/docs/package.json b/docs/package.json
index d057177d5..fa7473622 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -11,9 +11,9 @@
"dependencies": {
"@arco-design/web-react": "2.46.1",
"@visactor/vchart": "1.3.0",
- "@visactor/vutils": "~0.19.1",
+ "@visactor/vutils": "~0.19.2",
"@visactor/vgrammar": "~0.5.7",
- "@visactor/vrender": "workspace:0.21.0",
+ "@visactor/vrender": "workspace:0.21.1",
"markdown-it": "^13.0.0",
"highlight.js": "^11.8.0",
"axios": "^1.4.0",
diff --git a/packages/react-vrender-utils/CHANGELOG.json b/packages/react-vrender-utils/CHANGELOG.json
index acdb97019..a64b11760 100644
--- a/packages/react-vrender-utils/CHANGELOG.json
+++ b/packages/react-vrender-utils/CHANGELOG.json
@@ -1,6 +1,12 @@
{
"name": "@visactor/react-vrender-utils",
"entries": [
+ {
+ "version": "0.21.1",
+ "tag": "@visactor/react-vrender-utils_v0.21.1",
+ "date": "Thu, 05 Dec 2024 07:50:46 GMT",
+ "comments": {}
+ },
{
"version": "0.21.0",
"tag": "@visactor/react-vrender-utils_v0.21.0",
diff --git a/packages/react-vrender-utils/CHANGELOG.md b/packages/react-vrender-utils/CHANGELOG.md
index bda1d8757..05ea94f90 100644
--- a/packages/react-vrender-utils/CHANGELOG.md
+++ b/packages/react-vrender-utils/CHANGELOG.md
@@ -1,6 +1,11 @@
# Change Log - @visactor/react-vrender-utils
-This log was last generated on Thu, 28 Nov 2024 03:30:36 GMT and should not be manually modified.
+This log was last generated on Thu, 05 Dec 2024 07:50:46 GMT and should not be manually modified.
+
+## 0.21.1
+Thu, 05 Dec 2024 07:50:46 GMT
+
+_Version update only_
## 0.21.0
Thu, 28 Nov 2024 03:30:36 GMT
diff --git a/packages/react-vrender-utils/package.json b/packages/react-vrender-utils/package.json
index 3a3eac256..bd36b627f 100644
--- a/packages/react-vrender-utils/package.json
+++ b/packages/react-vrender-utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@visactor/react-vrender-utils",
- "version": "0.21.0",
+ "version": "0.21.1",
"description": "",
"sideEffects": false,
"main": "cjs/index.js",
@@ -24,9 +24,9 @@
"react-dom": "^18.2.0"
},
"dependencies": {
- "@visactor/vrender": "workspace:0.21.0",
- "@visactor/react-vrender": "workspace:0.21.0",
- "@visactor/vutils": "~0.19.1",
+ "@visactor/vrender": "workspace:0.21.1",
+ "@visactor/react-vrender": "workspace:0.21.1",
+ "@visactor/vutils": "~0.19.2",
"react-reconciler": "^0.29.0",
"tslib": "^2.3.1"
},
diff --git a/packages/react-vrender/CHANGELOG.json b/packages/react-vrender/CHANGELOG.json
index 20fcca5b2..68bf47c02 100644
--- a/packages/react-vrender/CHANGELOG.json
+++ b/packages/react-vrender/CHANGELOG.json
@@ -1,6 +1,12 @@
{
"name": "@visactor/react-vrender",
"entries": [
+ {
+ "version": "0.21.1",
+ "tag": "@visactor/react-vrender_v0.21.1",
+ "date": "Thu, 05 Dec 2024 07:50:46 GMT",
+ "comments": {}
+ },
{
"version": "0.21.0",
"tag": "@visactor/react-vrender_v0.21.0",
diff --git a/packages/react-vrender/CHANGELOG.md b/packages/react-vrender/CHANGELOG.md
index 985ab23c8..750bfc604 100644
--- a/packages/react-vrender/CHANGELOG.md
+++ b/packages/react-vrender/CHANGELOG.md
@@ -1,6 +1,11 @@
# Change Log - @visactor/react-vrender
-This log was last generated on Thu, 28 Nov 2024 03:30:36 GMT and should not be manually modified.
+This log was last generated on Thu, 05 Dec 2024 07:50:46 GMT and should not be manually modified.
+
+## 0.21.1
+Thu, 05 Dec 2024 07:50:46 GMT
+
+_Version update only_
## 0.21.0
Thu, 28 Nov 2024 03:30:36 GMT
diff --git a/packages/react-vrender/package.json b/packages/react-vrender/package.json
index e83271863..8be3ed039 100644
--- a/packages/react-vrender/package.json
+++ b/packages/react-vrender/package.json
@@ -1,6 +1,6 @@
{
"name": "@visactor/react-vrender",
- "version": "0.21.0",
+ "version": "0.21.1",
"description": "",
"sideEffects": false,
"main": "cjs/index.js",
@@ -23,8 +23,8 @@
"react": "^18.2.0"
},
"dependencies": {
- "@visactor/vrender": "workspace:0.21.0",
- "@visactor/vutils": "~0.19.1",
+ "@visactor/vrender": "workspace:0.21.1",
+ "@visactor/vutils": "~0.19.2",
"react-reconciler": "^0.29.0",
"tslib": "^2.3.1"
},
diff --git a/packages/vrender-components/CHANGELOG.json b/packages/vrender-components/CHANGELOG.json
index 00159ce97..658e5a182 100644
--- a/packages/vrender-components/CHANGELOG.json
+++ b/packages/vrender-components/CHANGELOG.json
@@ -1,6 +1,27 @@
{
"name": "@visactor/vrender-components",
"entries": [
+ {
+ "version": "0.21.1",
+ "tag": "@visactor/vrender-components_v0.21.1",
+ "date": "Thu, 05 Dec 2024 07:50:47 GMT",
+ "comments": {
+ "none": [
+ {
+ "comment": "feat: support `restorePosition` in position/bound label overlap strategy"
+ },
+ {
+ "comment": "feat: support vertex point of marker area label. close @VisActor/VChart#3442"
+ },
+ {
+ "comment": "fix: end symbol angle when arc line in markpoint. fix @VisActor/VChart#3427"
+ },
+ {
+ "comment": "fix: fix issue with scroll-plugin"
+ }
+ ]
+ }
+ },
{
"version": "0.21.0",
"tag": "@visactor/vrender-components_v0.21.0",
diff --git a/packages/vrender-components/CHANGELOG.md b/packages/vrender-components/CHANGELOG.md
index f45bf33f0..6734fc366 100644
--- a/packages/vrender-components/CHANGELOG.md
+++ b/packages/vrender-components/CHANGELOG.md
@@ -1,6 +1,16 @@
# Change Log - @visactor/vrender-components
-This log was last generated on Thu, 28 Nov 2024 03:30:36 GMT and should not be manually modified.
+This log was last generated on Thu, 05 Dec 2024 07:50:47 GMT and should not be manually modified.
+
+## 0.21.1
+Thu, 05 Dec 2024 07:50:47 GMT
+
+### Updates
+
+- feat: support `restorePosition` in position/bound label overlap strategy
+- feat: support vertex point of marker area label. close @VisActor/VChart#3442
+- fix: end symbol angle when arc line in markpoint. fix @VisActor/VChart#3427
+- fix: fix issue with scroll-plugin
## 0.21.0
Thu, 28 Nov 2024 03:30:36 GMT
diff --git a/packages/vrender-components/__tests__/browser/examples/scrollbar.ts b/packages/vrender-components/__tests__/browser/examples/scrollbar.ts
index adeca7333..04b0d6fc9 100644
--- a/packages/vrender-components/__tests__/browser/examples/scrollbar.ts
+++ b/packages/vrender-components/__tests__/browser/examples/scrollbar.ts
@@ -44,7 +44,7 @@ export function run() {
x: 100,
y: 100,
fill: 'red',
- clip: true,
+ clip: false,
overflow: 'scroll'
});
@@ -73,7 +73,8 @@ export function run() {
}
}
- const stage = render([hScrollBar, vScrollBar, group], 'main');
+ const stage = render([group], 'main');
+ window.stage = stage;
hScrollBar.addEventListener('mouseenter', e => {
console.log('abc');
hScrollBar.setAttributes({ visible: true });
diff --git a/packages/vrender-components/__tests__/util/render.ts b/packages/vrender-components/__tests__/util/render.ts
index 787fbc9b3..22fa3d04c 100644
--- a/packages/vrender-components/__tests__/util/render.ts
+++ b/packages/vrender-components/__tests__/util/render.ts
@@ -4,8 +4,11 @@ import { Group, Line, Text, createStage, Symbol, Rect, Path, Arc, Area, Circle,
import { array } from '@visactor/vutils';
import { initBrowserEnv } from '@visactor/vrender-kits';
+import { loadScrollbar } from '../../src';
initBrowserEnv();
+loadScrollbar();
+
export default function render(component: IGraphic | IGraphic[], canvasId: string, option?: Partial) {
// 创建舞台实例
const stage = createRenderer(canvasId, option);
@@ -26,7 +29,7 @@ export function createRenderer(canvasId: string, option: Partial =
width: 600,
height: 600,
autoRender: true,
- disableDirtyBounds: true,
+ disableDirtyBounds: false,
// canvasControled: false,
background: 'rgba(238,238,238,0.5)',
viewBox: {
diff --git a/packages/vrender-components/package.json b/packages/vrender-components/package.json
index 9b4accb29..dcaa29858 100644
--- a/packages/vrender-components/package.json
+++ b/packages/vrender-components/package.json
@@ -1,6 +1,6 @@
{
"name": "@visactor/vrender-components",
- "version": "0.21.0",
+ "version": "0.21.1",
"description": "components library for dp visualization",
"sideEffects": false,
"main": "cjs/index.js",
@@ -25,17 +25,17 @@
"build:spec-types": "rm -rf ./spec-types && tsc -p ./tsconfig.spec.json --declaration --emitDeclarationOnly --outDir ./spec-types"
},
"dependencies": {
- "@visactor/vutils": "~0.19.1",
- "@visactor/vscale": "~0.19.1",
- "@visactor/vrender-core": "workspace:0.21.0",
- "@visactor/vrender-kits": "workspace:0.21.0"
+ "@visactor/vutils": "~0.19.2",
+ "@visactor/vscale": "~0.19.2",
+ "@visactor/vrender-core": "workspace:0.21.1",
+ "@visactor/vrender-kits": "workspace:0.21.1"
},
"devDependencies": {
"@internal/bundler": "workspace:*",
"@internal/eslint-config": "workspace:*",
"@internal/ts-config": "workspace:*",
"@rushstack/eslint-patch": "~1.1.4",
- "@visactor/vscale": "~0.19.1",
+ "@visactor/vscale": "~0.19.2",
"@types/jest": "^26.0.0",
"jest": "^26.0.0",
"jest-electron": "^0.1.12",
diff --git a/packages/vrender-components/src/label/base.ts b/packages/vrender-components/src/label/base.ts
index 08ed969f4..a98b0d50a 100644
--- a/packages/vrender-components/src/label/base.ts
+++ b/packages/vrender-components/src/label/base.ts
@@ -550,9 +550,15 @@ export class LabelBase extends AbstractComponent {
const text = result[i];
const bounds = text.AABBBounds;
const range = boundToRange(bmpTool, bounds, true);
- if (canPlace(bmpTool, bitmap, bounds, clampForce, text._isClamped ? 0 : overlapPadding)) {
+ if (canPlace(bmpTool, bitmap, bounds, clampForce, overlapPadding)) {
bitmap.setRange(range);
} else {
+ if (clampForce) {
+ const placedAfterClampForce = this._processClampForce(text as IText, bmpTool, bitmap);
+ if (placedAfterClampForce) {
+ continue;
+ }
+ }
if (hideOnHit) {
text.setAttributes({ visible: false });
} else {
@@ -563,6 +569,34 @@ export class LabelBase extends AbstractComponent {
return result;
}
+ protected _processClampForce(text: IText, bmpTool: BitmapTool, bitmap: Bitmap) {
+ const { dy = 0, dx = 0 } = clampText(text as IText, bmpTool.width, bmpTool.height, bmpTool.padding);
+ if (dx === 0 && dy === 0) {
+ if (canPlace(bmpTool, bitmap, text.AABBBounds)) {
+ // xy方向偏移都为0,意味着不考虑 overlapPadding 时,实际上可以放得下
+ bitmap.setRange(boundToRange(bmpTool, text.AABBBounds, true));
+ return true;
+ }
+ } else if (
+ canPlace(
+ bmpTool,
+ bitmap,
+ {
+ x1: text.AABBBounds.x1 + dx,
+ x2: text.AABBBounds.x2 + dx,
+ y1: text.AABBBounds.y1 + dy,
+ y2: text.AABBBounds.y2 + dy
+ }
+ // 向内 clamp 只处理超出的位移量,不叠加 overlapPadding
+ )
+ ) {
+ text.setAttributes({ x: text.attribute.x + dx, y: text.attribute.y + dy });
+ bitmap.setRange(boundToRange(bmpTool, text.AABBBounds, true));
+ return true;
+ }
+ return false;
+ }
+
protected _overlapByStrategy(
labels: (IText | IRichText)[],
option: OverlapAttrs,
@@ -655,30 +689,8 @@ export class LabelBase extends AbstractComponent {
// 尝试向内挤压
if (!hasPlace && clampForce) {
- // 向内挤压不考虑 overlapPadding
- const { dx = 0, dy = 0 } = clampText(text as IText, bmpTool.width, bmpTool.height, bmpTool.padding);
- if (dx === 0 && dy === 0) {
- if (canPlace(bmpTool, bitmap, text.AABBBounds)) {
- // xy方向偏移都为0,意味着不考虑 overlapPadding 时,实际上可以放得下
- bitmap.setRange(boundToRange(bmpTool, text.AABBBounds, true));
- result.push(text);
- continue;
- }
- } else if (
- canPlace(
- bmpTool,
- bitmap,
- {
- x1: text.AABBBounds.x1 + dx,
- x2: text.AABBBounds.x2 + dx,
- y1: text.AABBBounds.y1 + dy,
- y2: text.AABBBounds.y2 + dy
- }
- // 向内 clamp 只处理超出的位移量,不叠加 overlapPadding
- )
- ) {
- text.setAttributes({ x: text.attribute.x + dx, y: text.attribute.y + dy });
- bitmap.setRange(boundToRange(bmpTool, text.AABBBounds, true));
+ const placedAfterClampForce = this._processClampForce(text as IText, bmpTool, bitmap);
+ if (placedAfterClampForce) {
result.push(text);
continue;
}
diff --git a/packages/vrender-components/src/label/overlap/place.ts b/packages/vrender-components/src/label/overlap/place.ts
index 14d8e4439..c8b1e2bb9 100644
--- a/packages/vrender-components/src/label/overlap/place.ts
+++ b/packages/vrender-components/src/label/overlap/place.ts
@@ -84,16 +84,21 @@ export function placeToCandidates(
text: Text,
candidates: PointLocationCfg[] = [],
clampForce = true,
- pad = 0
+ pad = 0,
+ changePosition = false
): PointLocationCfg | false {
const validCandidates = candidates.filter(candidate => isValid(candidate));
for (let i = 0; i < validCandidates.length; i++) {
- const tempText = text.clone();
- tempText.setAttributes(validCandidates[i]);
- tempText.update();
+ let measureText;
+ if (changePosition) {
+ measureText = text;
+ } else {
+ measureText = text.clone();
+ }
+ measureText.setAttributes(validCandidates[i]);
- if (canPlace($, bitmap, tempText.AABBBounds, clampForce, pad)) {
- bitmap.setRange(boundToRange($, tempText.AABBBounds, true));
+ if (canPlace($, bitmap, measureText.AABBBounds, clampForce, pad)) {
+ bitmap.setRange(boundToRange($, measureText.AABBBounds, true));
return validCandidates[i];
}
}
@@ -113,11 +118,11 @@ export function place(
const overlapPadding = (attrs.overlap as OverlapAttrs)?.overlapPadding;
if (s.type === 'bound' || s.type === 'position') {
if (isFunction(labeling)) {
- // TODO:这里可以 filter 掉初始位置,提升一部分性能
const userPosition = isFunction(s.position) ? s.position(text.attribute) : s.position;
const positions = (userPosition || defaultLabelPosition(attrs.type)) as string[];
const candidates = positions.map(p => labeling(text.AABBBounds, bounds, p, attrs.offset) as PointLocationCfg);
- return placeToCandidates($, bitmap, text, candidates, clampForce, overlapPadding);
+ const shouldClone = s.restorePosition === false;
+ return placeToCandidates($, bitmap, text, candidates, clampForce, overlapPadding, shouldClone);
}
return false;
}
diff --git a/packages/vrender-components/src/label/overlap/shiftY.ts b/packages/vrender-components/src/label/overlap/shiftY.ts
index 5559daf0c..b4cbfb9b7 100644
--- a/packages/vrender-components/src/label/overlap/shiftY.ts
+++ b/packages/vrender-components/src/label/overlap/shiftY.ts
@@ -103,17 +103,17 @@ export function shiftY(texts: IText[], option: IShiftYOption) {
};
function adjustPositionInOneGroup(texts: IText[]) {
- if (texts.length === 1) {
- return;
- }
// 从最后一个 text 向前遍历,如果与前一个 text 相交,则尝试放到下方(需要判断和前一个 text 是否相交,若相交则不能放到下方)
- for (let i = texts.length - 1; i > 0; i--) {
+ for (let i = texts.length - 1; i >= 0; i--) {
const curText = texts[i];
const upperText = texts[i - 1];
const lowerText = texts[i + 1];
-
// 当前 text 和上面一个 text 相交
- if (isIntersect(getY1(upperText) + getHeight(upperText), getY1(curText))) {
+ if (
+ (upperText && isIntersect(getY1(upperText) + getHeight(upperText), getY1(curText))) ||
+ // 如果是最顶上被 clamp 进来的 text,也尝试向下摆放
+ (getY1(curText) === 0 && curText._isClamped)
+ ) {
const { y } = labelling(curText);
// 挪动当前 text 后, 和下面一个 text 不相交
if (!lowerText || !isIntersect(y + getHeight(curText) / 2, getY1(lowerText))) {
diff --git a/packages/vrender-components/src/label/type.ts b/packages/vrender-components/src/label/type.ts
index 53006cf99..f8f316e4b 100644
--- a/packages/vrender-components/src/label/type.ts
+++ b/packages/vrender-components/src/label/type.ts
@@ -288,6 +288,13 @@ export type PositionStrategy = {
*/
type: 'position';
position?: Functional;
+ /**
+ * 当 position 内的备选位置依然无法放下标签时,标签是否放回原位。
+ * 默认为 true,若为 false,则标签会被放在 position 数组的最后一个位置。
+ * @since 0.20.18
+ * @default true
+ */
+ restorePosition?: boolean;
};
export type BoundStrategy = {
@@ -297,6 +304,13 @@ export type BoundStrategy = {
*/
type: 'bound';
position?: Functional;
+ /**
+ * 当 position 内的备选位置依然无法放下标签时,标签是否放回原位。
+ * 默认为 true,若为 false,则标签会被放在 position 数组的最后一个位置。
+ * @since 0.20.18
+ * @default true
+ */
+ restorePosition?: boolean;
};
export type MoveYStrategy = {
diff --git a/packages/vrender-components/src/marker/config.ts b/packages/vrender-components/src/marker/config.ts
index 73a6df366..f24bc5123 100644
--- a/packages/vrender-components/src/marker/config.ts
+++ b/packages/vrender-components/src/marker/config.ts
@@ -364,6 +364,39 @@ export const DEFAULT_CARTESIAN_MARK_AREA_TEXT_STYLE_MAP: {
middle: {
textAlign: 'center',
textBaseline: 'middle'
+ },
+
+ topLeft: {
+ textAlign: 'right',
+ textBaseline: 'top'
+ },
+ insideTopLeft: {
+ textAlign: 'left',
+ textBaseline: 'top'
+ },
+ topRight: {
+ textAlign: 'left',
+ textBaseline: 'top'
+ },
+ insideTopRight: {
+ textAlign: 'right',
+ textBaseline: 'top'
+ },
+ bottomLeft: {
+ textAlign: 'right',
+ textBaseline: 'bottom'
+ },
+ insideBottomLeft: {
+ textAlign: 'left',
+ textBaseline: 'bottom'
+ },
+ bottomRight: {
+ textAlign: 'left',
+ textBaseline: 'bottom'
+ },
+ insideBottomRight: {
+ textAlign: 'right',
+ textBaseline: 'bottom'
}
};
diff --git a/packages/vrender-components/src/marker/point.ts b/packages/vrender-components/src/marker/point.ts
index 071373873..1f77dea05 100644
--- a/packages/vrender-components/src/marker/point.ts
+++ b/packages/vrender-components/src/marker/point.ts
@@ -243,9 +243,19 @@ export class MarkPoint extends Marker {
this._isStraightLine =
fuzzyEqualNumber(itemOffsetX, 0, FUZZY_EQUAL_DELTA) || fuzzyEqualNumber(itemOffsetY, 0, FUZZY_EQUAL_DELTA);
if (this._isArcLine) {
- const { x: x1, y: y1 } = newPosition;
+ // 思路:
+ // 1. 以数据位置为起点, 标记内容的位置为终点绘制圆弧
+ // - 在起点与终点的垂直平分线上找圆心
+ // - 根据圆心计算半径
+ // - 根据圆心计算起始角度和结束角度
+ // 2. 根据数据位置上要绘制的targetSymbol调整起始角度, 保证标记线上的startSymbol紧贴在targetSymbol上
+ // ps: 计算时将targetSymbol看作圆, 如果是其他不规则形状, 无法保证
+ // - 直接计算圆弧与targetSymbol的交点 到 圆心 的角度(也可以先计算交点的准确坐标, 但需解二元二次方程, 可行却没必要)
+ // - 用计算好的起始角度 - 交点到圆心的角度, 得到最终起始角度
+ // 3. 根据是否为凹凸圆弧, 进行角度的进一步加工
+
+ const { x: x1, y: y1 } = this.attribute.position;
const { x: x2, y: y2 } = newItemPosition;
-
// 得到中点和斜率
const x0 = (x1 + x2) / 2;
const y0 = (y1 + y2) / 2;
@@ -254,14 +264,20 @@ export class MarkPoint extends Marker {
const line = (x: number) => k * (x - x0) + y0;
// 在垂直平分线上找圆心
const direction = y2 > y1 ? -1 : 1;
-
const deltaX = arcRatio * direction * x0; // 数值决定曲率, 符号决定法向, 可通过配置自定义
const centerX = x0 + deltaX;
const centerY = line(centerX);
+ // 计算半径和角度
startAngle = deltaXYToAngle(y1 - centerY, x1 - centerX);
endAngle = deltaXYToAngle(y2 - centerY, x2 - centerX);
center = { x: centerX, y: centerY };
+ // 圆弧与symbol交点的角度
+ const R = Math.sqrt((centerX - x1) * (centerX - x1) + (centerY - y1) * (centerY - y1));
+ const r = this.attribute.targetSymbol.style.size / 2;
+ const deltaAngle = Math.acos(Math.sqrt(1 - (r * r) / (4 * R * R))) * 2;
+ startAngle = startAngle + deltaAngle;
+
if (arcRatio > 0) {
// 此时绘制凹圆弧, 顺时针绘制
// 根据arc图元绘制逻辑, 需要保证endAngle > startAngle, 才能顺时针绘制
@@ -437,7 +453,7 @@ export class MarkPoint extends Marker {
}
protected computeNewPositionAfterTargetItem(position: Point) {
- const { itemContent = {}, targetSymbol } = this.attribute as MarkPointAttrs;
+ const { itemContent = {}, targetSymbol, itemLine } = this.attribute as MarkPointAttrs;
const { offsetX: itemContentOffsetX = 0, offsetY: itemContentOffsetY = 0 } = itemContent;
const {
offset: targetSymbolOffset = 0,
@@ -446,7 +462,18 @@ export class MarkPoint extends Marker {
size: targetSymbolSize
} = targetSymbol;
const targetSize = targetItemvisible ? targetSymbolStyle.size ?? targetSymbolSize ?? 20 : 0;
- const targetOffsetAngle = deltaXYToAngle(itemContentOffsetY, itemContentOffsetX);
+
+ let targetOffsetAngle;
+ if (itemLine.type === 'type-do') {
+ targetOffsetAngle = deltaXYToAngle(itemContentOffsetY, itemContentOffsetX / 2);
+ } else if (itemLine.type === 'type-po') {
+ targetOffsetAngle = deltaXYToAngle(0, itemContentOffsetX);
+ } else if (itemLine.type === 'type-op') {
+ targetOffsetAngle = deltaXYToAngle(itemContentOffsetY, 0);
+ } else {
+ targetOffsetAngle = deltaXYToAngle(itemContentOffsetY, itemContentOffsetX);
+ }
+
const newPosition: Point = {
x: position.x + (targetSize / 2 + targetSymbolOffset) * Math.cos(targetOffsetAngle),
y: position.y + (targetSize / 2 + targetSymbolOffset) * Math.sin(targetOffsetAngle)
@@ -455,7 +482,6 @@ export class MarkPoint extends Marker {
x: position.x + (targetSize / 2 + targetSymbolOffset) * Math.cos(targetOffsetAngle) + itemContentOffsetX, // 偏移量 = targetItem size + targetItem space + 用户配置offset
y: position.y + (targetSize / 2 + targetSymbolOffset) * Math.sin(targetOffsetAngle) + itemContentOffsetY // 偏移量 = targetItem size + targetItem space + 用户配置offset
};
-
return { newPosition, newItemPosition };
}
diff --git a/packages/vrender-components/src/marker/type.ts b/packages/vrender-components/src/marker/type.ts
index 981d8c7f8..45b09818e 100644
--- a/packages/vrender-components/src/marker/type.ts
+++ b/packages/vrender-components/src/marker/type.ts
@@ -42,11 +42,21 @@ export enum IMarkAreaLabelPosition {
right = 'right',
top = 'top',
bottom = 'bottom',
+ topLeft = 'topLeft',
+ topRight = 'topRight',
+ bottomLeft = 'bottomLeft',
+ bottomRight = 'bottomRight',
+
middle = 'middle',
+
insideLeft = 'insideLeft',
insideRight = 'insideRight',
insideTop = 'insideTop',
- insideBottom = 'insideBottom'
+ insideBottom = 'insideBottom',
+ insideTopLeft = 'insideTopLeft',
+ insideTopRight = 'insideTopRight',
+ insideBottomLeft = 'insideBottomLeft',
+ insideBottomRight = 'insideBottomRight'
}
export enum IMarkCommonArcLabelPosition {
diff --git a/packages/vrender-components/src/scrollbar/scrollbar-plugin.ts b/packages/vrender-components/src/scrollbar/scrollbar-plugin.ts
index 3598af95c..6201ce2bf 100644
--- a/packages/vrender-components/src/scrollbar/scrollbar-plugin.ts
+++ b/packages/vrender-components/src/scrollbar/scrollbar-plugin.ts
@@ -40,7 +40,10 @@ export class ScrollBarPlugin implements IPlugin {
this.params = ScrollBarPlugin.defaultParams;
}
scroll = (e: { deltaX: number; deltaY: number; target: IGraphic }) => {
+ // 计算子元素的bounds
const graphic = e.target as any;
+ // childrenBounds.set(0, 0, scrollContainer.AABBBounds.width(), scrollContainer.AABBBounds.height());
+
const data = this.getScrollContainer(graphic);
if (!data && !this.scrollContainer) {
@@ -72,8 +75,13 @@ export class ScrollBarPlugin implements IPlugin {
}
this.scrollContainer = data ?? this.scrollContainer;
-
+ if (!data) {
+ return;
+ }
const scrollContainer = data.g;
+ if (!scrollContainer) {
+ return;
+ }
const { width, height, scrollX = 0, scrollY = 0 } = scrollContainer.attribute;
let newScrollX = scrollX;
let newScrollY = scrollY;
@@ -94,34 +102,32 @@ export class ScrollBarPlugin implements IPlugin {
}
}
- // 计算子元素的bounds
- const childrenBounds = this.childrenBounds;
-
- childrenBounds.clear();
- childrenBounds.set(0, 0, scrollContainer.AABBBounds.width(), scrollContainer.AABBBounds.height());
-
- const scrollWidth = childrenBounds.width();
- const scrollHeight = childrenBounds.height();
+ const scrollWidth = this.childrenBounds.width();
+ const scrollHeight = this.childrenBounds.height();
if (showH) {
- newScrollX = Math.max(Math.min((e.deltaX ?? 0) - scrollX, scrollWidth - width), 0);
- } else {
- newScrollX = -scrollX;
+ newScrollX = scrollX - (e.deltaX ?? 0);
+ if (newScrollX > 0) {
+ newScrollX = 0;
+ } else if (newScrollX < width - scrollWidth) {
+ newScrollX = width - scrollWidth;
+ }
}
if (showV) {
- newScrollY = Math.max(Math.min((e.deltaY ?? 0) - scrollY, scrollHeight - height), 0);
- } else {
- newScrollY = -scrollY;
+ newScrollY = scrollY - (e.deltaY ?? 0);
+ if (newScrollY > 0) {
+ newScrollY = 0;
+ } else if (newScrollY < height - scrollHeight) {
+ newScrollY = height - scrollHeight;
+ }
}
- childrenBounds.translate(-newScrollX, -newScrollY);
-
- this.addOrUpdateScroll(showH, showV, scrollContainer.parent, scrollContainer);
scrollContainer.setAttributes({
- scrollX: -newScrollX,
- scrollY: -newScrollY
+ scrollX: newScrollX,
+ scrollY: newScrollY
});
+ this.addOrUpdateScroll(showH, showV, scrollContainer.parent, scrollContainer);
};
handleScrollBarChange = (params: any) => {
@@ -241,15 +247,16 @@ export class ScrollBarPlugin implements IPlugin {
}
const childrenBounds = this.childrenBounds;
+ const { scrollX, scrollY } = scrollContainer.attribute;
if (isHorozntal) {
const ratio = Math.min(this.scrollContainerBounds.width() / childrenBounds.width(), 1);
- const start = Math.max(Math.min(this.childrenBounds.x1 / this.childrenBounds.width(), 0), ratio - 1);
+ const start = Math.max(Math.min(scrollX / this.childrenBounds.width(), 0), ratio - 1);
attrs.x = x + dx;
- attrs.y = y + dy + height - this.scrollContainerBounds.height();
+ attrs.y = y + dy + height - (attrs.height ?? 0);
attrs.range = [-start, -start + ratio];
} else {
const ratio = Math.min(this.scrollContainerBounds.height() / childrenBounds.height(), 1);
- const start = Math.max(Math.min(this.childrenBounds.y1 / this.childrenBounds.height(), 0), ratio - 1);
+ const start = Math.max(Math.min(scrollY / this.childrenBounds.height(), 0), ratio - 1);
attrs.x = x + dx + width - this.scrollContainerBounds.width();
attrs.y = y + dy;
attrs.range = [-start, -start + ratio];
@@ -297,13 +304,20 @@ export class ScrollBarPlugin implements IPlugin {
showV = !showH;
}
+ const childrenBounds = this.childrenBounds;
+
+ childrenBounds.clear();
+ g.forEachChildren((g: IGraphic) => {
+ childrenBounds.union(g.AABBBounds);
+ });
+
if (!g.AABBBounds.empty()) {
if (showH) {
- showH = width < g.AABBBounds.width();
+ showH = width < childrenBounds.width();
}
if (showV) {
- showV = height < g.AABBBounds.height();
+ showV = height < childrenBounds.height();
}
}
diff --git a/packages/vrender-core/CHANGELOG.json b/packages/vrender-core/CHANGELOG.json
index db0c0e91c..1d699eb09 100644
--- a/packages/vrender-core/CHANGELOG.json
+++ b/packages/vrender-core/CHANGELOG.json
@@ -1,6 +1,27 @@
{
"name": "@visactor/vrender-core",
"entries": [
+ {
+ "version": "0.21.1",
+ "tag": "@visactor/vrender-core_v0.21.1",
+ "date": "Thu, 05 Dec 2024 07:50:47 GMT",
+ "comments": {
+ "none": [
+ {
+ "comment": "fix: fix issue with insertAfter and insertBefore"
+ },
+ {
+ "comment": "fix: fix the issue when line is configured to connect, closed #3238"
+ },
+ {
+ "comment": "fix: fix issue with richtext setAttribute, closed #1578"
+ },
+ {
+ "comment": "fix: fix issue with richtext default font"
+ }
+ ]
+ }
+ },
{
"version": "0.21.0",
"tag": "@visactor/vrender-core_v0.21.0",
diff --git a/packages/vrender-core/CHANGELOG.md b/packages/vrender-core/CHANGELOG.md
index 194bfc270..ed7f15751 100644
--- a/packages/vrender-core/CHANGELOG.md
+++ b/packages/vrender-core/CHANGELOG.md
@@ -1,6 +1,16 @@
# Change Log - @visactor/vrender-core
-This log was last generated on Thu, 28 Nov 2024 03:30:36 GMT and should not be manually modified.
+This log was last generated on Thu, 05 Dec 2024 07:50:47 GMT and should not be manually modified.
+
+## 0.21.1
+Thu, 05 Dec 2024 07:50:47 GMT
+
+### Updates
+
+- fix: fix issue with insertAfter and insertBefore
+- fix: fix the issue when line is configured to connect, closed #3238
+- fix: fix issue with richtext setAttribute, closed #1578
+- fix: fix issue with richtext default font
## 0.21.0
Thu, 28 Nov 2024 03:30:36 GMT
diff --git a/packages/vrender-core/package.json b/packages/vrender-core/package.json
index 0138c9bf7..fe476c736 100644
--- a/packages/vrender-core/package.json
+++ b/packages/vrender-core/package.json
@@ -1,6 +1,6 @@
{
"name": "@visactor/vrender-core",
- "version": "0.21.0",
+ "version": "0.21.1",
"description": "",
"sideEffects": [
"./src/modules.ts",
@@ -30,7 +30,7 @@
},
"dependencies": {
"color-convert": "2.0.1",
- "@visactor/vutils": "~0.19.1"
+ "@visactor/vutils": "~0.19.2"
},
"devDependencies": {
"@internal/bundler": "workspace:*",
diff --git a/packages/vrender-core/src/common/render-curve.ts b/packages/vrender-core/src/common/render-curve.ts
index 6bd59bb8d..b22abd04e 100644
--- a/packages/vrender-core/src/common/render-curve.ts
+++ b/packages/vrender-core/src/common/render-curve.ts
@@ -37,7 +37,9 @@ function drawEachCurve(
// 找到合法的点
const { originP1, originP2 } = curve;
let validP: IPointLike;
- if (originP1 && originP1.defined !== false) {
+ // 只能第一个curve才可以用p0作为合法点,后面的curve都不应该算p1,因为已经算在前面了
+ // lastCurve只在第一个curve不存在
+ if (originP1 && originP1.defined !== false && !lastCurve) {
validP = p0;
} else if (originP1 && originP2.defined !== false) {
validP = curve.p3 ?? curve.p1;
diff --git a/packages/vrender-core/src/constants.ts b/packages/vrender-core/src/constants.ts
index 888462379..55ce99357 100644
--- a/packages/vrender-core/src/constants.ts
+++ b/packages/vrender-core/src/constants.ts
@@ -1,2 +1,6 @@
export const EnvContribution = Symbol.for('EnvContribution');
export const VGlobal = Symbol.for('VGlobal');
+
+export const DEFAULT_TEXT_FONT_FAMILY =
+ // eslint-disable-next-line max-len
+ 'PingFang SC,Helvetica Neue,Microsoft Yahei,system-ui,-apple-system,segoe ui,Roboto,Helvetica,Arial,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol';
diff --git a/packages/vrender-core/src/graphic/group.ts b/packages/vrender-core/src/graphic/group.ts
index 5313511e4..021300b93 100644
--- a/packages/vrender-core/src/graphic/group.ts
+++ b/packages/vrender-core/src/graphic/group.ts
@@ -184,7 +184,7 @@ export class Group extends Graphic implements IGroup {
const originalAABBBounds = aabbBounds; // fix aabbbounds update error in flex layout
aabbBounds = aabbBounds.clone();
- const { width, height, path, clip = groupTheme.clip, display } = attribute;
+ const { width, height, path, clip = groupTheme.clip } = attribute;
// 添加自身的fill或者clip
if (path && path.length) {
path.forEach(g => {
@@ -198,6 +198,9 @@ export class Group extends Graphic implements IGroup {
this.forEachChildren((node: IGraphic) => {
aabbBounds.union(node.AABBBounds);
});
+ // 如果没有clip的话,还需要加一下scroll
+ const { scrollX = 0, scrollY = 0 } = attribute;
+ aabbBounds.translate(scrollX, scrollY);
}
application.graphicService.updateTempAABBBounds(aabbBounds);
diff --git a/packages/vrender-core/src/graphic/node-tree.ts b/packages/vrender-core/src/graphic/node-tree.ts
index d13969f6c..57bc6861a 100644
--- a/packages/vrender-core/src/graphic/node-tree.ts
+++ b/packages/vrender-core/src/graphic/node-tree.ts
@@ -181,7 +181,7 @@ export class Node extends EventEmitter implements INode {
if (!referenceNode) {
return this.appendChild(newNode);
}
- if (this._uid === newNode._uid) {
+ if (this === newNode || newNode === referenceNode) {
return null;
}
if (newNode.isAncestorsOf(this)) {
@@ -225,7 +225,7 @@ export class Node extends EventEmitter implements INode {
if (!referenceNode) {
return this.appendChild(newNode);
}
- if (this._uid === newNode._uid) {
+ if (this === newNode || newNode === referenceNode) {
return null;
}
if (newNode.isAncestorsOf(this)) {
@@ -272,7 +272,7 @@ export class Node extends EventEmitter implements INode {
if (idx >= this.childrenCount) {
return this.appendChild(newNode);
}
- if (this._uid === newNode._uid) {
+ if (this === newNode) {
return null;
}
if (newNode.isAncestorsOf(this)) {
diff --git a/packages/vrender-core/src/graphic/richtext.ts b/packages/vrender-core/src/graphic/richtext.ts
index a2e94ebd1..e7fe6d0a5 100644
--- a/packages/vrender-core/src/graphic/richtext.ts
+++ b/packages/vrender-core/src/graphic/richtext.ts
@@ -41,6 +41,13 @@ const RICHTEXT_UPDATE_TAG_KEY = [
'fill',
'stroke',
'fontSize',
+ 'fontFamily',
+ 'fontStyle',
+ 'fontWeight',
+ 'lineWidth',
+ 'opacity',
+ 'fillOpacity',
+ 'strokeOpacity',
...GRAPHIC_UPDATE_TAG_KEY
];
diff --git a/packages/vrender-core/src/graphic/richtext/utils.ts b/packages/vrender-core/src/graphic/richtext/utils.ts
index ed9303af9..44bf509bf 100644
--- a/packages/vrender-core/src/graphic/richtext/utils.ts
+++ b/packages/vrender-core/src/graphic/richtext/utils.ts
@@ -2,6 +2,7 @@ import type { IBoundsLike } from '@visactor/vutils';
import { application } from '../../application';
import { createColor } from '../../common/canvas-utils';
import type { IContext2d, ITextStyleParams, IRichTextParagraphCharacter } from '../../interface';
+import { DEFAULT_TEXT_FONT_FAMILY } from '../../constants';
export const DIRECTION_KEY = {
horizontal: {
@@ -26,7 +27,7 @@ export const DIRECTION_KEY = {
const defaultFormatting = {
fontSize: 16,
- fontFamily: 'sans-serif',
+ fontFamily: DEFAULT_TEXT_FONT_FAMILY,
fill: true,
stroke: false,
fontWeight: 'normal',
@@ -57,7 +58,7 @@ const setTextStyle = (ctx: IContext2d, character: IRichTextParagraphCharacter) =
fontStyle: character.fontStyle || '',
fontWeight: character.fontWeight || '',
fontSize,
- fontFamily: character.fontFamily || 'sans-serif'
+ fontFamily: character.fontFamily
} as ITextStyleParams);
};
diff --git a/packages/vrender-core/src/render/contributions/render/draw-contribution.ts b/packages/vrender-core/src/render/contributions/render/draw-contribution.ts
index 0d5ac268a..b3f066399 100644
--- a/packages/vrender-core/src/render/contributions/render/draw-contribution.ts
+++ b/packages/vrender-core/src/render/contributions/render/draw-contribution.ts
@@ -46,6 +46,8 @@ export class DefaultDrawContribution implements IDrawContribution {
declare global: IGlobal;
declare layerService: ILayerService;
+ declare scrollMatrix?: IMatrix;
+
constructor(
// @inject(ContributionProvider)
// @named(GraphicRender)
@@ -347,23 +349,32 @@ export class DefaultDrawContribution implements IDrawContribution {
return;
}
- let retrans: boolean = false;
- let tempBounds: IAABBBounds;
+ let retrans: boolean = this.scrollMatrix && (this.scrollMatrix.e !== 0 || this.scrollMatrix.f !== 0);
+ let tempBounds: IBounds;
if (graphic.parent) {
const { scrollX = 0, scrollY = 0 } = graphic.parent.attribute;
- retrans = !!(scrollX || scrollY);
- if (retrans) {
- tempBounds = this.dirtyBounds.clone();
- // 变换dirtyBounds
- const m = graphic.globalTransMatrix.getInverse();
- this.dirtyBounds.copy(this.backupDirtyBounds).transformWithMatrix(m);
- this.dirtyBounds.translate(-scrollX, -scrollY);
+ if (!!(scrollX || scrollY)) {
+ retrans = true;
+ if (!this.scrollMatrix) {
+ this.scrollMatrix = matrixAllocate.allocate(1, 0, 0, 1, 0, 0);
+ }
+ this.scrollMatrix.translate(-scrollX, -scrollY);
}
}
+ // 需要二次变化,那就重新算一个变换后的Bounds
+ if (retrans) {
+ tempBounds = this.dirtyBounds.clone().transformWithMatrix(this.scrollMatrix);
+ }
- if (this.useDirtyBounds && !(graphic.isContainer || isRectIntersect(graphic.AABBBounds, this.dirtyBounds, false))) {
- retrans && this.dirtyBounds.copy(tempBounds);
+ if (
+ this.useDirtyBounds &&
+ !(graphic.isContainer || isRectIntersect(graphic.AABBBounds, tempBounds ?? this.dirtyBounds, false))
+ ) {
+ if (retrans && graphic.parent) {
+ const { scrollX = 0, scrollY = 0 } = graphic.parent.attribute;
+ this.scrollMatrix && this.scrollMatrix.translate(scrollX, scrollY);
+ }
return;
}
@@ -378,7 +389,11 @@ export class DefaultDrawContribution implements IDrawContribution {
renderer.draw(graphic, this.currentRenderService, drawContext, params);
}
- retrans && this.dirtyBounds.copy(tempBounds);
+ // retrans && this.dirtyBounds.copy(tempBounds);
+ if (retrans && graphic.parent) {
+ const { scrollX = 0, scrollY = 0 } = graphic.parent.attribute;
+ this.scrollMatrix && this.scrollMatrix.translate(scrollX, scrollY);
+ }
// 添加拦截器
if (this.InterceptorContributions.length) {
diff --git a/packages/vrender-kits/CHANGELOG.json b/packages/vrender-kits/CHANGELOG.json
index 7d076d264..3b4cd3ee4 100644
--- a/packages/vrender-kits/CHANGELOG.json
+++ b/packages/vrender-kits/CHANGELOG.json
@@ -1,6 +1,12 @@
{
"name": "@visactor/vrender-kits",
"entries": [
+ {
+ "version": "0.21.1",
+ "tag": "@visactor/vrender-kits_v0.21.1",
+ "date": "Thu, 05 Dec 2024 07:50:47 GMT",
+ "comments": {}
+ },
{
"version": "0.21.0",
"tag": "@visactor/vrender-kits_v0.21.0",
diff --git a/packages/vrender-kits/CHANGELOG.md b/packages/vrender-kits/CHANGELOG.md
index 8ff8464a7..73c95f45a 100644
--- a/packages/vrender-kits/CHANGELOG.md
+++ b/packages/vrender-kits/CHANGELOG.md
@@ -1,6 +1,11 @@
# Change Log - @visactor/vrender-kits
-This log was last generated on Thu, 28 Nov 2024 03:30:36 GMT and should not be manually modified.
+This log was last generated on Thu, 05 Dec 2024 07:50:47 GMT and should not be manually modified.
+
+## 0.21.1
+Thu, 05 Dec 2024 07:50:47 GMT
+
+_Version update only_
## 0.21.0
Thu, 28 Nov 2024 03:30:36 GMT
diff --git a/packages/vrender-kits/package.json b/packages/vrender-kits/package.json
index 96bcf79d6..49c44900e 100644
--- a/packages/vrender-kits/package.json
+++ b/packages/vrender-kits/package.json
@@ -1,6 +1,6 @@
{
"name": "@visactor/vrender-kits",
- "version": "0.21.0",
+ "version": "0.21.1",
"description": "",
"sideEffects": false,
"main": "cjs/index.js",
@@ -20,8 +20,8 @@
"test": ""
},
"dependencies": {
- "@visactor/vutils": "~0.19.1",
- "@visactor/vrender-core": "workspace:0.21.0",
+ "@visactor/vutils": "~0.19.2",
+ "@visactor/vrender-core": "workspace:0.21.1",
"@resvg/resvg-js": "2.4.1",
"roughjs": "4.5.2"
},
diff --git a/packages/vrender/CHANGELOG.json b/packages/vrender/CHANGELOG.json
index 608a30e5d..eb005aec2 100644
--- a/packages/vrender/CHANGELOG.json
+++ b/packages/vrender/CHANGELOG.json
@@ -1,6 +1,12 @@
{
"name": "@visactor/vrender",
"entries": [
+ {
+ "version": "0.21.1",
+ "tag": "@visactor/vrender_v0.21.1",
+ "date": "Thu, 05 Dec 2024 07:50:47 GMT",
+ "comments": {}
+ },
{
"version": "0.21.0",
"tag": "@visactor/vrender_v0.21.0",
diff --git a/packages/vrender/CHANGELOG.md b/packages/vrender/CHANGELOG.md
index a619351e6..d76fe89d3 100644
--- a/packages/vrender/CHANGELOG.md
+++ b/packages/vrender/CHANGELOG.md
@@ -1,6 +1,11 @@
# Change Log - @visactor/vrender
-This log was last generated on Thu, 28 Nov 2024 03:30:36 GMT and should not be manually modified.
+This log was last generated on Thu, 05 Dec 2024 07:50:47 GMT and should not be manually modified.
+
+## 0.21.1
+Thu, 05 Dec 2024 07:50:47 GMT
+
+_Version update only_
## 0.21.0
Thu, 28 Nov 2024 03:30:36 GMT
diff --git a/packages/vrender/package.json b/packages/vrender/package.json
index f9e78e7a7..a8c4167a1 100644
--- a/packages/vrender/package.json
+++ b/packages/vrender/package.json
@@ -1,6 +1,6 @@
{
"name": "@visactor/vrender",
- "version": "0.21.0",
+ "version": "0.21.1",
"description": "",
"sideEffects": true,
"main": "cjs/index.js",
@@ -24,15 +24,15 @@
"test-watch": "cross-env DEBUG_MODE=1 jest --watch"
},
"dependencies": {
- "@visactor/vrender-core": "workspace:0.21.0",
- "@visactor/vrender-kits": "workspace:0.21.0"
+ "@visactor/vrender-core": "workspace:0.21.1",
+ "@visactor/vrender-kits": "workspace:0.21.1"
},
"devDependencies": {
"@internal/bundler": "workspace:*",
"@internal/eslint-config": "workspace:*",
"@internal/ts-config": "workspace:*",
"@rushstack/eslint-patch": "~1.1.4",
- "@visactor/vutils": "~0.19.1",
+ "@visactor/vutils": "~0.19.2",
"canvas": "2.11.2",
"react": "^18.0.0",
"react-dom": "^18.0.0",
diff --git a/tools/bugserver-trigger/package.json b/tools/bugserver-trigger/package.json
index e88960bf0..69ccb1f00 100644
--- a/tools/bugserver-trigger/package.json
+++ b/tools/bugserver-trigger/package.json
@@ -8,10 +8,10 @@
"ci": "ts-node --transpileOnly --skipProject ./scripts/trigger-test.ts"
},
"dependencies": {
- "@visactor/vrender": "workspace:0.21.0",
- "@visactor/vrender-core": "workspace:0.21.0",
- "@visactor/vrender-kits": "workspace:0.21.0",
- "@visactor/vrender-components": "workspace:0.21.0"
+ "@visactor/vrender": "workspace:0.21.1",
+ "@visactor/vrender-core": "workspace:0.21.1",
+ "@visactor/vrender-kits": "workspace:0.21.1",
+ "@visactor/vrender-components": "workspace:0.21.1"
},
"devDependencies": {
"@rushstack/eslint-patch": "~1.1.4",