liquid.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. import { __assign } from "tslib";
  2. import { registerShape } from '@antv/g2';
  3. import { isNumber, mix, reduce } from '@antv/util';
  4. import { transform } from '../../../utils/matrix';
  5. var DURATION = 5000;
  6. /**
  7. * 一个线性映射的函数
  8. * @param min
  9. * @param max
  10. * @param factor
  11. */
  12. function lerp(min, max, factor) {
  13. return min + (max - min) * factor;
  14. }
  15. /**
  16. * 波浪的 attrs
  17. * @param cfg
  18. */
  19. function getFillAttrs(cfg) {
  20. var attrs = __assign({ opacity: 1 }, cfg.style);
  21. if (cfg.color && !attrs.fill) {
  22. attrs.fill = cfg.color;
  23. }
  24. return attrs;
  25. }
  26. /**
  27. * shape 的 attrs
  28. * @param cfg
  29. */
  30. function getLineAttrs(cfg) {
  31. var defaultAttrs = {
  32. fill: '#fff',
  33. fillOpacity: 0,
  34. lineWidth: 4,
  35. };
  36. var attrs = mix({}, defaultAttrs, cfg.style);
  37. if (cfg.color && !attrs.stroke) {
  38. attrs.stroke = cfg.color;
  39. }
  40. if (isNumber(cfg.opacity)) {
  41. attrs.opacity = attrs.strokeOpacity = cfg.opacity;
  42. }
  43. return attrs;
  44. }
  45. /**
  46. * 用贝塞尔曲线模拟正弦波
  47. * Using Bezier curves to fit sine wave.
  48. * There is 4 control points for each curve of wave,
  49. * which is at 1/4 wave length of the sine wave.
  50. *
  51. * The control points for a wave from (a) to (d) are a-b-c-d:
  52. * c *----* d
  53. * b *
  54. * |
  55. * ... a * ..................
  56. *
  57. * whose positions are a: (0, 0), b: (0.5, 0.5), c: (1, 1), d: (PI / 2, 1)
  58. *
  59. * @param x x position of the left-most point (a)
  60. * @param stage 0-3, stating which part of the wave it is
  61. * @param waveLength wave length of the sine wave
  62. * @param amplitude wave amplitude
  63. * @return 正弦片段曲线
  64. */
  65. function getWaterWavePositions(x, stage, waveLength, amplitude) {
  66. if (stage === 0) {
  67. return [
  68. [x + ((1 / 2) * waveLength) / Math.PI / 2, amplitude / 2],
  69. [x + ((1 / 2) * waveLength) / Math.PI, amplitude],
  70. [x + waveLength / 4, amplitude],
  71. ];
  72. }
  73. if (stage === 1) {
  74. return [
  75. [x + (((1 / 2) * waveLength) / Math.PI / 2) * (Math.PI - 2), amplitude],
  76. [x + (((1 / 2) * waveLength) / Math.PI / 2) * (Math.PI - 1), amplitude / 2],
  77. [x + waveLength / 4, 0],
  78. ];
  79. }
  80. if (stage === 2) {
  81. return [
  82. [x + ((1 / 2) * waveLength) / Math.PI / 2, -amplitude / 2],
  83. [x + ((1 / 2) * waveLength) / Math.PI, -amplitude],
  84. [x + waveLength / 4, -amplitude],
  85. ];
  86. }
  87. return [
  88. [x + (((1 / 2) * waveLength) / Math.PI / 2) * (Math.PI - 2), -amplitude],
  89. [x + (((1 / 2) * waveLength) / Math.PI / 2) * (Math.PI - 1), -amplitude / 2],
  90. [x + waveLength / 4, 0],
  91. ];
  92. }
  93. /**
  94. * 获取水波路径
  95. * @param radius 半径
  96. * @param waterLevel 水位
  97. * @param waveLength 波长
  98. * @param phase 相位
  99. * @param amplitude 震幅
  100. * @param cx 圆心x
  101. * @param cy 圆心y
  102. * @return path 路径
  103. * @reference http://gitlab.alipay-inc.com/datavis/g6/blob/1.2.0/src/graph/utils/path.js#L135
  104. */
  105. function getWaterWavePath(radius, waterLevel, waveLength, phase, amplitude, cx, cy) {
  106. var curves = Math.ceil(((2 * radius) / waveLength) * 4) * 4;
  107. var path = [];
  108. var _phase = phase;
  109. // map phase to [-Math.PI * 2, 0]
  110. while (_phase < -Math.PI * 2) {
  111. _phase += Math.PI * 2;
  112. }
  113. while (_phase > 0) {
  114. _phase -= Math.PI * 2;
  115. }
  116. _phase = (_phase / Math.PI / 2) * waveLength;
  117. var left = cx - radius + _phase - radius * 2;
  118. /**
  119. * top-left corner as start point
  120. *
  121. * draws this point
  122. * |
  123. * \|/
  124. * ~~~~~~~~
  125. * | |
  126. * +------+
  127. */
  128. path.push(['M', left, waterLevel]);
  129. /**
  130. * top wave
  131. *
  132. * ~~~~~~~~ <- draws this sine wave
  133. * | |
  134. * +------+
  135. */
  136. var waveRight = 0;
  137. for (var c = 0; c < curves; ++c) {
  138. var stage = c % 4;
  139. var pos = getWaterWavePositions((c * waveLength) / 4, stage, waveLength, amplitude);
  140. path.push([
  141. 'C',
  142. pos[0][0] + left,
  143. -pos[0][1] + waterLevel,
  144. pos[1][0] + left,
  145. -pos[1][1] + waterLevel,
  146. pos[2][0] + left,
  147. -pos[2][1] + waterLevel,
  148. ]);
  149. if (c === curves - 1) {
  150. waveRight = pos[2][0];
  151. }
  152. }
  153. /**
  154. * top-right corner
  155. *
  156. * ~~~~~~~~
  157. * 3. draws this line -> | | <- 1. draws this line
  158. * +------+
  159. * ^
  160. * |
  161. * 2. draws this line
  162. */
  163. path.push(['L', waveRight + left, cy + radius]);
  164. path.push(['L', left, cy + radius]);
  165. path.push(['Z']);
  166. // path.push(['L', left, waterLevel]);
  167. return path;
  168. }
  169. /**
  170. * 添加水波
  171. * @param x 中心x
  172. * @param y 中心y
  173. * @param level 水位等级 0~1
  174. * @param waveCount 水波数
  175. * @param waveAttrs 色值
  176. * @param group 图组
  177. * @param clip 用于剪切的图形
  178. * @param radius 绘制图形的高度
  179. * @param waveLength 波的长度
  180. */
  181. export function addWaterWave(x, y, level, waveCount, waveAttrs, group, clip, radius, waveLength, animation) {
  182. // 盒子属性 颜色 宽高
  183. var fill = waveAttrs.fill, opacity = waveAttrs.opacity;
  184. var bbox = clip.getBBox();
  185. var width = bbox.maxX - bbox.minX;
  186. var height = bbox.maxY - bbox.minY;
  187. // 循环 waveCount 个数
  188. for (var idx = 0; idx < waveCount; idx++) {
  189. var factor = waveCount <= 1 ? 1 : idx / (waveCount - 1);
  190. // 画波
  191. var wave = group.addShape('path', {
  192. name: "waterwave-path",
  193. attrs: {
  194. // 波形路径配置
  195. path: getWaterWavePath(radius, bbox.minY + height * level, waveLength, 0, width / 32, // 波幅高度
  196. x, y),
  197. fill: fill,
  198. opacity: lerp(0.2, 0.9, factor) * opacity,
  199. },
  200. });
  201. try {
  202. // 默认 underfind 开启动画
  203. if (animation === false)
  204. return;
  205. var matrix = transform([['t', waveLength, 0]]);
  206. wave.stopAnimate();
  207. wave.animate({ matrix: matrix }, {
  208. duration: lerp(0.5 * DURATION, DURATION, factor),
  209. repeat: true,
  210. });
  211. }
  212. catch (e) {
  213. // TODO off-screen canvas 中动画会找不到 canvas
  214. console.warn('off-screen group animate error!');
  215. }
  216. }
  217. }
  218. /**
  219. *
  220. * @param x 中心 x
  221. * @param y 中心 y
  222. * @param width 外接矩形的宽
  223. * @param height 外接矩形的高
  224. */
  225. function pin(x, y, width, height) {
  226. var w = (width * 2) / 3;
  227. var h = Math.max(w, height);
  228. var r = w / 2;
  229. // attrs of the upper circle
  230. var cx = x;
  231. var cy = r + y - h / 2;
  232. var theta = Math.asin(r / ((h - r) * 0.85));
  233. var dy = Math.sin(theta) * r;
  234. var dx = Math.cos(theta) * r;
  235. // the start point of the path
  236. var x0 = cx - dx;
  237. var y0 = cy + dy;
  238. // control point
  239. var cpX = x;
  240. var cpY = cy + r / Math.sin(theta);
  241. return "\n M ".concat(x0, " ").concat(y0, "\n A ").concat(r, " ").concat(r, " 0 1 1 ").concat(x0 + dx * 2, " ").concat(y0, "\n Q ").concat(cpX, " ").concat(cpY, " ").concat(x, " ").concat(y + h / 2, "\n Q ").concat(cpX, " ").concat(cpY, " ").concat(x0, " ").concat(y0, "\n Z \n ");
  242. }
  243. /**
  244. *
  245. * @param x 中心 x
  246. * @param y 中心 y
  247. * @param width 外接矩形的宽
  248. * @param height 外接矩形的高
  249. */
  250. function circle(x, y, width, height) {
  251. var rx = width / 2;
  252. var ry = height / 2;
  253. return "\n M ".concat(x, " ").concat(y - ry, " \n a ").concat(rx, " ").concat(ry, " 0 1 0 0 ").concat(ry * 2, "\n a ").concat(rx, " ").concat(ry, " 0 1 0 0 ").concat(-ry * 2, "\n Z\n ");
  254. }
  255. /**
  256. *
  257. * @param x 中心 x
  258. * @param y 中心 y
  259. * @param width 外接矩形的宽
  260. * @param height 外接矩形的高
  261. */
  262. function diamond(x, y, width, height) {
  263. var h = height / 2;
  264. var w = width / 2;
  265. return "\n M ".concat(x, " ").concat(y - h, "\n L ").concat(x + w, " ").concat(y, "\n L ").concat(x, " ").concat(y + h, "\n L ").concat(x - w, " ").concat(y, "\n Z\n ");
  266. }
  267. /**
  268. *
  269. * @param x 中心 x
  270. * @param y 中心 y
  271. * @param width 外接矩形的宽
  272. * @param height 外接矩形的高
  273. */
  274. function triangle(x, y, width, height) {
  275. var h = height / 2;
  276. var w = width / 2;
  277. return "\n M ".concat(x, " ").concat(y - h, "\n L ").concat(x + w, " ").concat(y + h, "\n L ").concat(x - w, " ").concat(y + h, "\n Z\n ");
  278. }
  279. /**
  280. *
  281. * @param x 中心 x
  282. * @param y 中心 y
  283. * @param width 外接矩形的宽
  284. * @param height 外接矩形的高
  285. */
  286. function rect(x, y, width, height) {
  287. var GOLDEN_SECTION_RATIO = 0.618;
  288. var h = height / 2;
  289. var w = (width / 2) * GOLDEN_SECTION_RATIO;
  290. return "\n M ".concat(x - w, " ").concat(y - h, "\n L ").concat(x + w, " ").concat(y - h, "\n L ").concat(x + w, " ").concat(y + h, "\n L ").concat(x - w, " ").concat(y + h, "\n Z\n ");
  291. }
  292. var builtInShapeByName = {
  293. pin: pin,
  294. circle: circle,
  295. diamond: diamond,
  296. triangle: triangle,
  297. rect: rect,
  298. };
  299. registerShape('interval', 'liquid-fill-gauge', {
  300. draw: function (cfg, container) {
  301. var cx = 0.5;
  302. var cy = 0.5;
  303. var customInfo = cfg.customInfo;
  304. var _a = customInfo, percent = _a.percent, radio = _a.radius, shape = _a.shape, shapeStyle = _a.shapeStyle, background = _a.background, animation = _a.animation;
  305. var outline = customInfo.outline;
  306. var wave = customInfo.wave;
  307. var border = outline.border, distance = outline.distance;
  308. var waveCount = wave.count, waveLength = wave.length;
  309. // 获取最小 minX
  310. var minX = reduce(cfg.points, function (r, p) {
  311. return Math.min(r, p.x);
  312. }, Infinity);
  313. var center = this.parsePoint({ x: cx, y: cy });
  314. var minXPoint = this.parsePoint({ x: minX, y: cy });
  315. var halfWidth = center.x - minXPoint.x;
  316. // 保证半径是 画布宽高最小值的 radius 值
  317. var radius = Math.min(halfWidth, minXPoint.y * radio);
  318. var waveAttrs = getFillAttrs(cfg);
  319. var outlineAttrs = getLineAttrs(mix({}, cfg, outline));
  320. var innerRadius = radius - border / 2;
  321. var buildPath = typeof shape === 'function' ? shape : builtInShapeByName[shape] || builtInShapeByName['circle'];
  322. var shapePath = buildPath(center.x, center.y, innerRadius * 2, innerRadius * 2);
  323. // 1. 当 shapeStyle 不为空时,绘制形状样式作为背景
  324. if (shapeStyle) {
  325. container.addShape('path', {
  326. name: 'shape',
  327. attrs: __assign({ path: shapePath }, shapeStyle),
  328. });
  329. }
  330. // 比例大于 0 时才绘制水波
  331. if (percent > 0) {
  332. // 2. 绘制一个波
  333. var waves = container.addGroup({
  334. name: 'waves',
  335. });
  336. // 3. 波对应的 clip 裁剪形状
  337. var clipPath = waves.setClip({
  338. type: 'path',
  339. attrs: {
  340. path: shapePath,
  341. },
  342. });
  343. // 4. 绘制波形
  344. addWaterWave(center.x, center.y, 1 - cfg.points[1].y, waveCount, waveAttrs, waves, clipPath, radius * 2, waveLength, animation);
  345. }
  346. // 5. 绘制一个 distance 宽的 border
  347. container.addShape('path', {
  348. name: 'distance',
  349. attrs: {
  350. path: shapePath,
  351. fill: 'transparent',
  352. lineWidth: border + distance * 2,
  353. stroke: background === 'transparent' ? '#fff' : background,
  354. },
  355. });
  356. // 6. 绘制一个 border 宽的 border
  357. container.addShape('path', {
  358. name: 'wrap',
  359. attrs: mix(outlineAttrs, {
  360. path: shapePath,
  361. fill: 'transparent',
  362. lineWidth: border,
  363. }),
  364. });
  365. return container;
  366. },
  367. });
  368. //# sourceMappingURL=liquid.js.map