123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
- var zrUtil = require("zrender/lib/core/util");
- var clazzUtil = require("../../util/clazz");
- var graphic = require("../../util/graphic");
- var axisPointerModelHelper = require("./modelHelper");
- var eventTool = require("zrender/lib/core/event");
- var throttleUtil = require("../../util/throttle");
- var _model = require("../../util/model");
- var makeInner = _model.makeInner;
- /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
- var inner = makeInner();
- var clone = zrUtil.clone;
- var bind = zrUtil.bind;
- /**
- * Base axis pointer class in 2D.
- * Implemenents {module:echarts/component/axis/IAxisPointer}.
- */
- function BaseAxisPointer() {}
- BaseAxisPointer.prototype = {
- /**
- * @private
- */
- _group: null,
- /**
- * @private
- */
- _lastGraphicKey: null,
- /**
- * @private
- */
- _handle: null,
- /**
- * @private
- */
- _dragging: false,
- /**
- * @private
- */
- _lastValue: null,
- /**
- * @private
- */
- _lastStatus: null,
- /**
- * @private
- */
- _payloadInfo: null,
- /**
- * In px, arbitrary value. Do not set too small,
- * no animation is ok for most cases.
- * @protected
- */
- animationThreshold: 15,
- /**
- * @implement
- */
- render: function (axisModel, axisPointerModel, api, forceRender) {
- var value = axisPointerModel.get('value');
- var status = axisPointerModel.get('status'); // Bind them to `this`, not in closure, otherwise they will not
- // be replaced when user calling setOption in not merge mode.
- this._axisModel = axisModel;
- this._axisPointerModel = axisPointerModel;
- this._api = api; // Optimize: `render` will be called repeatly during mouse move.
- // So it is power consuming if performing `render` each time,
- // especially on mobile device.
- if (!forceRender && this._lastValue === value && this._lastStatus === status) {
- return;
- }
- this._lastValue = value;
- this._lastStatus = status;
- var group = this._group;
- var handle = this._handle;
- if (!status || status === 'hide') {
- // Do not clear here, for animation better.
- group && group.hide();
- handle && handle.hide();
- return;
- }
- group && group.show();
- handle && handle.show(); // Otherwise status is 'show'
- var elOption = {};
- this.makeElOption(elOption, value, axisModel, axisPointerModel, api); // Enable change axis pointer type.
- var graphicKey = elOption.graphicKey;
- if (graphicKey !== this._lastGraphicKey) {
- this.clear(api);
- }
- this._lastGraphicKey = graphicKey;
- var moveAnimation = this._moveAnimation = this.determineAnimation(axisModel, axisPointerModel);
- if (!group) {
- group = this._group = new graphic.Group();
- this.createPointerEl(group, elOption, axisModel, axisPointerModel);
- this.createLabelEl(group, elOption, axisModel, axisPointerModel);
- api.getZr().add(group);
- } else {
- var doUpdateProps = zrUtil.curry(updateProps, axisPointerModel, moveAnimation);
- this.updatePointerEl(group, elOption, doUpdateProps, axisPointerModel);
- this.updateLabelEl(group, elOption, doUpdateProps, axisPointerModel);
- }
- updateMandatoryProps(group, axisPointerModel, true);
- this._renderHandle(value);
- },
- /**
- * @implement
- */
- remove: function (api) {
- this.clear(api);
- },
- /**
- * @implement
- */
- dispose: function (api) {
- this.clear(api);
- },
- /**
- * @protected
- */
- determineAnimation: function (axisModel, axisPointerModel) {
- var animation = axisPointerModel.get('animation');
- var axis = axisModel.axis;
- var isCategoryAxis = axis.type === 'category';
- var useSnap = axisPointerModel.get('snap'); // Value axis without snap always do not snap.
- if (!useSnap && !isCategoryAxis) {
- return false;
- }
- if (animation === 'auto' || animation == null) {
- var animationThreshold = this.animationThreshold;
- if (isCategoryAxis && axis.getBandWidth() > animationThreshold) {
- return true;
- } // It is important to auto animation when snap used. Consider if there is
- // a dataZoom, animation will be disabled when too many points exist, while
- // it will be enabled for better visual effect when little points exist.
- if (useSnap) {
- var seriesDataCount = axisPointerModelHelper.getAxisInfo(axisModel).seriesDataCount;
- var axisExtent = axis.getExtent(); // Approximate band width
- return Math.abs(axisExtent[0] - axisExtent[1]) / seriesDataCount > animationThreshold;
- }
- return false;
- }
- return animation === true;
- },
- /**
- * add {pointer, label, graphicKey} to elOption
- * @protected
- */
- makeElOption: function (elOption, value, axisModel, axisPointerModel, api) {// Shoule be implemenented by sub-class.
- },
- /**
- * @protected
- */
- createPointerEl: function (group, elOption, axisModel, axisPointerModel) {
- var pointerOption = elOption.pointer;
- if (pointerOption) {
- var pointerEl = inner(group).pointerEl = new graphic[pointerOption.type](clone(elOption.pointer));
- group.add(pointerEl);
- }
- },
- /**
- * @protected
- */
- createLabelEl: function (group, elOption, axisModel, axisPointerModel) {
- if (elOption.label) {
- var labelEl = inner(group).labelEl = new graphic.Rect(clone(elOption.label));
- group.add(labelEl);
- updateLabelShowHide(labelEl, axisPointerModel);
- }
- },
- /**
- * @protected
- */
- updatePointerEl: function (group, elOption, updateProps) {
- var pointerEl = inner(group).pointerEl;
- if (pointerEl && elOption.pointer) {
- pointerEl.setStyle(elOption.pointer.style);
- updateProps(pointerEl, {
- shape: elOption.pointer.shape
- });
- }
- },
- /**
- * @protected
- */
- updateLabelEl: function (group, elOption, updateProps, axisPointerModel) {
- var labelEl = inner(group).labelEl;
- if (labelEl) {
- labelEl.setStyle(elOption.label.style);
- updateProps(labelEl, {
- // Consider text length change in vertical axis, animation should
- // be used on shape, otherwise the effect will be weird.
- shape: elOption.label.shape,
- position: elOption.label.position
- });
- updateLabelShowHide(labelEl, axisPointerModel);
- }
- },
- /**
- * @private
- */
- _renderHandle: function (value) {
- if (this._dragging || !this.updateHandleTransform) {
- return;
- }
- var axisPointerModel = this._axisPointerModel;
- var zr = this._api.getZr();
- var handle = this._handle;
- var handleModel = axisPointerModel.getModel('handle');
- var status = axisPointerModel.get('status');
- if (!handleModel.get('show') || !status || status === 'hide') {
- handle && zr.remove(handle);
- this._handle = null;
- return;
- }
- var isInit;
- if (!this._handle) {
- isInit = true;
- handle = this._handle = graphic.createIcon(handleModel.get('icon'), {
- cursor: 'move',
- draggable: true,
- onmousemove: function (e) {
- // Fot mobile devicem, prevent screen slider on the button.
- eventTool.stop(e.event);
- },
- onmousedown: bind(this._onHandleDragMove, this, 0, 0),
- drift: bind(this._onHandleDragMove, this),
- ondragend: bind(this._onHandleDragEnd, this)
- });
- zr.add(handle);
- }
- updateMandatoryProps(handle, axisPointerModel, false); // update style
- var includeStyles = ['color', 'borderColor', 'borderWidth', 'opacity', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY'];
- handle.setStyle(handleModel.getItemStyle(null, includeStyles)); // update position
- var handleSize = handleModel.get('size');
- if (!zrUtil.isArray(handleSize)) {
- handleSize = [handleSize, handleSize];
- }
- handle.attr('scale', [handleSize[0] / 2, handleSize[1] / 2]);
- throttleUtil.createOrUpdate(this, '_doDispatchAxisPointer', handleModel.get('throttle') || 0, 'fixRate');
- this._moveHandleToValue(value, isInit);
- },
- /**
- * @private
- */
- _moveHandleToValue: function (value, isInit) {
- updateProps(this._axisPointerModel, !isInit && this._moveAnimation, this._handle, getHandleTransProps(this.getHandleTransform(value, this._axisModel, this._axisPointerModel)));
- },
- /**
- * @private
- */
- _onHandleDragMove: function (dx, dy) {
- var handle = this._handle;
- if (!handle) {
- return;
- }
- this._dragging = true; // Persistent for throttle.
- var trans = this.updateHandleTransform(getHandleTransProps(handle), [dx, dy], this._axisModel, this._axisPointerModel);
- this._payloadInfo = trans;
- handle.stopAnimation();
- handle.attr(getHandleTransProps(trans));
- inner(handle).lastProp = null;
- this._doDispatchAxisPointer();
- },
- /**
- * Throttled method.
- * @private
- */
- _doDispatchAxisPointer: function () {
- var handle = this._handle;
- if (!handle) {
- return;
- }
- var payloadInfo = this._payloadInfo;
- var axisModel = this._axisModel;
- this._api.dispatchAction({
- type: 'updateAxisPointer',
- x: payloadInfo.cursorPoint[0],
- y: payloadInfo.cursorPoint[1],
- tooltipOption: payloadInfo.tooltipOption,
- axesInfo: [{
- axisDim: axisModel.axis.dim,
- axisIndex: axisModel.componentIndex
- }]
- });
- },
- /**
- * @private
- */
- _onHandleDragEnd: function (moveAnimation) {
- this._dragging = false;
- var handle = this._handle;
- if (!handle) {
- return;
- }
- var value = this._axisPointerModel.get('value'); // Consider snap or categroy axis, handle may be not consistent with
- // axisPointer. So move handle to align the exact value position when
- // drag ended.
- this._moveHandleToValue(value); // For the effect: tooltip will be shown when finger holding on handle
- // button, and will be hidden after finger left handle button.
- this._api.dispatchAction({
- type: 'hideTip'
- });
- },
- /**
- * Should be implemenented by sub-class if support `handle`.
- * @protected
- * @param {number} value
- * @param {module:echarts/model/Model} axisModel
- * @param {module:echarts/model/Model} axisPointerModel
- * @return {Object} {position: [x, y], rotation: 0}
- */
- getHandleTransform: null,
- /**
- * * Should be implemenented by sub-class if support `handle`.
- * @protected
- * @param {Object} transform {position, rotation}
- * @param {Array.<number>} delta [dx, dy]
- * @param {module:echarts/model/Model} axisModel
- * @param {module:echarts/model/Model} axisPointerModel
- * @return {Object} {position: [x, y], rotation: 0, cursorPoint: [x, y]}
- */
- updateHandleTransform: null,
- /**
- * @private
- */
- clear: function (api) {
- this._lastValue = null;
- this._lastStatus = null;
- var zr = api.getZr();
- var group = this._group;
- var handle = this._handle;
- if (zr && group) {
- this._lastGraphicKey = null;
- group && zr.remove(group);
- handle && zr.remove(handle);
- this._group = null;
- this._handle = null;
- this._payloadInfo = null;
- }
- },
- /**
- * @protected
- */
- doClear: function () {// Implemented by sub-class if necessary.
- },
- /**
- * @protected
- * @param {Array.<number>} xy
- * @param {Array.<number>} wh
- * @param {number} [xDimIndex=0] or 1
- */
- buildLabel: function (xy, wh, xDimIndex) {
- xDimIndex = xDimIndex || 0;
- return {
- x: xy[xDimIndex],
- y: xy[1 - xDimIndex],
- width: wh[xDimIndex],
- height: wh[1 - xDimIndex]
- };
- }
- };
- BaseAxisPointer.prototype.constructor = BaseAxisPointer;
- function updateProps(animationModel, moveAnimation, el, props) {
- // Animation optimize.
- if (!propsEqual(inner(el).lastProp, props)) {
- inner(el).lastProp = props;
- moveAnimation ? graphic.updateProps(el, props, animationModel) : (el.stopAnimation(), el.attr(props));
- }
- }
- function propsEqual(lastProps, newProps) {
- if (zrUtil.isObject(lastProps) && zrUtil.isObject(newProps)) {
- var equals = true;
- zrUtil.each(newProps, function (item, key) {
- equals = equals && propsEqual(lastProps[key], item);
- });
- return !!equals;
- } else {
- return lastProps === newProps;
- }
- }
- function updateLabelShowHide(labelEl, axisPointerModel) {
- labelEl[axisPointerModel.get('label.show') ? 'show' : 'hide']();
- }
- function getHandleTransProps(trans) {
- return {
- position: trans.position.slice(),
- rotation: trans.rotation || 0
- };
- }
- function updateMandatoryProps(group, axisPointerModel, silent) {
- var z = axisPointerModel.get('z');
- var zlevel = axisPointerModel.get('zlevel');
- group && group.traverse(function (el) {
- if (el.type !== 'group') {
- z != null && (el.z = z);
- zlevel != null && (el.zlevel = zlevel);
- el.silent = silent;
- }
- });
- }
- clazzUtil.enableClassExtend(BaseAxisPointer);
- var _default = BaseAxisPointer;
- module.exports = _default;
|