utils.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. import { __assign } from "tslib";
  2. import { isArray, isFunction, isString } from '@antv/util';
  3. import { getContainerSize, LEVEL, log } from '../../utils';
  4. import { normalPadding } from '../../utils/padding';
  5. import { functor, wordCloud } from '../../utils/transform/word-cloud';
  6. /**
  7. * 用 DataSet 转换词云图数据
  8. * @param params
  9. */
  10. export function transform(params) {
  11. var rawOptions = params.options, chart = params.chart;
  12. var _a = chart, width = _a.width, height = _a.height, chartPadding = _a.padding, appendPadding = _a.appendPadding, ele = _a.ele;
  13. var data = rawOptions.data, imageMask = rawOptions.imageMask, wordField = rawOptions.wordField, weightField = rawOptions.weightField, colorField = rawOptions.colorField, wordStyle = rawOptions.wordStyle, timeInterval = rawOptions.timeInterval, random = rawOptions.random, spiral = rawOptions.spiral, _b = rawOptions.autoFit, autoFit = _b === void 0 ? true : _b, placementStrategy = rawOptions.placementStrategy;
  14. if (!data || !data.length) {
  15. return [];
  16. }
  17. var fontFamily = wordStyle.fontFamily, fontWeight = wordStyle.fontWeight, padding = wordStyle.padding, fontSize = wordStyle.fontSize;
  18. var arr = getSingleKeyValues(data, weightField);
  19. var range = [min(arr), max(arr)];
  20. // 变换出 text 和 value 字段
  21. var words = data.map(function (datum) { return ({
  22. text: datum[wordField],
  23. value: datum[weightField],
  24. color: datum[colorField],
  25. datum: datum,
  26. }); });
  27. var options = {
  28. imageMask: imageMask,
  29. font: fontFamily,
  30. fontSize: getFontSizeMapping(fontSize, range),
  31. fontWeight: fontWeight,
  32. // 图表宽高减去 padding 之后的宽高
  33. size: getSize({
  34. width: width,
  35. height: height,
  36. padding: chartPadding,
  37. appendPadding: appendPadding,
  38. autoFit: autoFit,
  39. container: ele,
  40. }),
  41. padding: padding,
  42. timeInterval: timeInterval,
  43. random: random,
  44. spiral: spiral,
  45. rotate: getRotate(rawOptions),
  46. };
  47. // 自定义布局函数
  48. if (isFunction(placementStrategy)) {
  49. var result = words.map(function (word, index, words) { return (__assign(__assign(__assign({}, word), { hasText: !!word.text, font: functor(options.font)(word, index, words), weight: functor(options.fontWeight)(word, index, words), rotate: functor(options.rotate)(word, index, words), size: functor(options.fontSize)(word, index, words), style: 'normal' }), placementStrategy.call(chart, word, index, words))); });
  50. // 添加两个参照数据,分别表示左上角和右下角
  51. result.push({
  52. text: '',
  53. value: 0,
  54. x: 0,
  55. y: 0,
  56. opacity: 0,
  57. });
  58. result.push({
  59. text: '',
  60. value: 0,
  61. x: options.size[0],
  62. y: options.size[1],
  63. opacity: 0,
  64. });
  65. return result;
  66. }
  67. // 数据准备在外部做,wordCloud 单纯就是做布局
  68. return wordCloud(words, options);
  69. }
  70. /**
  71. * 获取最终的实际绘图尺寸:[width, height]
  72. * @param chart
  73. */
  74. export function getSize(options) {
  75. var width = options.width, height = options.height;
  76. var container = options.container, autoFit = options.autoFit, padding = options.padding, appendPadding = options.appendPadding;
  77. // 由于词云图每个词语的坐标都是先通过 DataSet 根据图表宽高计算出来的,
  78. // 也就是说,如果一开始提供给 DataSet 的宽高信息和最终显示的宽高不相同,
  79. // 那么就会出现布局错乱的情况,所以这里处理的目的就是让一开始提供给 DataSet 的
  80. // 宽高信息与最终显示的宽高信息相同,避免显示错乱。
  81. if (autoFit) {
  82. var containerSize = getContainerSize(container);
  83. width = containerSize.width;
  84. height = containerSize.height;
  85. }
  86. // 宽高不能为 0,否则会造成死循环
  87. width = width || 400;
  88. height = height || 400;
  89. var _a = resolvePadding({ padding: padding, appendPadding: appendPadding }), top = _a[0], right = _a[1], bottom = _a[2], left = _a[3];
  90. var result = [width - (left + right), height - (top + bottom)];
  91. return result;
  92. }
  93. /**
  94. * 根据图表的 padding 和 appendPadding 计算出图表的最终 padding
  95. * @param chart
  96. */
  97. function resolvePadding(options) {
  98. var padding = normalPadding(options.padding);
  99. var appendPadding = normalPadding(options.appendPadding);
  100. var top = padding[0] + appendPadding[0];
  101. var right = padding[1] + appendPadding[1];
  102. var bottom = padding[2] + appendPadding[2];
  103. var left = padding[3] + appendPadding[3];
  104. return [top, right, bottom, left];
  105. }
  106. /**
  107. * 处理 imageMask 可能为 url 字符串的情况
  108. * @param {HTMLImageElement | string} img
  109. * @return {Promise}
  110. */
  111. export function processImageMask(img) {
  112. return new Promise(function (res, rej) {
  113. if (img instanceof HTMLImageElement) {
  114. res(img);
  115. return;
  116. }
  117. if (isString(img)) {
  118. var image_1 = new Image();
  119. image_1.crossOrigin = 'anonymous';
  120. image_1.src = img;
  121. image_1.onload = function () {
  122. res(image_1);
  123. };
  124. image_1.onerror = function () {
  125. log(LEVEL.ERROR, false, 'image %s load failed !!!', img);
  126. rej();
  127. };
  128. return;
  129. }
  130. log(LEVEL.WARN, img === undefined, 'The type of imageMask option must be String or HTMLImageElement.');
  131. rej();
  132. });
  133. }
  134. /**
  135. * 把用户提供的 fontSize 值转换成符合 DataSet 要求的值
  136. * @param options
  137. * @param range
  138. */
  139. export function getFontSizeMapping(fontSize, range) {
  140. if (isFunction(fontSize)) {
  141. return fontSize;
  142. }
  143. if (isArray(fontSize)) {
  144. var fMin_1 = fontSize[0], fMax_1 = fontSize[1];
  145. if (!range) {
  146. return function () { return (fMax_1 + fMin_1) / 2; };
  147. }
  148. var min_1 = range[0], max_1 = range[1];
  149. if (max_1 === min_1) {
  150. return function () { return (fMax_1 + fMin_1) / 2; };
  151. }
  152. return function fontSize(_a) {
  153. var value = _a.value;
  154. return ((fMax_1 - fMin_1) / (max_1 - min_1)) * (value - min_1) + fMin_1;
  155. };
  156. }
  157. return function () { return fontSize; };
  158. }
  159. export function getSingleKeyValues(data, key) {
  160. return data
  161. .map(function (v) { return v[key]; })
  162. .filter(function (v) {
  163. // 过滤非 number
  164. if (typeof v === 'number' && !isNaN(v))
  165. return true;
  166. return false;
  167. });
  168. }
  169. /**
  170. * 把用户提供的关于旋转角度的字段值转换成符合 DataSet 要求的值
  171. * @param options
  172. */
  173. function getRotate(options) {
  174. var _a = resolveRotate(options), rotation = _a.rotation, rotationSteps = _a.rotationSteps;
  175. if (!isArray(rotation))
  176. return rotation;
  177. var min = rotation[0];
  178. var max = rotation[1];
  179. // 等于 1 时不旋转,所以把每份大小设为 0
  180. var perSize = rotationSteps === 1 ? 0 : (max - min) / (rotationSteps - 1);
  181. return function rotate() {
  182. if (max === min)
  183. return max;
  184. return Math.floor(Math.random() * rotationSteps) * perSize;
  185. };
  186. }
  187. /**
  188. * 确保值在要求范围内
  189. * @param options
  190. */
  191. function resolveRotate(options) {
  192. var rotationSteps = options.wordStyle.rotationSteps;
  193. if (rotationSteps < 1) {
  194. log(LEVEL.WARN, false, 'The rotationSteps option must be greater than or equal to 1.');
  195. rotationSteps = 1;
  196. }
  197. return {
  198. rotation: options.wordStyle.rotation,
  199. rotationSteps: rotationSteps,
  200. };
  201. }
  202. /**
  203. * 传入一个元素为数字的数组,
  204. * 返回该数组中值最小的数字。
  205. * @param numbers
  206. */
  207. function min(numbers) {
  208. return Math.min.apply(Math, numbers);
  209. }
  210. /**
  211. * 传入一个元素为数字的数组,
  212. * 返回该数组中值最大的数字。
  213. * @param numbers
  214. */
  215. function max(numbers) {
  216. return Math.max.apply(Math, numbers);
  217. }
  218. //# sourceMappingURL=utils.js.map