SliderZoomView.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. /**
  20. * AUTO-GENERATED FILE. DO NOT MODIFY.
  21. */
  22. /*
  23. * Licensed to the Apache Software Foundation (ASF) under one
  24. * or more contributor license agreements. See the NOTICE file
  25. * distributed with this work for additional information
  26. * regarding copyright ownership. The ASF licenses this file
  27. * to you under the Apache License, Version 2.0 (the
  28. * "License"); you may not use this file except in compliance
  29. * with the License. You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing,
  34. * software distributed under the License is distributed on an
  35. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36. * KIND, either express or implied. See the License for the
  37. * specific language governing permissions and limitations
  38. * under the License.
  39. */
  40. import { __extends } from "tslib";
  41. import { bind, each, isFunction, isString, indexOf } from 'zrender/lib/core/util.js';
  42. import * as eventTool from 'zrender/lib/core/event.js';
  43. import * as graphic from '../../util/graphic.js';
  44. import * as throttle from '../../util/throttle.js';
  45. import DataZoomView from './DataZoomView.js';
  46. import { linearMap, asc, parsePercent, round } from '../../util/number.js';
  47. import * as layout from '../../util/layout.js';
  48. import sliderMove from '../helper/sliderMove.js';
  49. import { getAxisMainType, collectReferCoordSysModelInfo, getAlignTo } from './helper.js';
  50. import { enableHoverEmphasis } from '../../util/states.js';
  51. import { createSymbol, symbolBuildProxies } from '../../util/symbol.js';
  52. import { deprecateLog } from '../../util/log.js';
  53. import { createTextStyle } from '../../label/labelStyle.js';
  54. import tokens from '../../visual/tokens.js';
  55. import { isOrdinalScale, isTimeScale } from '../../scale/helper.js';
  56. var Rect = graphic.Rect;
  57. // Constants
  58. var DEFAULT_FRAME_BORDER_WIDTH = 1;
  59. var DEFAULT_FILLER_SIZE = 30;
  60. var DEFAULT_MOVE_HANDLE_SIZE = 7;
  61. var HORIZONTAL = 'horizontal';
  62. var VERTICAL = 'vertical';
  63. var LABEL_GAP = 5;
  64. var SHOW_DATA_SHADOW_SERIES_TYPE = ['line', 'bar', 'candlestick', 'scatter'];
  65. var REALTIME_ANIMATION_CONFIG = {
  66. easing: 'cubicOut',
  67. duration: 100,
  68. delay: 0
  69. };
  70. var SliderZoomView = /** @class */function (_super) {
  71. __extends(SliderZoomView, _super);
  72. function SliderZoomView() {
  73. var _this = _super !== null && _super.apply(this, arguments) || this;
  74. _this.type = SliderZoomView.type;
  75. _this._displayables = {};
  76. return _this;
  77. }
  78. SliderZoomView.prototype.init = function (ecModel, api) {
  79. this.api = api;
  80. // A unique handler for each dataZoom component
  81. this._onBrush = bind(this._onBrush, this);
  82. this._onBrushEnd = bind(this._onBrushEnd, this);
  83. };
  84. SliderZoomView.prototype.render = function (dataZoomModel, ecModel, api, payload) {
  85. _super.prototype.render.apply(this, arguments);
  86. throttle.createOrUpdate(this, '_dispatchZoomAction', dataZoomModel.get('throttle'), 'fixRate');
  87. this._orient = dataZoomModel.getOrient();
  88. if (dataZoomModel.get('show') === false) {
  89. this.group.removeAll();
  90. return;
  91. }
  92. if (dataZoomModel.noTarget()) {
  93. this._clear();
  94. this.group.removeAll();
  95. return;
  96. }
  97. // Notice: this._resetInterval() should not be executed when payload.type
  98. // is 'dataZoom', origin this._range should be maintained, otherwise 'pan'
  99. // or 'zoom' info will be missed because of 'throttle' of this.dispatchAction,
  100. if (!payload || payload.type !== 'dataZoom' || payload.from !== this.uid) {
  101. this._buildView();
  102. }
  103. this._updateView();
  104. };
  105. SliderZoomView.prototype.dispose = function () {
  106. this._clear();
  107. _super.prototype.dispose.apply(this, arguments);
  108. };
  109. SliderZoomView.prototype._clear = function () {
  110. throttle.clear(this, '_dispatchZoomAction');
  111. var zr = this.api.getZr();
  112. zr.off('mousemove', this._onBrush);
  113. zr.off('mouseup', this._onBrushEnd);
  114. };
  115. SliderZoomView.prototype._buildView = function () {
  116. var thisGroup = this.group;
  117. thisGroup.removeAll();
  118. this._brushing = false;
  119. this._displayables.brushRect = null;
  120. this._resetLocation();
  121. this._resetInterval();
  122. var barGroup = this._displayables.sliderGroup = new graphic.Group();
  123. this._renderBackground();
  124. this._renderHandle();
  125. this._renderDataShadow();
  126. thisGroup.add(barGroup);
  127. this._positionGroup();
  128. };
  129. SliderZoomView.prototype._resetLocation = function () {
  130. var dataZoomModel = this.dataZoomModel;
  131. var api = this.api;
  132. var showMoveHandle = dataZoomModel.get('brushSelect');
  133. var moveHandleSize = showMoveHandle ? DEFAULT_MOVE_HANDLE_SIZE : 0;
  134. var refContainer = layout.createBoxLayoutReference(dataZoomModel, api).refContainer;
  135. // If some of x/y/width/height are not specified,
  136. // auto-adapt according to target grid.
  137. var coordRect = this._findCoordRect();
  138. var edgeGap = dataZoomModel.get('defaultLocationEdgeGap', true) || 0;
  139. // Default align by coordinate system rect.
  140. var positionInfo = this._orient === HORIZONTAL ? {
  141. // Why using 'right', because right should be used in vertical,
  142. // and it is better to be consistent for dealing with position param merge.
  143. right: refContainer.width - coordRect.x - coordRect.width,
  144. top: refContainer.height - DEFAULT_FILLER_SIZE - edgeGap - moveHandleSize,
  145. width: coordRect.width,
  146. height: DEFAULT_FILLER_SIZE
  147. } : {
  148. right: edgeGap,
  149. top: coordRect.y,
  150. width: DEFAULT_FILLER_SIZE,
  151. height: coordRect.height
  152. };
  153. // Do not write back to option and replace value 'ph', because
  154. // the 'ph' value should be recalculated when resize.
  155. var layoutParams = layout.getLayoutParams(dataZoomModel.option);
  156. // Replace the placeholder value.
  157. each(['right', 'top', 'width', 'height'], function (name) {
  158. if (layoutParams[name] === 'ph') {
  159. layoutParams[name] = positionInfo[name];
  160. }
  161. });
  162. var layoutRect = layout.getLayoutRect(layoutParams, refContainer);
  163. this._location = {
  164. x: layoutRect.x,
  165. y: layoutRect.y
  166. };
  167. this._size = [layoutRect.width, layoutRect.height];
  168. this._orient === VERTICAL && this._size.reverse();
  169. };
  170. SliderZoomView.prototype._positionGroup = function () {
  171. var thisGroup = this.group;
  172. var location = this._location;
  173. var orient = this._orient;
  174. // Just use the first axis to determine mapping.
  175. var targetAxisModel = this.dataZoomModel.getFirstTargetAxisModel();
  176. var inverse = targetAxisModel && targetAxisModel.get('inverse');
  177. var sliderGroup = this._displayables.sliderGroup;
  178. var otherAxisInverse = (this._dataShadowInfo || {}).otherAxisInverse;
  179. // Transform barGroup.
  180. sliderGroup.attr(orient === HORIZONTAL && !inverse ? {
  181. scaleY: otherAxisInverse ? 1 : -1,
  182. scaleX: 1
  183. } : orient === HORIZONTAL && inverse ? {
  184. scaleY: otherAxisInverse ? 1 : -1,
  185. scaleX: -1
  186. } : orient === VERTICAL && !inverse ? {
  187. scaleY: otherAxisInverse ? -1 : 1,
  188. scaleX: 1,
  189. rotation: Math.PI / 2
  190. }
  191. // Don't use Math.PI, considering shadow direction.
  192. : {
  193. scaleY: otherAxisInverse ? -1 : 1,
  194. scaleX: -1,
  195. rotation: Math.PI / 2
  196. });
  197. // Position barGroup
  198. var rect = thisGroup.getBoundingRect([sliderGroup]);
  199. var rectX = isNaN(rect.x) ? 0 : rect.x;
  200. var rectY = isNaN(rect.y) ? 0 : rect.y;
  201. thisGroup.x = location.x - rectX;
  202. thisGroup.y = location.y - rectY;
  203. thisGroup.markRedraw();
  204. };
  205. SliderZoomView.prototype._getViewExtent = function () {
  206. return [0, this._size[0]];
  207. };
  208. SliderZoomView.prototype._renderBackground = function () {
  209. var dataZoomModel = this.dataZoomModel;
  210. var size = this._size;
  211. var barGroup = this._displayables.sliderGroup;
  212. var brushSelect = dataZoomModel.get('brushSelect');
  213. barGroup.add(new Rect({
  214. silent: true,
  215. shape: {
  216. x: 0,
  217. y: 0,
  218. width: size[0],
  219. height: size[1]
  220. },
  221. style: {
  222. fill: dataZoomModel.get('backgroundColor')
  223. },
  224. z2: -40
  225. }));
  226. // Click panel, over shadow, below handles.
  227. var clickPanel = new Rect({
  228. shape: {
  229. x: 0,
  230. y: 0,
  231. width: size[0],
  232. height: size[1]
  233. },
  234. style: {
  235. fill: 'transparent'
  236. },
  237. z2: 0,
  238. onclick: bind(this._onClickPanel, this)
  239. });
  240. var zr = this.api.getZr();
  241. if (brushSelect) {
  242. clickPanel.on('mousedown', this._onBrushStart, this);
  243. clickPanel.cursor = 'crosshair';
  244. zr.on('mousemove', this._onBrush);
  245. zr.on('mouseup', this._onBrushEnd);
  246. } else {
  247. zr.off('mousemove', this._onBrush);
  248. zr.off('mouseup', this._onBrushEnd);
  249. }
  250. barGroup.add(clickPanel);
  251. };
  252. SliderZoomView.prototype._renderDataShadow = function () {
  253. var info = this._dataShadowInfo = this._prepareDataShadowInfo();
  254. this._displayables.dataShadowSegs = [];
  255. if (!info) {
  256. return;
  257. }
  258. var size = this._size;
  259. var oldSize = this._shadowSize || [];
  260. var seriesModel = info.series;
  261. var data = seriesModel.getRawData();
  262. var candlestickDim = seriesModel.getShadowDim && seriesModel.getShadowDim();
  263. var otherDim = candlestickDim && data.getDimensionInfo(candlestickDim) ? seriesModel.getShadowDim() // @see candlestick
  264. : info.otherDim;
  265. if (otherDim == null) {
  266. return;
  267. }
  268. var polygonPts = this._shadowPolygonPts;
  269. var polylinePts = this._shadowPolylinePts;
  270. // Not re-render if data doesn't change.
  271. if (data !== this._shadowData || otherDim !== this._shadowDim || size[0] !== oldSize[0] || size[1] !== oldSize[1]) {
  272. var thisDataExtent_1 = data.getDataExtent(info.thisDim);
  273. var otherDataExtent_1 = data.getDataExtent(otherDim);
  274. // Nice extent.
  275. var otherOffset = (otherDataExtent_1[1] - otherDataExtent_1[0]) * 0.3;
  276. otherDataExtent_1 = [otherDataExtent_1[0] - otherOffset, otherDataExtent_1[1] + otherOffset];
  277. var otherShadowExtent_1 = [0, size[1]];
  278. var thisShadowExtent = [0, size[0]];
  279. var areaPoints_1 = [[size[0], 0], [0, 0]];
  280. var linePoints_1 = [];
  281. var step_1 = thisShadowExtent[1] / Math.max(1, data.count() - 1);
  282. var normalizationConstant_1 = size[0] / (thisDataExtent_1[1] - thisDataExtent_1[0]);
  283. var isTimeAxis_1 = info.thisAxis.type === 'time';
  284. var thisCoord_1 = -step_1;
  285. // Optimize for large data shadow
  286. var stride_1 = Math.round(data.count() / size[0]);
  287. var lastIsEmpty_1;
  288. data.each([info.thisDim, otherDim], function (thisValue, otherValue, index) {
  289. if (stride_1 > 0 && index % stride_1) {
  290. if (!isTimeAxis_1) {
  291. thisCoord_1 += step_1;
  292. }
  293. return;
  294. }
  295. thisCoord_1 = isTimeAxis_1 ? (+thisValue - thisDataExtent_1[0]) * normalizationConstant_1 : thisCoord_1 + step_1;
  296. // FIXME
  297. // Should consider axis.min/axis.max when drawing dataShadow.
  298. // FIXME
  299. // 应该使用统一的空判断?还是在list里进行空判断?
  300. var isEmpty = otherValue == null || isNaN(otherValue) || otherValue === '';
  301. // See #4235.
  302. var otherCoord = isEmpty ? 0 : linearMap(otherValue, otherDataExtent_1, otherShadowExtent_1, true);
  303. // Attempt to draw data shadow precisely when there are empty value.
  304. if (isEmpty && !lastIsEmpty_1 && index) {
  305. areaPoints_1.push([areaPoints_1[areaPoints_1.length - 1][0], 0]);
  306. linePoints_1.push([linePoints_1[linePoints_1.length - 1][0], 0]);
  307. } else if (!isEmpty && lastIsEmpty_1) {
  308. areaPoints_1.push([thisCoord_1, 0]);
  309. linePoints_1.push([thisCoord_1, 0]);
  310. }
  311. if (!isEmpty) {
  312. areaPoints_1.push([thisCoord_1, otherCoord]);
  313. linePoints_1.push([thisCoord_1, otherCoord]);
  314. }
  315. lastIsEmpty_1 = isEmpty;
  316. });
  317. polygonPts = this._shadowPolygonPts = areaPoints_1;
  318. polylinePts = this._shadowPolylinePts = linePoints_1;
  319. }
  320. this._shadowData = data;
  321. this._shadowDim = otherDim;
  322. this._shadowSize = [size[0], size[1]];
  323. var dataZoomModel = this.dataZoomModel;
  324. function createDataShadowGroup(isSelectedArea) {
  325. var model = dataZoomModel.getModel(isSelectedArea ? 'selectedDataBackground' : 'dataBackground');
  326. var group = new graphic.Group();
  327. var polygon = new graphic.Polygon({
  328. shape: {
  329. points: polygonPts
  330. },
  331. segmentIgnoreThreshold: 1,
  332. style: model.getModel('areaStyle').getAreaStyle(),
  333. silent: true,
  334. z2: -20
  335. });
  336. var polyline = new graphic.Polyline({
  337. shape: {
  338. points: polylinePts
  339. },
  340. segmentIgnoreThreshold: 1,
  341. style: model.getModel('lineStyle').getLineStyle(),
  342. silent: true,
  343. z2: -19
  344. });
  345. group.add(polygon);
  346. group.add(polyline);
  347. return group;
  348. }
  349. // let dataBackgroundModel = dataZoomModel.getModel('dataBackground');
  350. for (var i = 0; i < 3; i++) {
  351. var group = createDataShadowGroup(i === 1);
  352. this._displayables.sliderGroup.add(group);
  353. this._displayables.dataShadowSegs.push(group);
  354. }
  355. };
  356. SliderZoomView.prototype._prepareDataShadowInfo = function () {
  357. var dataZoomModel = this.dataZoomModel;
  358. var showDataShadow = dataZoomModel.get('showDataShadow');
  359. if (showDataShadow === false) {
  360. return;
  361. }
  362. // Find a representative series.
  363. var result;
  364. var ecModel = this.ecModel;
  365. dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {
  366. var seriesModels = dataZoomModel.getAxisProxy(axisDim, axisIndex).getTargetSeriesModels();
  367. each(seriesModels, function (seriesModel) {
  368. if (result) {
  369. return;
  370. }
  371. if (showDataShadow !== true && indexOf(SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type')) < 0) {
  372. return;
  373. }
  374. var thisAxis = ecModel.getComponent(getAxisMainType(axisDim), axisIndex).axis;
  375. var otherDim = getOtherDim(axisDim);
  376. var otherAxisInverse;
  377. var coordSys = seriesModel.coordinateSystem;
  378. if (otherDim != null && coordSys.getOtherAxis) {
  379. otherAxisInverse = coordSys.getOtherAxis(thisAxis).inverse;
  380. }
  381. otherDim = seriesModel.getData().mapDimension(otherDim);
  382. var thisDim = seriesModel.getData().mapDimension(axisDim);
  383. result = {
  384. thisAxis: thisAxis,
  385. series: seriesModel,
  386. thisDim: thisDim,
  387. otherDim: otherDim,
  388. otherAxisInverse: otherAxisInverse
  389. };
  390. }, this);
  391. }, this);
  392. return result;
  393. };
  394. SliderZoomView.prototype._renderHandle = function () {
  395. var thisGroup = this.group;
  396. var displayables = this._displayables;
  397. var handles = displayables.handles = [null, null];
  398. var handleLabels = displayables.handleLabels = [null, null];
  399. var sliderGroup = this._displayables.sliderGroup;
  400. var size = this._size;
  401. var dataZoomModel = this.dataZoomModel;
  402. var api = this.api;
  403. var borderRadius = dataZoomModel.get('borderRadius') || 0;
  404. var brushSelect = dataZoomModel.get('brushSelect');
  405. var filler = displayables.filler = new Rect({
  406. silent: brushSelect,
  407. style: {
  408. fill: dataZoomModel.get('fillerColor')
  409. },
  410. textConfig: {
  411. position: 'inside'
  412. }
  413. });
  414. sliderGroup.add(filler);
  415. // Frame border.
  416. sliderGroup.add(new Rect({
  417. silent: true,
  418. subPixelOptimize: true,
  419. shape: {
  420. x: 0,
  421. y: 0,
  422. width: size[0],
  423. height: size[1],
  424. r: borderRadius
  425. },
  426. style: {
  427. // deprecated option
  428. stroke: dataZoomModel.get('dataBackgroundColor') || dataZoomModel.get('borderColor'),
  429. lineWidth: DEFAULT_FRAME_BORDER_WIDTH,
  430. fill: tokens.color.transparent
  431. }
  432. }));
  433. // Left and right handle to resize
  434. each([0, 1], function (handleIndex) {
  435. var iconStr = dataZoomModel.get('handleIcon');
  436. if (!symbolBuildProxies[iconStr] && iconStr.indexOf('path://') < 0 && iconStr.indexOf('image://') < 0) {
  437. // Compatitable with the old icon parsers. Which can use a path string without path://
  438. iconStr = 'path://' + iconStr;
  439. if (process.env.NODE_ENV !== 'production') {
  440. deprecateLog('handleIcon now needs \'path://\' prefix when using a path string');
  441. }
  442. }
  443. var path = createSymbol(iconStr, -1, 0, 2, 2, null, true);
  444. path.attr({
  445. cursor: getCursor(this._orient),
  446. draggable: true,
  447. drift: bind(this._onDragMove, this, handleIndex),
  448. ondragend: bind(this._onDragEnd, this),
  449. onmouseover: bind(this._onOverDataInfoTriggerArea, this, true),
  450. onmouseout: bind(this._onOverDataInfoTriggerArea, this, false),
  451. z2: 5
  452. });
  453. var bRect = path.getBoundingRect();
  454. var handleSize = dataZoomModel.get('handleSize');
  455. this._handleHeight = parsePercent(handleSize, this._size[1]);
  456. this._handleWidth = bRect.width / bRect.height * this._handleHeight;
  457. path.setStyle(dataZoomModel.getModel('handleStyle').getItemStyle());
  458. path.style.strokeNoScale = true;
  459. path.rectHover = true;
  460. path.ensureState('emphasis').style = dataZoomModel.getModel(['emphasis', 'handleStyle']).getItemStyle();
  461. enableHoverEmphasis(path);
  462. var handleColor = dataZoomModel.get('handleColor'); // deprecated option
  463. // Compatitable with previous version
  464. if (handleColor != null) {
  465. path.style.fill = handleColor;
  466. }
  467. sliderGroup.add(handles[handleIndex] = path);
  468. var textStyleModel = dataZoomModel.getModel('textStyle');
  469. var handleLabel = dataZoomModel.get('handleLabel') || {};
  470. var handleLabelShow = handleLabel.show || false;
  471. thisGroup.add(handleLabels[handleIndex] = new graphic.Text({
  472. silent: true,
  473. invisible: !handleLabelShow,
  474. style: createTextStyle(textStyleModel, {
  475. x: 0,
  476. y: 0,
  477. text: '',
  478. verticalAlign: 'middle',
  479. align: 'center',
  480. fill: textStyleModel.getTextColor(),
  481. font: textStyleModel.getFont()
  482. }),
  483. z2: 10
  484. }));
  485. }, this);
  486. // Handle to move. Only visible when brushSelect is set true.
  487. var actualMoveZone = filler;
  488. if (brushSelect) {
  489. var moveHandleHeight = parsePercent(dataZoomModel.get('moveHandleSize'), size[1]);
  490. var moveHandle_1 = displayables.moveHandle = new graphic.Rect({
  491. style: dataZoomModel.getModel('moveHandleStyle').getItemStyle(),
  492. silent: true,
  493. shape: {
  494. r: [0, 0, 2, 2],
  495. y: size[1] - 0.5,
  496. height: moveHandleHeight
  497. }
  498. });
  499. var iconSize = moveHandleHeight * 0.8;
  500. var moveHandleIcon = displayables.moveHandleIcon = createSymbol(dataZoomModel.get('moveHandleIcon'), -iconSize / 2, -iconSize / 2, iconSize, iconSize, tokens.color.neutral00, true);
  501. moveHandleIcon.silent = true;
  502. moveHandleIcon.y = size[1] + moveHandleHeight / 2 - 0.5;
  503. moveHandle_1.ensureState('emphasis').style = dataZoomModel.getModel(['emphasis', 'moveHandleStyle']).getItemStyle();
  504. var moveZoneExpandSize = Math.min(size[1] / 2, Math.max(moveHandleHeight, 10));
  505. actualMoveZone = displayables.moveZone = new graphic.Rect({
  506. invisible: true,
  507. shape: {
  508. y: size[1] - moveZoneExpandSize,
  509. height: moveHandleHeight + moveZoneExpandSize
  510. }
  511. });
  512. actualMoveZone.on('mouseover', function () {
  513. api.enterEmphasis(moveHandle_1);
  514. }).on('mouseout', function () {
  515. api.leaveEmphasis(moveHandle_1);
  516. });
  517. sliderGroup.add(moveHandle_1);
  518. sliderGroup.add(moveHandleIcon);
  519. sliderGroup.add(actualMoveZone);
  520. }
  521. actualMoveZone.attr({
  522. draggable: true,
  523. cursor: 'grab',
  524. drift: bind(this._onActualMoveZoneDrift, this),
  525. ondragstart: bind(this._onActualMoveZoneDragStart, this),
  526. ondragend: bind(this._onActualMoveZoneDragEnd, this),
  527. onmouseover: bind(this._onOverDataInfoTriggerArea, this, true),
  528. onmouseout: bind(this._onOverDataInfoTriggerArea, this, false)
  529. });
  530. };
  531. SliderZoomView.prototype._resetInterval = function () {
  532. var range = this._range = this.dataZoomModel.getPercentRange();
  533. var viewExtent = this._getViewExtent();
  534. this._handleEnds = [linearMap(range[0], [0, 100], viewExtent, true), linearMap(range[1], [0, 100], viewExtent, true)];
  535. };
  536. SliderZoomView.prototype._updateInterval = function (handleIndex, delta) {
  537. var dataZoomModel = this.dataZoomModel;
  538. var handleEnds = this._handleEnds;
  539. var viewExtend = this._getViewExtent();
  540. var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();
  541. var percentExtent = [0, 100];
  542. sliderMove(delta, handleEnds, viewExtend, dataZoomModel.get('zoomLock') ? 'all' : handleIndex, minMaxSpan.minSpan != null ? linearMap(minMaxSpan.minSpan, percentExtent, viewExtend, true) : null, minMaxSpan.maxSpan != null ? linearMap(minMaxSpan.maxSpan, percentExtent, viewExtend, true) : null);
  543. var lastRange = this._range;
  544. var range = this._range = asc([linearMap(handleEnds[0], viewExtend, percentExtent, true), linearMap(handleEnds[1], viewExtend, percentExtent, true)]);
  545. return !lastRange || lastRange[0] !== range[0] || lastRange[1] !== range[1];
  546. };
  547. SliderZoomView.prototype._updateView = function (nonRealtime) {
  548. var displaybles = this._displayables;
  549. var handleEnds = this._handleEnds;
  550. var handleInterval = asc(handleEnds.slice());
  551. var size = this._size;
  552. each([0, 1], function (handleIndex) {
  553. // Handles
  554. var handle = displaybles.handles[handleIndex];
  555. var handleHeight = this._handleHeight;
  556. handle.attr({
  557. scaleX: handleHeight / 2,
  558. scaleY: handleHeight / 2,
  559. // This is a trick, by adding an extra tiny offset to let the default handle's end point align to the drag window.
  560. // NOTE: It may affect some custom shapes a bit. But we prefer to have better result by default.
  561. x: handleEnds[handleIndex] + (handleIndex ? -1 : 1),
  562. y: size[1] / 2 - handleHeight / 2
  563. });
  564. }, this);
  565. // Filler
  566. displaybles.filler.setShape({
  567. x: handleInterval[0],
  568. y: 0,
  569. width: handleInterval[1] - handleInterval[0],
  570. height: size[1]
  571. });
  572. var viewExtent = {
  573. x: handleInterval[0],
  574. width: handleInterval[1] - handleInterval[0]
  575. };
  576. // Move handle
  577. if (displaybles.moveHandle) {
  578. displaybles.moveHandle.setShape(viewExtent);
  579. displaybles.moveZone.setShape(viewExtent);
  580. // Force update path on the invisible object
  581. displaybles.moveZone.getBoundingRect();
  582. displaybles.moveHandleIcon && displaybles.moveHandleIcon.attr('x', viewExtent.x + viewExtent.width / 2);
  583. }
  584. // update clip path of shadow.
  585. var dataShadowSegs = displaybles.dataShadowSegs;
  586. var segIntervals = [0, handleInterval[0], handleInterval[1], size[0]];
  587. for (var i = 0; i < dataShadowSegs.length; i++) {
  588. var segGroup = dataShadowSegs[i];
  589. var clipPath = segGroup.getClipPath();
  590. if (!clipPath) {
  591. clipPath = new graphic.Rect();
  592. segGroup.setClipPath(clipPath);
  593. }
  594. clipPath.setShape({
  595. x: segIntervals[i],
  596. y: 0,
  597. width: segIntervals[i + 1] - segIntervals[i],
  598. height: size[1]
  599. });
  600. }
  601. this._updateDataInfo(nonRealtime);
  602. };
  603. SliderZoomView.prototype._updateDataInfo = function (nonRealtime) {
  604. var dataZoomModel = this.dataZoomModel;
  605. var displayables = this._displayables;
  606. var handleLabels = displayables.handleLabels;
  607. var orient = this._orient;
  608. var labelTexts = ['', ''];
  609. if (dataZoomModel.get('showDetail')) {
  610. var axisProxy = dataZoomModel.findRepresentativeAxisProxy();
  611. var scale = axisProxy.getAxisModel().axis.scale;
  612. if (axisProxy) {
  613. var range = this._range;
  614. var window_1;
  615. if (nonRealtime) {
  616. // See #4434, data and axis are not processed and reset yet in non-realtime mode.
  617. var calcWinInput = {
  618. start: range[0],
  619. end: range[1]
  620. };
  621. var alignTo = getAlignTo(dataZoomModel, axisProxy);
  622. if (alignTo) {
  623. var alignToWindow = alignTo.calculateDataWindow(calcWinInput).percentInverted;
  624. calcWinInput = {
  625. start: alignToWindow[0],
  626. end: alignToWindow[1]
  627. };
  628. }
  629. window_1 = axisProxy.calculateDataWindow(calcWinInput);
  630. } else {
  631. window_1 = axisProxy.getWindow();
  632. }
  633. labelTexts = [formatLabel(dataZoomModel, 0, window_1, scale), formatLabel(dataZoomModel, 1, window_1, scale)];
  634. }
  635. }
  636. var orderedHandleEnds = asc(this._handleEnds.slice());
  637. setLabel.call(this, 0);
  638. setLabel.call(this, 1);
  639. function setLabel(handleIndex) {
  640. // Label
  641. // Text should not transform by barGroup.
  642. // Ignore handlers transform
  643. var barTransform = graphic.getTransform(displayables.handles[handleIndex].parent, this.group);
  644. var direction = graphic.transformDirection(handleIndex === 0 ? 'right' : 'left', barTransform);
  645. var offset = this._handleWidth / 2 + LABEL_GAP;
  646. var textPoint = graphic.applyTransform([orderedHandleEnds[handleIndex] + (handleIndex === 0 ? -offset : offset), this._size[1] / 2], barTransform);
  647. handleLabels[handleIndex].setStyle({
  648. x: textPoint[0],
  649. y: textPoint[1],
  650. verticalAlign: orient === HORIZONTAL ? 'middle' : direction,
  651. align: orient === HORIZONTAL ? direction : 'center',
  652. text: labelTexts[handleIndex]
  653. });
  654. }
  655. };
  656. SliderZoomView.prototype._onOverDataInfoTriggerArea = function (isOver) {
  657. this._isOverDataInfoTriggerArea = isOver;
  658. this._showDataInfo(isOver);
  659. };
  660. /**
  661. * @param isEmphasis true: show, false: hide
  662. */
  663. SliderZoomView.prototype._showDataInfo = function (isEmphasis) {
  664. var handleLabel = this.dataZoomModel.get('handleLabel') || {};
  665. var normalShow = handleLabel.show || false;
  666. var emphasisHandleLabel = this.dataZoomModel.getModel(['emphasis', 'handleLabel']);
  667. var emphasisShow = emphasisHandleLabel.get('show') || false;
  668. // Dragging is considered as emphasis, unless emphasisShow is false
  669. var toShow = isEmphasis || this._dragging ? emphasisShow : normalShow;
  670. var displayables = this._displayables;
  671. var handleLabels = displayables.handleLabels;
  672. handleLabels[0].attr('invisible', !toShow);
  673. handleLabels[1].attr('invisible', !toShow);
  674. // Highlight move handle
  675. displayables.moveHandle && this.api[toShow ? 'enterEmphasis' : 'leaveEmphasis'](displayables.moveHandle, 1);
  676. };
  677. SliderZoomView.prototype._onActualMoveZoneDrift = function (dx, dy, event) {
  678. this.api.getZr().setCursorStyle('grabbing');
  679. this._onDragMove('all', dx, dy, event);
  680. };
  681. SliderZoomView.prototype._onActualMoveZoneDragStart = function (event) {
  682. event.target.attr('cursor', 'grabbing');
  683. this._showDataInfo(true);
  684. };
  685. SliderZoomView.prototype._onActualMoveZoneDragEnd = function (event) {
  686. event.target.attr('cursor', 'grab');
  687. this._onDragEnd();
  688. };
  689. SliderZoomView.prototype._onDragMove = function (handleIndex, dx, dy, event) {
  690. this._dragging = true;
  691. // For mobile device, prevent screen slider on the button.
  692. eventTool.stop(event.event);
  693. // Transform dx, dy to bar coordination.
  694. var barTransform = this._displayables.sliderGroup.getLocalTransform();
  695. var vertex = graphic.applyTransform([dx, dy], barTransform, true);
  696. var changed = this._updateInterval(handleIndex, vertex[0]);
  697. var realtime = this.dataZoomModel.get('realtime');
  698. this._updateView(!realtime);
  699. // Avoid dispatch dataZoom repeatly but range not changed,
  700. // which cause bad visual effect when progressive enabled.
  701. changed && realtime && this._dispatchZoomAction(true);
  702. };
  703. SliderZoomView.prototype._onDragEnd = function () {
  704. this._dragging = false;
  705. if (!this._isOverDataInfoTriggerArea) {
  706. // Drag end may occur on draggable bars, where data info should be still shown.
  707. this._showDataInfo(false);
  708. }
  709. // While in realtime mode and stream mode, dispatch action when
  710. // drag end will cause the whole view rerender, which is unnecessary.
  711. var realtime = this.dataZoomModel.get('realtime');
  712. !realtime && this._dispatchZoomAction(false);
  713. };
  714. SliderZoomView.prototype._onClickPanel = function (e) {
  715. var size = this._size;
  716. var localPoint = this._displayables.sliderGroup.transformCoordToLocal(e.offsetX, e.offsetY);
  717. if (localPoint[0] < 0 || localPoint[0] > size[0] || localPoint[1] < 0 || localPoint[1] > size[1]) {
  718. return;
  719. }
  720. var handleEnds = this._handleEnds;
  721. var center = (handleEnds[0] + handleEnds[1]) / 2;
  722. var changed = this._updateInterval('all', localPoint[0] - center);
  723. this._updateView();
  724. changed && this._dispatchZoomAction(false);
  725. };
  726. SliderZoomView.prototype._onBrushStart = function (e) {
  727. var x = e.offsetX;
  728. var y = e.offsetY;
  729. this._brushStart = new graphic.Point(x, y);
  730. this._brushing = true;
  731. this._brushStartTime = +new Date();
  732. // this._updateBrushRect(x, y);
  733. };
  734. SliderZoomView.prototype._onBrushEnd = function (e) {
  735. if (!this._brushing) {
  736. return;
  737. }
  738. var brushRect = this._displayables.brushRect;
  739. this._brushing = false;
  740. if (!brushRect) {
  741. return;
  742. }
  743. brushRect.attr('ignore', true);
  744. var brushShape = brushRect.shape;
  745. var brushEndTime = +new Date();
  746. // console.log(brushEndTime - this._brushStartTime);
  747. if (brushEndTime - this._brushStartTime < 200 && Math.abs(brushShape.width) < 5) {
  748. // Will treat it as a click
  749. return;
  750. }
  751. var viewExtend = this._getViewExtent();
  752. var percentExtent = [0, 100];
  753. var handleEnds = this._handleEnds = [brushShape.x, brushShape.x + brushShape.width];
  754. var minMaxSpan = this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();
  755. // Restrict range.
  756. sliderMove(0, handleEnds, viewExtend, 0, minMaxSpan.minSpan != null ? linearMap(minMaxSpan.minSpan, percentExtent, viewExtend, true) : null, minMaxSpan.maxSpan != null ? linearMap(minMaxSpan.maxSpan, percentExtent, viewExtend, true) : null);
  757. this._range = asc([linearMap(handleEnds[0], viewExtend, percentExtent, true), linearMap(handleEnds[1], viewExtend, percentExtent, true)]);
  758. this._updateView();
  759. this._dispatchZoomAction(false);
  760. };
  761. SliderZoomView.prototype._onBrush = function (e) {
  762. if (this._brushing) {
  763. // For mobile device, prevent screen slider on the button.
  764. eventTool.stop(e.event);
  765. this._updateBrushRect(e.offsetX, e.offsetY);
  766. }
  767. };
  768. SliderZoomView.prototype._updateBrushRect = function (mouseX, mouseY) {
  769. var displayables = this._displayables;
  770. var dataZoomModel = this.dataZoomModel;
  771. var brushRect = displayables.brushRect;
  772. if (!brushRect) {
  773. brushRect = displayables.brushRect = new Rect({
  774. silent: true,
  775. style: dataZoomModel.getModel('brushStyle').getItemStyle()
  776. });
  777. displayables.sliderGroup.add(brushRect);
  778. }
  779. brushRect.attr('ignore', false);
  780. var brushStart = this._brushStart;
  781. var sliderGroup = this._displayables.sliderGroup;
  782. var endPoint = sliderGroup.transformCoordToLocal(mouseX, mouseY);
  783. var startPoint = sliderGroup.transformCoordToLocal(brushStart.x, brushStart.y);
  784. var size = this._size;
  785. endPoint[0] = Math.max(Math.min(size[0], endPoint[0]), 0);
  786. brushRect.setShape({
  787. x: startPoint[0],
  788. y: 0,
  789. width: endPoint[0] - startPoint[0],
  790. height: size[1]
  791. });
  792. };
  793. /**
  794. * This action will be throttled.
  795. */
  796. SliderZoomView.prototype._dispatchZoomAction = function (realtime) {
  797. var range = this._range;
  798. this.api.dispatchAction({
  799. type: 'dataZoom',
  800. from: this.uid,
  801. dataZoomId: this.dataZoomModel.id,
  802. animation: realtime ? REALTIME_ANIMATION_CONFIG : null,
  803. start: range[0],
  804. end: range[1]
  805. });
  806. };
  807. SliderZoomView.prototype._findCoordRect = function () {
  808. // Find the grid corresponding to the first axis referred by dataZoom.
  809. var rect;
  810. var coordSysInfoList = collectReferCoordSysModelInfo(this.dataZoomModel).infoList;
  811. if (!rect && coordSysInfoList.length) {
  812. var coordSys = coordSysInfoList[0].model.coordinateSystem;
  813. rect = coordSys.getRect && coordSys.getRect();
  814. }
  815. if (!rect) {
  816. var width = this.api.getWidth();
  817. var height = this.api.getHeight();
  818. rect = {
  819. x: width * 0.2,
  820. y: height * 0.2,
  821. width: width * 0.6,
  822. height: height * 0.6
  823. };
  824. }
  825. return rect;
  826. };
  827. SliderZoomView.type = 'dataZoom.slider';
  828. return SliderZoomView;
  829. }(DataZoomView);
  830. function formatLabel(dataZoomModel, extentIdx, window, scale) {
  831. var labelFormatter = dataZoomModel.get('labelFormatter');
  832. var labelPrecision = dataZoomModel.get('labelPrecision');
  833. if (labelPrecision == null || labelPrecision === 'auto') {
  834. labelPrecision = window.valuePrecision;
  835. }
  836. // Do not display values out of `SCALE_EXTENT_KIND_EFFECTIVE` - generally they are meaningless.
  837. // For example, `scaleExtent[0]` is often `0`, and negative values are unlikely to be meaningful.
  838. // That is, "nice" expansion and `SCALE_EXTENT_KIND_MAPPING` expansion are always not display in labels.
  839. var value = window.value[extentIdx];
  840. var valueStr = value == null || isNaN(value) ? '' : isOrdinalScale(scale) || isTimeScale(scale) ? scale.getLabel({
  841. value: Math.round(value)
  842. }) : isFinite(labelPrecision) ? round(value, labelPrecision, true) : value + '';
  843. return isFunction(labelFormatter) ? labelFormatter(value, valueStr) : isString(labelFormatter) ? labelFormatter.replace('{value}', valueStr) : valueStr;
  844. }
  845. function getOtherDim(thisDim) {
  846. // FIXME
  847. // 这个逻辑和getOtherAxis里一致,但是写在这里是否不好
  848. var map = {
  849. x: 'y',
  850. y: 'x',
  851. radius: 'angle',
  852. angle: 'radius'
  853. };
  854. return map[thisDim];
  855. }
  856. function getCursor(orient) {
  857. return orient === 'vertical' ? 'ns-resize' : 'ew-resize';
  858. }
  859. export default SliderZoomView;