Skip to content

Commit 5f99d95

Browse files
authored
feat(word-cloud): imageMask选项支持url字符串格式 (#1617)
* feat: imageMask选项支持url字符串格式 * chore: 优化代码 * docs: 添加base64示例 * docs: 更改base64图片 * docs: 修改文档
1 parent 136ee33 commit 5f99d95

File tree

9 files changed

+114
-54
lines changed

9 files changed

+114
-54
lines changed

examples/word-cloud/basic/API.en.md

+4-13
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,11 @@
4646

4747
#### imageMask
4848

49-
**可选**, _HTMLImageElement_
49+
**可选**, _HTMLImageElement \| string_
5050

51-
功能描述: 设置一张图片,然后图表就可以根据该图片的形状进行渲染,必须是已加载完成的图片对象
51+
功能描述: 设置一张图片,然后图表就可以根据该图片的形状进行渲染,可以是图片元素实例或者 url 地址和 base64
52+
53+
注意: 词语只渲染在图片的深色部位,浅色的部位(如白色)不渲染词语。当使用图片的 url 地址时,图片的大小不宜过大,不然图片加载时间过长
5254

5355
默认配置: 无
5456

@@ -76,17 +78,6 @@
7678
| -------- | ----------------------------------- | ---------------------------------------------------- |
7779
| text | _string_ | 文本内容 |
7880
| value | _number_ | 该文本所占权重 |
79-
| font | _string_ | 字体 |
80-
| style | _"normal" \| "italic" \| "oblique"_ | 字体样式 |
81-
| weight | _number \| string_ | 文本粗细 |
82-
| rotate | _number_ | 旋转角度 |
83-
| size | _number_ | 字体大小 |
84-
| padding | _number_ | 一个单词所占的盒子的内边距,值越大单词之间的间隔越大 |
85-
| hasText | _boolean_ | 是否包含文本 |
86-
| width | _number_ | 单词所占盒子的宽度 |
87-
| height | _number_ | 单词所占盒子的高度 |
88-
| x | _number_ | x 轴坐标 |
89-
| y | _number_ | y 轴坐标 |
9081

9182
`markdown:docs/common/color.en.md`
9283

examples/word-cloud/basic/API.zh.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,11 @@
4646

4747
#### imageMask
4848

49-
**可选**, _HTMLImageElement_
49+
**可选**, _HTMLImageElement \| string_
5050

51-
功能描述: 设置一张图片,然后图表就可以根据该图片的形状进行渲染,必须是已加载完成的图片对象
51+
功能描述: 设置一张图片,然后图表就可以根据该图片的形状进行渲染,可以是图片元素实例或者 url 地址和 base64
52+
53+
注意: 词语只渲染在图片的深色部位,浅色的部位(如白色)不渲染词语。当使用图片的 url 地址时,图片的大小不宜过大,不然图片加载时间过长
5254

5355
默认配置: 无
5456

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { WordCloud } from '@antv/g2plot';
2+
3+
fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/antv-keywords.json')
4+
.then((res) => res.json())
5+
.then((data) => {
6+
const wordCloud = new WordCloud('container', {
7+
data,
8+
width: 600,
9+
height: 400,
10+
wordField: 'name',
11+
weightField: 'value',
12+
imageMask:
13+
'data:image/png;base64,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',
14+
wordStyle: {
15+
fontFamily: 'Verdana',
16+
fontSize: [8, 32],
17+
},
18+
});
19+
20+
wordCloud.render();
21+
});
+17-25
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,20 @@
11
import { WordCloud } from '@antv/g2plot';
22

3-
const imageMask = new Image();
4-
imageMask.crossOrigin = '';
5-
imageMask.src = 'https://gw.alipayobjects.com/mdn/rms_2274c3/afts/img/A*07tdTIOmvlYAAAAAAAAAAABkARQnAQ';
6-
7-
// 等待图片加载完成
8-
imageMask.onload = () => {
9-
fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/antv-keywords.json')
10-
.then((res) => res.json())
11-
.then((data) => {
12-
const wordCloud = new WordCloud('container', {
13-
data,
14-
// 宽高设置最好根据 imageMask 做调整
15-
width: 600,
16-
height: 400,
17-
wordField: 'name',
18-
weightField: 'value',
19-
imageMask,
20-
wordStyle: {
21-
fontFamily: 'Verdana',
22-
fontSize: [8, 32],
23-
},
24-
});
25-
26-
wordCloud.render();
3+
fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/antv-keywords.json')
4+
.then((res) => res.json())
5+
.then((data) => {
6+
const wordCloud = new WordCloud('container', {
7+
data,
8+
width: 600,
9+
height: 400,
10+
wordField: 'name',
11+
weightField: 'value',
12+
imageMask: 'https://gw.alipayobjects.com/mdn/rms_2274c3/afts/img/A*07tdTIOmvlYAAAAAAAAAAABkARQnAQ',
13+
wordStyle: {
14+
fontFamily: 'Verdana',
15+
fontSize: [8, 32],
16+
},
2717
});
28-
};
18+
19+
wordCloud.render();
20+
});

examples/word-cloud/basic/demo/meta.json

+8
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@
2727
"en": "Word Cloud chart - image mask"
2828
},
2929
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f5c722/afts/img/A*id4CSZIMCtsAAAAAAAAAAABkARQnAQ"
30+
},
31+
{
32+
"filename": "image-mask-base64.ts",
33+
"title": {
34+
"zh": "词云图-图片遮罩-base64",
35+
"en": "Word Cloud chart - image mask base64"
36+
},
37+
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f5c722/afts/img/A*id4CSZIMCtsAAAAAAAAAAABkARQnAQ"
3038
}
3139
]
3240
}

package.json

+6-6
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@
5656
"@antv/data-set": "^0.11.5",
5757
"@antv/event-emitter": "^0.1.2",
5858
"@antv/g2": "^4.1.0-beta.7",
59-
"dayjs": "^1.8.34",
59+
"dayjs": "^1.8.36",
6060
"size-sensor": "^1.0.1",
6161
"tslib": "^1.13.0"
6262
},
6363
"devDependencies": {
64-
"@antv/gatsby-theme-antv": "^1.0.0-beta.0",
64+
"@antv/gatsby-theme-antv": "^1.0.0-beta.4",
6565
"@babel/core": "^7.10.4",
6666
"@babel/preset-env": "^7.10.4",
6767
"@commitlint/cli": "^8.2.0",
@@ -76,14 +76,14 @@
7676
"eslint-config-prettier": "^6.0.0",
7777
"eslint-plugin-import": "^2.22.0",
7878
"eslint-plugin-prettier": "^3.1.0",
79-
"gatsby": "^2.20.22",
79+
"gatsby": "^2.24.63",
8080
"generate-changelog": "^1.8.0",
8181
"gh-pages": "^3.1.0",
8282
"husky": "^4.2.3",
8383
"jest": "^26.0.1",
8484
"jest-electron": "^0.1.7",
8585
"jest-extended": "^0.11.2",
86-
"limit-size": "^0.1.1",
86+
"limit-size": "^0.1.3",
8787
"lint-md-cli": "^0.1.2",
8888
"lint-staged": "^10.0.7",
8989
"npm-run-all": "^4.1.5",
@@ -93,8 +93,8 @@
9393
"ts-jest": "^25.4.0",
9494
"ts-loader": "^7.0.0",
9595
"typescript": "^3.5.3",
96-
"webpack": "^4.39.2",
97-
"webpack-bundle-analyzer": "^3.4.1",
96+
"webpack": "^4.44.2",
97+
"webpack-bundle-analyzer": "^3.9.0",
9898
"webpack-cli": "^3.3.7",
9999
"webpack-dev-server": "^3.9.0"
100100
},

src/plots/word-cloud/index.ts

+24-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Plot } from '../../core/plot';
33
import { Adaptor } from '../../core/adaptor';
44
import { WordCloudOptions } from './types';
55
import { adaptor } from './adaptor';
6+
import { processImageMask } from './utils';
67
// 注册的shape
78
import './shapes/word-cloud';
89

@@ -18,7 +19,6 @@ export class WordCloud extends Plot<WordCloudOptions> {
1819
protected getDefaultOptions(): Partial<WordCloudOptions> {
1920
return deepMix({}, super.getDefaultOptions(), {
2021
timeInterval: 2000,
21-
autoFit: true,
2222
tooltip: {
2323
showTitle: false,
2424
showMarkers: false,
@@ -36,6 +36,29 @@ export class WordCloud extends Plot<WordCloudOptions> {
3636
});
3737
}
3838

39+
/**
40+
* 覆写父类方法,词云图需要加载图片资源,所以需要异步渲染
41+
*/
42+
public render() {
43+
const { imageMask } = this.options;
44+
45+
if (!imageMask) {
46+
// 调用父类渲染函数
47+
super.render();
48+
return;
49+
}
50+
51+
processImageMask(imageMask, (img) => {
52+
this.options = {
53+
...this.options,
54+
imageMask: img || null,
55+
};
56+
57+
// 调用父类渲染函数
58+
super.render();
59+
});
60+
}
61+
3962
/**
4063
* 获取 词云图 的适配器
4164
*/

src/plots/word-cloud/types.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ export interface WordCloudOptions extends Options {
5858
readonly wordField: string;
5959
/** 词条权重字段 */
6060
readonly weightField: string;
61-
/** 遮罩图片实例 */
62-
readonly imageMask?: HTMLImageElement;
61+
/** 遮罩图片实例,可以是图片 URL 或者 base64 */
62+
readonly imageMask?: HTMLImageElement | string;
6363
/** 最大执行时间 */
6464
readonly timeInterval?: number;
6565
/** 文字样式配置 */

src/plots/word-cloud/utils.ts

+28-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Chart, View } from '@antv/g2';
22
import DataSet from '@antv/data-set';
3-
import { isArray, isFunction, isNumber } from '@antv/util';
3+
import { isArray, isFunction, isNumber, isString } from '@antv/util';
44
import { Params } from '../../core/adaptor';
55
import { log, LEVEL, getContainerSize } from '../../utils';
66
import { WordCloudOptions } from './types';
@@ -23,7 +23,7 @@ export function transform(params: Params<WordCloudOptions>) {
2323
dv.transform({
2424
type: 'tag-cloud',
2525
fields: [wordField, weightField],
26-
imageMask: getImageMask(imageMask),
26+
imageMask: imageMask as HTMLImageElement,
2727
font: fontFamily,
2828
fontSize: getFontSize(options, range),
2929
fontWeight: fontWeight,
@@ -43,7 +43,7 @@ export function transform(params: Params<WordCloudOptions>) {
4343
*/
4444
function getSize(params: Params<WordCloudOptions> & { chart: Chart }) {
4545
const { chart, options } = params;
46-
const { autoFit } = options;
46+
const { autoFit = true } = options;
4747
let { width, height } = chart;
4848

4949
// 由于词云图每个词语的坐标都是先通过 DataSet 根据图表宽高计算出来的,
@@ -105,8 +105,31 @@ function normalPadding(padding: number | number[] | 'auto'): [number, number, nu
105105
return [0, 0, 0, 0];
106106
}
107107

108-
function getImageMask(img: HTMLImageElement) {
109-
return img;
108+
/**
109+
* 处理 imageMask 可能为 url 字符串的情况
110+
* @param img
111+
* @param callback
112+
*/
113+
export function processImageMask(img: HTMLImageElement | string, callback?: (img?: HTMLImageElement) => void) {
114+
if (img instanceof HTMLImageElement) {
115+
callback(img);
116+
return;
117+
}
118+
if (isString(img)) {
119+
const image = new Image();
120+
image.crossOrigin = 'anonymous';
121+
image.src = img;
122+
image.onload = () => {
123+
callback(image);
124+
};
125+
image.onerror = () => {
126+
log(LEVEL.ERROR, false, 'image %s load failed !!!', img);
127+
callback();
128+
};
129+
return;
130+
}
131+
log(LEVEL.WARN, img === undefined, 'the type of imageMask option must be String or HTMLImageElement.');
132+
callback();
110133
}
111134

112135
/**

0 commit comments

Comments
 (0)