diff --git a/examples/word-cloud/basic/API.en.md b/examples/word-cloud/basic/API.en.md index fb6cdeda60..94342e0924 100644 --- a/examples/word-cloud/basic/API.en.md +++ b/examples/word-cloud/basic/API.en.md @@ -46,9 +46,11 @@ #### imageMask -**可选**, _HTMLImageElement_ +**可选**, _HTMLImageElement \| string_ -功能描述: 设置一张图片,然后图表就可以根据该图片的形状进行渲染,必须是已加载完成的图片对象 +功能描述: 设置一张图片,然后图表就可以根据该图片的形状进行渲染,可以是图片元素实例或者 url 地址和 base64 + +注意: 词语只渲染在图片的深色部位,浅色的部位(如白色)不渲染词语。当使用图片的 url 地址时,图片的大小不宜过大,不然图片加载时间过长 默认配置: 无 @@ -76,17 +78,6 @@ | -------- | ----------------------------------- | ---------------------------------------------------- | | text | _string_ | 文本内容 | | value | _number_ | 该文本所占权重 | -| font | _string_ | 字体 | -| style | _"normal" \| "italic" \| "oblique"_ | 字体样式 | -| weight | _number \| string_ | 文本粗细 | -| rotate | _number_ | 旋转角度 | -| size | _number_ | 字体大小 | -| padding | _number_ | 一个单词所占的盒子的内边距,值越大单词之间的间隔越大 | -| hasText | _boolean_ | 是否包含文本 | -| width | _number_ | 单词所占盒子的宽度 | -| height | _number_ | 单词所占盒子的高度 | -| x | _number_ | x 轴坐标 | -| y | _number_ | y 轴坐标 | `markdown:docs/common/color.en.md` diff --git a/examples/word-cloud/basic/API.zh.md b/examples/word-cloud/basic/API.zh.md index c566d7b871..c3cf0d0d20 100644 --- a/examples/word-cloud/basic/API.zh.md +++ b/examples/word-cloud/basic/API.zh.md @@ -46,9 +46,11 @@ #### imageMask -**可选**, _HTMLImageElement_ +**可选**, _HTMLImageElement \| string_ -功能描述: 设置一张图片,然后图表就可以根据该图片的形状进行渲染,必须是已加载完成的图片对象 +功能描述: 设置一张图片,然后图表就可以根据该图片的形状进行渲染,可以是图片元素实例或者 url 地址和 base64 + +注意: 词语只渲染在图片的深色部位,浅色的部位(如白色)不渲染词语。当使用图片的 url 地址时,图片的大小不宜过大,不然图片加载时间过长 默认配置: 无 diff --git a/examples/word-cloud/basic/demo/image-mask-base64.ts b/examples/word-cloud/basic/demo/image-mask-base64.ts new file mode 100644 index 0000000000..2e96f2657e --- /dev/null +++ b/examples/word-cloud/basic/demo/image-mask-base64.ts @@ -0,0 +1,21 @@ +import { WordCloud } from '@antv/g2plot'; + +fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/antv-keywords.json') + .then((res) => res.json()) + .then((data) => { + const wordCloud = new WordCloud('container', { + data, + width: 600, + height: 400, + wordField: 'name', + weightField: 'value', + imageMask: + 'data:image/png;base64,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', + wordStyle: { + fontFamily: 'Verdana', + fontSize: [8, 32], + }, + }); + + wordCloud.render(); + }); diff --git a/examples/word-cloud/basic/demo/image-mask.ts b/examples/word-cloud/basic/demo/image-mask.ts index 7cfc4d5df5..e088f3863e 100644 --- a/examples/word-cloud/basic/demo/image-mask.ts +++ b/examples/word-cloud/basic/demo/image-mask.ts @@ -1,28 +1,20 @@ import { WordCloud } from '@antv/g2plot'; -const imageMask = new Image(); -imageMask.crossOrigin = ''; -imageMask.src = 'https://gw.alipayobjects.com/mdn/rms_2274c3/afts/img/A*07tdTIOmvlYAAAAAAAAAAABkARQnAQ'; - -// 等待图片加载完成 -imageMask.onload = () => { - fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/antv-keywords.json') - .then((res) => res.json()) - .then((data) => { - const wordCloud = new WordCloud('container', { - data, - // 宽高设置最好根据 imageMask 做调整 - width: 600, - height: 400, - wordField: 'name', - weightField: 'value', - imageMask, - wordStyle: { - fontFamily: 'Verdana', - fontSize: [8, 32], - }, - }); - - wordCloud.render(); +fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/antv-keywords.json') + .then((res) => res.json()) + .then((data) => { + const wordCloud = new WordCloud('container', { + data, + width: 600, + height: 400, + wordField: 'name', + weightField: 'value', + imageMask: 'https://gw.alipayobjects.com/mdn/rms_2274c3/afts/img/A*07tdTIOmvlYAAAAAAAAAAABkARQnAQ', + wordStyle: { + fontFamily: 'Verdana', + fontSize: [8, 32], + }, }); -}; + + wordCloud.render(); + }); diff --git a/examples/word-cloud/basic/demo/meta.json b/examples/word-cloud/basic/demo/meta.json index 686442606e..ef71494dcf 100644 --- a/examples/word-cloud/basic/demo/meta.json +++ b/examples/word-cloud/basic/demo/meta.json @@ -27,6 +27,14 @@ "en": "Word Cloud chart - image mask" }, "screenshot": "https://gw.alipayobjects.com/mdn/rms_f5c722/afts/img/A*id4CSZIMCtsAAAAAAAAAAABkARQnAQ" + }, + { + "filename": "image-mask-base64.ts", + "title": { + "zh": "词云图-图片遮罩-base64", + "en": "Word Cloud chart - image mask base64" + }, + "screenshot": "https://gw.alipayobjects.com/mdn/rms_f5c722/afts/img/A*id4CSZIMCtsAAAAAAAAAAABkARQnAQ" } ] } diff --git a/package.json b/package.json index b10f527133..7d41fbaa62 100644 --- a/package.json +++ b/package.json @@ -56,12 +56,12 @@ "@antv/data-set": "^0.11.5", "@antv/event-emitter": "^0.1.2", "@antv/g2": "^4.1.0-beta.7", - "dayjs": "^1.8.34", + "dayjs": "^1.8.36", "size-sensor": "^1.0.1", "tslib": "^1.13.0" }, "devDependencies": { - "@antv/gatsby-theme-antv": "^1.0.0-beta.0", + "@antv/gatsby-theme-antv": "^1.0.0-beta.4", "@babel/core": "^7.10.4", "@babel/preset-env": "^7.10.4", "@commitlint/cli": "^8.2.0", @@ -76,14 +76,14 @@ "eslint-config-prettier": "^6.0.0", "eslint-plugin-import": "^2.22.0", "eslint-plugin-prettier": "^3.1.0", - "gatsby": "^2.20.22", + "gatsby": "^2.24.63", "generate-changelog": "^1.8.0", "gh-pages": "^3.1.0", "husky": "^4.2.3", "jest": "^26.0.1", "jest-electron": "^0.1.7", "jest-extended": "^0.11.2", - "limit-size": "^0.1.1", + "limit-size": "^0.1.3", "lint-md-cli": "^0.1.2", "lint-staged": "^10.0.7", "npm-run-all": "^4.1.5", @@ -93,8 +93,8 @@ "ts-jest": "^25.4.0", "ts-loader": "^7.0.0", "typescript": "^3.5.3", - "webpack": "^4.39.2", - "webpack-bundle-analyzer": "^3.4.1", + "webpack": "^4.44.2", + "webpack-bundle-analyzer": "^3.9.0", "webpack-cli": "^3.3.7", "webpack-dev-server": "^3.9.0" }, diff --git a/src/plots/word-cloud/index.ts b/src/plots/word-cloud/index.ts index 6ee25a6f07..42fbc83546 100644 --- a/src/plots/word-cloud/index.ts +++ b/src/plots/word-cloud/index.ts @@ -3,6 +3,7 @@ import { Plot } from '../../core/plot'; import { Adaptor } from '../../core/adaptor'; import { WordCloudOptions } from './types'; import { adaptor } from './adaptor'; +import { processImageMask } from './utils'; // 注册的shape import './shapes/word-cloud'; @@ -18,7 +19,6 @@ export class WordCloud extends Plot { protected getDefaultOptions(): Partial { return deepMix({}, super.getDefaultOptions(), { timeInterval: 2000, - autoFit: true, tooltip: { showTitle: false, showMarkers: false, @@ -36,6 +36,29 @@ export class WordCloud extends Plot { }); } + /** + * 覆写父类方法,词云图需要加载图片资源,所以需要异步渲染 + */ + public render() { + const { imageMask } = this.options; + + if (!imageMask) { + // 调用父类渲染函数 + super.render(); + return; + } + + processImageMask(imageMask, (img) => { + this.options = { + ...this.options, + imageMask: img || null, + }; + + // 调用父类渲染函数 + super.render(); + }); + } + /** * 获取 词云图 的适配器 */ diff --git a/src/plots/word-cloud/types.ts b/src/plots/word-cloud/types.ts index dda1e7a43e..aeb9214400 100644 --- a/src/plots/word-cloud/types.ts +++ b/src/plots/word-cloud/types.ts @@ -58,8 +58,8 @@ export interface WordCloudOptions extends Options { readonly wordField: string; /** 词条权重字段 */ readonly weightField: string; - /** 遮罩图片实例 */ - readonly imageMask?: HTMLImageElement; + /** 遮罩图片实例,可以是图片 URL 或者 base64 */ + readonly imageMask?: HTMLImageElement | string; /** 最大执行时间 */ readonly timeInterval?: number; /** 文字样式配置 */ diff --git a/src/plots/word-cloud/utils.ts b/src/plots/word-cloud/utils.ts index f58d95bd45..f12830c023 100644 --- a/src/plots/word-cloud/utils.ts +++ b/src/plots/word-cloud/utils.ts @@ -1,6 +1,6 @@ import { Chart, View } from '@antv/g2'; import DataSet from '@antv/data-set'; -import { isArray, isFunction, isNumber } from '@antv/util'; +import { isArray, isFunction, isNumber, isString } from '@antv/util'; import { Params } from '../../core/adaptor'; import { log, LEVEL, getContainerSize } from '../../utils'; import { WordCloudOptions } from './types'; @@ -23,7 +23,7 @@ export function transform(params: Params) { dv.transform({ type: 'tag-cloud', fields: [wordField, weightField], - imageMask: getImageMask(imageMask), + imageMask: imageMask as HTMLImageElement, font: fontFamily, fontSize: getFontSize(options, range), fontWeight: fontWeight, @@ -43,7 +43,7 @@ export function transform(params: Params) { */ function getSize(params: Params & { chart: Chart }) { const { chart, options } = params; - const { autoFit } = options; + const { autoFit = true } = options; let { width, height } = chart; // 由于词云图每个词语的坐标都是先通过 DataSet 根据图表宽高计算出来的, @@ -105,8 +105,31 @@ function normalPadding(padding: number | number[] | 'auto'): [number, number, nu return [0, 0, 0, 0]; } -function getImageMask(img: HTMLImageElement) { - return img; +/** + * 处理 imageMask 可能为 url 字符串的情况 + * @param img + * @param callback + */ +export function processImageMask(img: HTMLImageElement | string, callback?: (img?: HTMLImageElement) => void) { + if (img instanceof HTMLImageElement) { + callback(img); + return; + } + if (isString(img)) { + const image = new Image(); + image.crossOrigin = 'anonymous'; + image.src = img; + image.onload = () => { + callback(image); + }; + image.onerror = () => { + log(LEVEL.ERROR, false, 'image %s load failed !!!', img); + callback(); + }; + return; + } + log(LEVEL.WARN, img === undefined, 'the type of imageMask option must be String or HTMLImageElement.'); + callback(); } /**