SliderZoomView.js 31 KB

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