slider.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. import { __assign, __extends } from "tslib";
  2. import { clamp, deepMix, each, get, isArray, isNil, size } from '@antv/util';
  3. import GroupComponent from '../abstract/group-component';
  4. import { Trend } from '../trend/trend';
  5. import { DEFAULT_HANDLER_STYLE, Handler } from './handler';
  6. import { BACKGROUND_STYLE, DEFAULT_HANDLER_WIDTH, FOREGROUND_STYLE, HANDLER_STYLE, SLIDER_CHANGE, TEXT_STYLE, } from './constant';
  7. var Slider = /** @class */ (function (_super) {
  8. __extends(Slider, _super);
  9. function Slider() {
  10. var _this = _super !== null && _super.apply(this, arguments) || this;
  11. _this.onMouseDown = function (target) { return function (e) {
  12. _this.currentTarget = target;
  13. // 取出原生事件
  14. var event = e.originalEvent;
  15. // 2. 存储当前点击位置
  16. event.stopPropagation();
  17. event.preventDefault();
  18. // 兼容移动端获取数据
  19. _this.prevX = get(event, 'touches.0.pageX', event.pageX);
  20. _this.prevY = get(event, 'touches.0.pageY', event.pageY);
  21. // 3. 开始滑动的时候,绑定 move 和 up 事件
  22. var containerDOM = _this.getContainerDOM();
  23. containerDOM.addEventListener('mousemove', _this.onMouseMove);
  24. containerDOM.addEventListener('mouseup', _this.onMouseUp);
  25. containerDOM.addEventListener('mouseleave', _this.onMouseUp);
  26. // 移动端事件
  27. containerDOM.addEventListener('touchmove', _this.onMouseMove);
  28. containerDOM.addEventListener('touchend', _this.onMouseUp);
  29. containerDOM.addEventListener('touchcancel', _this.onMouseUp);
  30. }; };
  31. _this.onMouseMove = function (event) {
  32. var width = _this.cfg.width;
  33. var originValue = [_this.get('start'), _this.get('end')];
  34. // 滑动过程中,计算偏移,更新滑块,然后 emit 数据出去
  35. event.stopPropagation();
  36. event.preventDefault();
  37. var x = get(event, 'touches.0.pageX', event.pageX);
  38. var y = get(event, 'touches.0.pageY', event.pageY);
  39. // 横向的 slider 只处理 x
  40. var offsetX = x - _this.prevX;
  41. var offsetXRange = _this.adjustOffsetRange(offsetX / width);
  42. // 更新 start end range 范围
  43. _this.updateStartEnd(offsetXRange);
  44. // 更新 ui
  45. _this.updateUI(_this.getElementByLocalId('foreground'), _this.getElementByLocalId('minText'), _this.getElementByLocalId('maxText'));
  46. _this.prevX = x;
  47. _this.prevY = y;
  48. _this.draw();
  49. // 因为存储的 start、end 可能不一定是按大小存储的,所以排序一下,对外是 end >= start
  50. _this.emit(SLIDER_CHANGE, [_this.get('start'), _this.get('end')].sort());
  51. _this.delegateEmit('valuechanged', {
  52. originValue: originValue,
  53. value: [_this.get('start'), _this.get('end')],
  54. });
  55. };
  56. _this.onMouseUp = function () {
  57. // 结束之后,取消绑定的事件
  58. if (_this.currentTarget) {
  59. _this.currentTarget = undefined;
  60. }
  61. var containerDOM = _this.getContainerDOM();
  62. if (containerDOM) {
  63. containerDOM.removeEventListener('mousemove', _this.onMouseMove);
  64. containerDOM.removeEventListener('mouseup', _this.onMouseUp);
  65. // 防止滑动到 canvas 外部之后,状态丢失
  66. containerDOM.removeEventListener('mouseleave', _this.onMouseUp);
  67. // 移动端事件
  68. containerDOM.removeEventListener('touchmove', _this.onMouseMove);
  69. containerDOM.removeEventListener('touchend', _this.onMouseUp);
  70. containerDOM.removeEventListener('touchcancel', _this.onMouseUp);
  71. }
  72. };
  73. return _this;
  74. }
  75. Slider.prototype.setRange = function (min, max) {
  76. this.set('minLimit', min);
  77. this.set('maxLimit', max);
  78. var oldStart = this.get('start');
  79. var oldEnd = this.get('end');
  80. var newStart = clamp(oldStart, min, max);
  81. var newEnd = clamp(oldEnd, min, max);
  82. if (!this.get('isInit') && (oldStart !== newStart || oldEnd !== newEnd)) {
  83. this.setValue([newStart, newEnd]);
  84. }
  85. };
  86. Slider.prototype.getRange = function () {
  87. return {
  88. min: this.get('minLimit') || 0,
  89. max: this.get('maxLimit') || 1,
  90. };
  91. };
  92. Slider.prototype.setValue = function (value) {
  93. var range = this.getRange();
  94. if (isArray(value) && value.length === 2) {
  95. var originValue = [this.get('start'), this.get('end')];
  96. this.update({
  97. start: clamp(value[0], range.min, range.max),
  98. end: clamp(value[1], range.min, range.max),
  99. });
  100. if (!this.get('updateAutoRender')) {
  101. this.render();
  102. }
  103. this.delegateEmit('valuechanged', {
  104. originValue: originValue,
  105. value: value,
  106. });
  107. }
  108. };
  109. Slider.prototype.getValue = function () {
  110. return [this.get('start'), this.get('end')];
  111. };
  112. Slider.prototype.getDefaultCfg = function () {
  113. var cfg = _super.prototype.getDefaultCfg.call(this);
  114. return __assign(__assign({}, cfg), { name: 'slider', x: 0, y: 0, width: 100, height: 16, backgroundStyle: {}, foregroundStyle: {}, handlerStyle: {}, textStyle: {}, defaultCfg: {
  115. backgroundStyle: BACKGROUND_STYLE,
  116. foregroundStyle: FOREGROUND_STYLE,
  117. handlerStyle: HANDLER_STYLE,
  118. textStyle: TEXT_STYLE,
  119. } });
  120. };
  121. Slider.prototype.update = function (cfg) {
  122. var start = cfg.start, end = cfg.end;
  123. var validCfg = __assign({}, cfg);
  124. if (!isNil(start)) {
  125. validCfg.start = clamp(start, 0, 1);
  126. }
  127. if (!isNil(end)) {
  128. validCfg.end = clamp(end, 0, 1);
  129. }
  130. _super.prototype.update.call(this, validCfg);
  131. this.minHandler = this.getChildComponentById(this.getElementId('minHandler'));
  132. this.maxHandler = this.getChildComponentById(this.getElementId('maxHandler'));
  133. this.trend = this.getChildComponentById(this.getElementId('trend'));
  134. };
  135. Slider.prototype.init = function () {
  136. this.set('start', clamp(this.get('start'), 0, 1));
  137. this.set('end', clamp(this.get('end'), 0, 1));
  138. _super.prototype.init.call(this);
  139. };
  140. Slider.prototype.render = function () {
  141. _super.prototype.render.call(this);
  142. this.updateUI(this.getElementByLocalId('foreground'), this.getElementByLocalId('minText'), this.getElementByLocalId('maxText'));
  143. };
  144. Slider.prototype.renderInner = function (group) {
  145. var _a = this.cfg, start = _a.start, end = _a.end, width = _a.width, height = _a.height, _b = _a.trendCfg, trendCfg = _b === void 0 ? {} : _b, minText = _a.minText, maxText = _a.maxText, _c = _a.backgroundStyle, backgroundStyle = _c === void 0 ? {} : _c, _d = _a.foregroundStyle, foregroundStyle = _d === void 0 ? {} : _d, _e = _a.textStyle, textStyle = _e === void 0 ? {} : _e;
  146. var handlerStyle = deepMix({}, DEFAULT_HANDLER_STYLE, this.cfg.handlerStyle);
  147. var min = start * width;
  148. var max = end * width;
  149. // 趋势图数据
  150. if (size(get(trendCfg, 'data'))) {
  151. this.trend = this.addComponent(group, __assign({ component: Trend, id: this.getElementId('trend'), x: 0, y: 0, width: width,
  152. height: height }, trendCfg));
  153. }
  154. // 1. 背景
  155. this.addShape(group, {
  156. id: this.getElementId('background'),
  157. type: 'rect',
  158. attrs: __assign({ x: 0, y: 0, width: width,
  159. height: height }, backgroundStyle),
  160. });
  161. // 2. 左右文字
  162. var minTextShape = this.addShape(group, {
  163. id: this.getElementId('minText'),
  164. type: 'text',
  165. attrs: __assign({
  166. // x: 0,
  167. y: height / 2, textAlign: 'right', text: minText, silent: false }, textStyle),
  168. });
  169. var maxTextShape = this.addShape(group, {
  170. id: this.getElementId('maxText'),
  171. type: 'text',
  172. attrs: __assign({
  173. // x: 0,
  174. y: height / 2, textAlign: 'left', text: maxText, silent: false }, textStyle),
  175. });
  176. // 3. 前景 选中背景框
  177. var foregroundShape = this.addShape(group, {
  178. id: this.getElementId('foreground'),
  179. name: 'foreground',
  180. type: 'rect',
  181. attrs: __assign({
  182. // x: 0,
  183. y: 0,
  184. // width: 0,
  185. height: height }, foregroundStyle),
  186. });
  187. // 滑块相关的大小信息
  188. var handlerWidth = get(handlerStyle, 'width', DEFAULT_HANDLER_WIDTH);
  189. var handlerHeight = get(handlerStyle, 'height', 24);
  190. // 4. 左右滑块
  191. this.minHandler = this.addComponent(group, {
  192. component: Handler,
  193. id: this.getElementId('minHandler'),
  194. name: 'handler-min',
  195. x: 0,
  196. y: (height - handlerHeight) / 2,
  197. width: handlerWidth,
  198. height: handlerHeight,
  199. cursor: 'ew-resize',
  200. style: handlerStyle,
  201. });
  202. this.maxHandler = this.addComponent(group, {
  203. component: Handler,
  204. id: this.getElementId('maxHandler'),
  205. name: 'handler-max',
  206. x: 0,
  207. y: (height - handlerHeight) / 2,
  208. width: handlerWidth,
  209. height: handlerHeight,
  210. cursor: 'ew-resize',
  211. style: handlerStyle,
  212. });
  213. };
  214. Slider.prototype.applyOffset = function () {
  215. this.moveElementTo(this.get('group'), {
  216. x: this.get('x'),
  217. y: this.get('y'),
  218. });
  219. };
  220. Slider.prototype.initEvent = function () {
  221. this.bindEvents();
  222. };
  223. Slider.prototype.updateUI = function (foregroundShape, minTextShape, maxTextShape) {
  224. var _a = this.cfg, start = _a.start, end = _a.end, width = _a.width, minText = _a.minText, maxText = _a.maxText, handlerStyle = _a.handlerStyle, height = _a.height;
  225. var min = start * width;
  226. var max = end * width;
  227. if (this.trend) {
  228. this.trend.update({
  229. width: width,
  230. height: height,
  231. });
  232. if (!this.get('updateAutoRender')) {
  233. this.trend.render();
  234. }
  235. }
  236. // 1. foreground
  237. foregroundShape.attr('x', min);
  238. foregroundShape.attr('width', max - min);
  239. // 滑块相关的大小信息
  240. var handlerWidth = get(handlerStyle, 'width', DEFAULT_HANDLER_WIDTH);
  241. // 设置文本
  242. minTextShape.attr('text', minText);
  243. maxTextShape.attr('text', maxText);
  244. var _b = this._dodgeText([min, max], minTextShape, maxTextShape), minAttrs = _b[0], maxAttrs = _b[1];
  245. // 2. 左侧滑块和文字位置
  246. if (this.minHandler) {
  247. this.minHandler.update({
  248. x: min - handlerWidth / 2,
  249. });
  250. if (!this.get('updateAutoRender')) {
  251. this.minHandler.render();
  252. }
  253. }
  254. each(minAttrs, function (v, k) { return minTextShape.attr(k, v); });
  255. // 3. 右侧滑块和文字位置
  256. if (this.maxHandler) {
  257. this.maxHandler.update({
  258. x: max - handlerWidth / 2,
  259. });
  260. if (!this.get('updateAutoRender')) {
  261. this.maxHandler.render();
  262. }
  263. }
  264. each(maxAttrs, function (v, k) { return maxTextShape.attr(k, v); });
  265. };
  266. Slider.prototype.bindEvents = function () {
  267. var group = this.get('group');
  268. group.on('handler-min:mousedown', this.onMouseDown('minHandler'));
  269. group.on('handler-min:touchstart', this.onMouseDown('minHandler'));
  270. // 2. 右滑块的滑动
  271. group.on('handler-max:mousedown', this.onMouseDown('maxHandler'));
  272. group.on('handler-max:touchstart', this.onMouseDown('maxHandler'));
  273. // 3. 前景选中区域
  274. var foreground = group.findById(this.getElementId('foreground'));
  275. foreground.on('mousedown', this.onMouseDown('foreground'));
  276. foreground.on('touchstart', this.onMouseDown('foreground'));
  277. };
  278. /**
  279. * 调整 offsetRange,因为一些范围的限制
  280. * @param offsetRange
  281. */
  282. Slider.prototype.adjustOffsetRange = function (offsetRange) {
  283. var _a = this.cfg, start = _a.start, end = _a.end;
  284. // 针对不同的滑动组件,处理的方式不同
  285. switch (this.currentTarget) {
  286. case 'minHandler': {
  287. var min = 0 - start;
  288. var max = 1 - start;
  289. return Math.min(max, Math.max(min, offsetRange));
  290. }
  291. case 'maxHandler': {
  292. var min = 0 - end;
  293. var max = 1 - end;
  294. return Math.min(max, Math.max(min, offsetRange));
  295. }
  296. case 'foreground': {
  297. var min = 0 - start;
  298. var max = 1 - end;
  299. return Math.min(max, Math.max(min, offsetRange));
  300. }
  301. }
  302. };
  303. Slider.prototype.updateStartEnd = function (offsetRange) {
  304. var _a = this.cfg, start = _a.start, end = _a.end;
  305. // 操作不同的组件,反馈不一样
  306. switch (this.currentTarget) {
  307. case 'minHandler':
  308. start += offsetRange;
  309. break;
  310. case 'maxHandler':
  311. end += offsetRange;
  312. break;
  313. case 'foreground':
  314. start += offsetRange;
  315. end += offsetRange;
  316. break;
  317. }
  318. this.set('start', start);
  319. this.set('end', end);
  320. };
  321. /**
  322. * 调整 text 的位置,自动躲避
  323. * 根据位置,调整返回新的位置
  324. * @param range
  325. */
  326. Slider.prototype._dodgeText = function (range, minTextShape, maxTextShape) {
  327. var _a, _b;
  328. var _c = this.cfg, handlerStyle = _c.handlerStyle, width = _c.width;
  329. var PADDING = 2;
  330. var handlerWidth = get(handlerStyle, 'width', DEFAULT_HANDLER_WIDTH);
  331. var min = range[0], max = range[1];
  332. var sorted = false;
  333. // 如果交换了位置,则对应的 min max 也交互
  334. if (min > max) {
  335. _a = [max, min], min = _a[0], max = _a[1];
  336. _b = [maxTextShape, minTextShape], minTextShape = _b[0], maxTextShape = _b[1];
  337. sorted = true;
  338. }
  339. // 避让规则,优先显示在两侧,只有显示不下的时候,才显示在中间
  340. var minBBox = minTextShape.getBBox();
  341. var maxBBox = maxTextShape.getBBox();
  342. var minAttrs = minBBox.width > min - PADDING
  343. ? { x: min + handlerWidth / 2 + PADDING, textAlign: 'left' }
  344. : { x: min - handlerWidth / 2 - PADDING, textAlign: 'right' };
  345. var maxAttrs = maxBBox.width > width - max - PADDING
  346. ? { x: max - handlerWidth / 2 - PADDING, textAlign: 'right' }
  347. : { x: max + handlerWidth / 2 + PADDING, textAlign: 'left' };
  348. return !sorted ? [minAttrs, maxAttrs] : [maxAttrs, minAttrs];
  349. };
  350. Slider.prototype.draw = function () {
  351. var container = this.get('container');
  352. var canvas = container && container.get('canvas');
  353. if (canvas) {
  354. canvas.draw();
  355. }
  356. };
  357. Slider.prototype.getContainerDOM = function () {
  358. var container = this.get('container');
  359. var canvas = container && container.get('canvas');
  360. return canvas && canvas.get('container');
  361. };
  362. return Slider;
  363. }(GroupComponent));
  364. export { Slider };
  365. export default Slider;
  366. //# sourceMappingURL=slider.js.map