UNPKG

chartx

Version:

Data Visualization Chart Library

780 lines (763 loc) 26.4 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _component = _interopRequireDefault(require("../component")); var _canvax = _interopRequireDefault(require("canvax")); var _tools = require("../../utils/tools"); function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } var _ = _canvax.default._; var Rect = _canvax.default.Shapes.Rect; var Line = _canvax.default.Shapes.Line; var Tips = /*#__PURE__*/function (_Component) { function Tips(opt, app) { var _this; (0, _classCallCheck2.default)(this, Tips); _this = _callSuper(this, Tips, [opt, app]); _this.uuid = (0, _tools.generateUUID)() + "_" + Math.ceil(Math.random() * 100000000) + "_" + new Date().getTime(); _this.inited = false; _this.name = "tips"; _this.cW = 0; //容器的width _this.cH = 0; //容器的height _this.dW = 0; //html的tips内容width _this.dH = 0; //html的tips内容Height _this._tipDom = null; _this._tipsPointer = null; //所有调用tip的 event 上面 要附带有符合下面结构的eventInfo属性 //会deepExtend到this.indo上面来 _this.eventInfo = null; _this.sprite = null; _this.sprite = new _canvax.default.Display.Sprite({ id: "TipSprite" }); _this.app.stage.addChild(_this.sprite); var me = _this; _this.sprite.on("destroy", function () { //me._tipDom = null; me._removeContent(); }); _.extend(true, _this, (0, _tools.getDefaultProps)(Tips.defaultProps()), opt); _this.tipDomContainer = null; if (document) { if (_this.containerIsBody) { _this.tipDomContainer = document.body; } else { _this.tipDomContainer = _this.app.canvax.domView; } } ; // (document && this.containerIsBody) ? document.body : null; //this.app.canvax.domView; return _this; } (0, _inherits2.default)(Tips, _Component); return (0, _createClass2.default)(Tips, [{ key: "draw", value: function draw() { var _coord = this.app.getComponent({ name: 'coord' }); if (this.defaultXVal !== null && _coord.type == 'rect' && !this.inited) { var induce = _coord.induce; var x = _coord._xAxis.getPosOfVal(this.defaultXVal); var y = 0; var globalPoint = induce.localToGlobal({ x: x, y: y }); var domBX = 0, domBY = 0; if (this._tipDom) { //小程序中不能用getBoundingClientRect var domBounding = this.app.canvax.el.getBoundingClientRect(); domBX = domBounding.x || domBounding.left; domBY = domBounding.y || domBounding.top; } var event = { type: 'mouseover', target: induce, point: { x: x, y: y }, x: domBX + globalPoint.x, y: domBY + globalPoint.y }; if (!this.containerIsBody) { event.offsetX = parseInt(globalPoint.x); event.offsetY = parseInt(globalPoint.y); } this.app._setGraphsTipsInfo.apply(this.app, [event]); this.show(event); this.app._tipsPointerAtAllGraphs(event); } this.inited = true; } }, { key: "show", value: function show(e) { var _this2 = this; if (!this.enabled) return; if (e.eventInfo) { this.eventInfo = e.eventInfo; //TODO:这里要优化,canvax后续要提供直接获取canvax实例的方法 var stage = e.target.getStage(); if (stage) { this.cW = stage.context.width; this.cH = stage.context.height; } else { if (e.target.type == 'canvax') { this.cW = e.target.width; this.cH = e.target.height; } ; } ; var content = this._setContent(e); content.then(function (content) { if (content) { _this2._setPosition(e); _this2.sprite.toFront(); } else { _this2._hideDialogTips(e); } }); } else { this._hideDialogTips(e); } this._tipsPointerShow(e); this.onshow.apply(this, [e]); } }, { key: "move", value: function move(e) { var _this3 = this; if (!this.enabled) return; if (e.eventInfo) { this.eventInfo = e.eventInfo; var content = this._setContent(e); content.then(function (content) { if (content) { if (_this3.followPointer) { _this3._setPosition(e); } } else { //move的时候hide的只有dialogTips, pointer不想要隐藏 _this3._hideDialogTips(); } }); } ; this._tipsPointerMove(e); this.onmove.apply(this, [e]); } }, { key: "hide", value: function hide(e) { //console.log('tips hide') if (this._tipDom && (e.toElement == this._tipDom || this._tipDom.contains(e.toElement))) { return; } this._hide(e); if (this.viewPath) { this.innerHTMLClear && this.innerHTMLClear(e, this); } this.onhide.apply(this, [e]); } }, { key: "_hide", value: function _hide(e) { if (!this.enabled) return; this._hideDialogTips(e); this._tipsPointerHide(e); } }, { key: "_hideDialogTips", value: function _hideDialogTips() { if (this.eventInfo) { this.eventInfo = null; this.sprite.removeAllChildren(); this._removeContent(); } ; } /** *@pos {x:0,y:0} */ }, { key: "_setPosition", value: function _setPosition(e) { //tips直接修改为fixed,所以定位直接用e.x e.y 2020-02-27 if (!this.enabled) return; if (!this._tipDom) return; var domBounding = this.app.canvax.el.getBoundingClientRect(); var domBX = domBounding.x || domBounding.left; var domBY = domBounding.y || domBounding.top; var x, y; if (this.containerIsBody) { var globalPoint = e.target.localToGlobal(e.point); x = this._checkX(globalPoint.x + domBX + this.offsetX); y = this._checkY(globalPoint.y + domBY + this.offsetY); } else { x = this._checkX(e.offsetX + domBX + this.offsetX); y = this._checkY(e.offsetY + domBY + this.offsetY); x -= domBX; y -= domBY; } //let x = this._checkX( e.clientX + this.offsetX); //let y = this._checkY( e.clientY + this.offsetY); this._tipDom.style.cssText += ";visibility:visible;left:" + x + "px;top:" + y + "px;"; if (this.positionOfPoint == "left") { this._tipDom.style.left = this._checkX(e.x - this.offsetX - this._tipDom.offsetWidth) + "px"; } ; } /** *content相关------------------------- */ }, { key: "_creatTipDom", value: function _creatTipDom(e) { var me = this; if (document) { this._tipDom = document.createElement("div"); this._tipDom.className = "chart-tips"; this._tipDom.style.cssText += "; border-radius:" + this.borderRadius + "px;background:" + this.fillStyle + ";border:1px solid " + this.strokeStyle + ";visibility:hidden;position:" + (this.containerIsBody ? 'fixed' : 'absolute') + ";z-index:99999999;enabled:inline-block;*enabled:inline;*zoom:1;padding:6px;color:" + this.fontColor + ";line-height:1.5"; this._tipDom.style.cssText += "; box-shadow:1px 1px 3px " + this.strokeStyle; this._tipDom.style.cssText += "; border:none;white-space:nowrap;word-wrap:normal"; this._tipDom.style.cssText += "; text-align:left"; if (!this.pointerEvent) { this._tipDom.style.cssText += "; pointer-events:none"; } this._tipDom.style.cssText += "; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;"; if (this.css) { this._tipDom.style.cssText += "; " + this.css; } this.tipDomContainer && this.tipDomContainer.appendChild(this._tipDom); if (this.pointerEvent) { this._tipDom.onmouseleave = function (e) { me.hide(e); }; // console.log('tipdom') // this._tipDom.onmouseout = function( event ){ // debugger // const relatedTarget = event.relatedTarget; // if (!me._tipDom.contains(relatedTarget) && relatedTarget !== me._tipDom) { // console.log("鼠标已离开目标 div"); // // 执行你的逻辑 // me.hide() // } // } } return this._tipDom; } } }, { key: "_removeContent", value: function _removeContent() { if (!this._tipDom) return; this._tipDom.style.visibility = 'hidden'; if (!this.viewPath) { //viewPath的话就不需要清除tipDom this.tipDomContainer && this.tipDomContainer.removeChild(this._tipDom); this._tipDom = null; } } }, { key: "_setContent", value: function _setContent(e) { var _this4 = this; var me = this; return new Promise(function (resolve) { var tipxContent = _this4._getContent(e); if (!tipxContent && tipxContent !== 0) { resolve(''); return; } ; if (!_this4._tipDom) { _this4._eventInfoStr = ''; _this4._tipDom = _this4._creatTipDom(e); } ; //小程序等场景就无法创建_tipDom if (_this4._tipDom) { var insetToTipDom = function insetToTipDom(tipxContent) { //判断数据中的nodeInfo数据和x周数据没变的话就不用重新innerHTML var newEventInfo = {}; var keys = ['iNode', 'nodes', 'title', 'xAxis']; //这几个属性会保存起来做对比 var _loop = function _loop() { if (keys.indexOf(k) > -1) { newEventInfo[k] = e.eventInfo[k]; if (k == 'nodes') { var newNodes = []; e.eventInfo[k].forEach(function (node) { var newNode = {}; Object.assign(newNode, node); delete newNode.nodeElement; newNodes.push(newNode); }); newEventInfo[k] = newNodes; } } }; for (var k in e.eventInfo) { _loop(); } var _eventInfoStr; try { _eventInfoStr = (0, _tools.stringifyIgnoreCycles)(newEventInfo); } catch (error) {} if (_eventInfoStr != me._eventInfoStr) { me._tipDom.innerHTML = tipxContent; if (me.viewPath) { me.innerHTMLEnd && me.innerHTMLEnd(e, me); } } me._eventInfoStr = _eventInfoStr; me.dW = me._tipDom.offsetWidth; me.dH = me._tipDom.offsetHeight; resolve(tipxContent); }; if (tipxContent.then) { tipxContent.then(function (tipxContent) { insetToTipDom(tipxContent); }); } else { insetToTipDom(tipxContent); } } ; }); } }, { key: "_getContent", value: function _getContent(e) { var content = ''; if (this.triggers && this.triggers.indexOf(e.eventInfo.trigger) == -1) { // } else { if (e.eventInfo.tipsContent) { content = _.isFunction(e.eventInfo.tipsContent) ? e.eventInfo.tipsContent(e.eventInfo, e) : e.eventInfo.tipsContent; } ; if (!content) { if (this.content) { content = _.isFunction(this.content) ? this.content(e.eventInfo, e) : this.content; } else { content = this._getDefaultContent(e.eventInfo); } } } return content; } }, { key: "_getDefaultContent", value: function _getDefaultContent(info) { if (this.viewPath) { str = "<div id='".concat(this.uuid, "' viewpath='").concat(this.viewPath, "' style=\"width:").concat(this.viewWidth, ";height:").concat(this.viewHeight, ";\"></div>"); return str; } var _coord = this.app.getComponent({ name: 'coord' }); var str = ""; if (!info.nodes.length) { return str; } ; var hasNodesContent = false; if (info.nodes.length) { str += "<table >"; if (info.title !== undefined && info.title !== null && info.title !== "") { str += "<tr><td colspan='2' style='text-align:left;padding-left:3px;'>"; str += "<span style='font-size:12px;padding:4px;color:#333;'>" + info.title + "</span>"; str += "</td></tr>"; hasNodesContent = true; } ; _.each(info.nodes, function (node, i) { // if (!node.value && node.value !== 0) { // return; // }; var style = node.color || node.fillStyle || node.strokeStyle; var name, value; var fieldConfig = _coord.getFieldConfig(node.field); //node.name优先级最高,是因为像 pie funnel cloud 等一维图表,会有name属性 //关系图中会有content name = node.name || node.label || (fieldConfig || {}).name || node.content || node.field || ''; str += "<tr>"; if ((0, _typeof2.default)(node.value) == 'object') { //主要是用在散点图的情况 if (node.value && node.value.x) { var xfieldConfig = _coord.getFieldConfig(info.xAxis.field); var xName = xfieldConfig && xfieldConfig.name || info.xAxis.field; var xvalue = xfieldConfig ? xfieldConfig.getFormatValue(node.value.x) : node.value.x; str += "<td style='padding:0px 6px;'>" + xName + ":<span style='color:" + style + "'>" + xvalue + "</span></td>"; hasNodesContent = true; } if (node.value && node.value.y) { value = fieldConfig ? fieldConfig.getFormatValue(node.value.y) : node.value.y; str += "<td style='padding:0px 6px;'>" + name + ":<span style='color:" + style + "'>" + value + "</span></td>"; hasNodesContent = true; } } else { value = fieldConfig ? fieldConfig.getFormatValue(node.value) : node.value; var hasValue = node.value || node.value === 0; if (!hasValue && !node.__no_value) { style = "#ddd"; value = '--'; } if (!node.__no__name) { str += "<td style='padding:0px 6px;color:" + (!hasValue && !node.__no_value ? '#ddd' : '#a0a0a0;') + "'>" + name + "</td>"; hasNodesContent = true; } if (!node.__no_value) { str += "<td style='padding:0px 6px;font-weight:bold;'>"; str += "<span style='color:" + style + "'>" + value + "</span>"; if (node.subValue) { str += "<span style='padding-left:6px;font-weight:normal;'>" + node.subValue + "</span>"; hasNodesContent = true; } ; str += "</td>"; } } str += "</tr>"; }); str += "</table>"; } if (!hasNodesContent) { str = ""; } return str; } /** *检测是x方向超过了视窗 */ }, { key: "_checkX", value: function _checkX(x) { var w = this.dW + 2; //后面的2 是 两边的 linewidth var left = 0; var clientWidth = document.documentElement.clientWidth; if (x < left) { x = left; } else if (x + w > clientWidth) { x = clientWidth - w; } return x; } /** *检测是y方向超过了视窗 */ }, { key: "_checkY", value: function _checkY(y) { var h = this.dH + 2; //后面的2 是 两边的 linewidth var top = 0; var clientHeight = document.documentElement.clientHeight; if (y < top) { y = top; } else if (y + h > clientHeight) { y = clientHeight - h; } return y; } }, { key: "_tipsPointerShow", value: function _tipsPointerShow(e) { var _this5 = this; //legend等组件上面的tips是没有xAxis等轴信息的 if (!e.eventInfo || !e.eventInfo.xAxis) { return; } ; var _coord = this.app.getComponent({ name: 'coord' }); //目前只实现了直角坐标系的tipsPointer if (!_coord || _coord.type != 'rect') return; if (!this.pointer) return; //自动检测到如果数据里有一个柱状图的数据, 那么就启用region的pointer e.eventInfo.nodes.forEach(function (node) { if (node.type == "bar") { _this5.pointer = "region"; } }); var el = this._tipsPointer; var y = _coord.origin.y - _coord.height; var x = 0; if (this.pointer == "line") { x = _coord.origin.x + e.eventInfo.xAxis.x; } if (this.pointer == "region") { var regionWidth = _coord._xAxis.getCellLengthOfPos(e.eventInfo.xAxis.x); x = _coord.origin.x + e.eventInfo.xAxis.x - regionWidth / 2; if (e.eventInfo.xAxis.ind < 0) { //当没有任何数据的时候, e.eventInfo.xAxis.ind==-1 x = _coord.origin.x; } } if (!el) { if (this.pointer == "line") { el = new Line({ //xyToInt : false, context: { x: x, y: y, start: { x: 0, y: 0 }, end: { x: 0, y: _coord.height }, lineWidth: this.pointerLineWidth, strokeStyle: this.pointerColor } }); } ; if (this.pointer == "region") { var _regionWidth = _coord._xAxis.getCellLengthOfPos(x); el = new Rect({ //xyToInt : false, context: { width: _regionWidth, height: _coord.height, x: x, y: y, fillStyle: this.pointerColor, globalAlpha: this.pointerRegionAlpha } }); } ; this.app.graphsSprite.addChild(el, 0); this._tipsPointer = el; } else { if (this.pointerAnim && _coord._xAxis.layoutType != "proportion") { if (el.__animation) { el.__animation.stop(); } ; el.__animation = el.animate({ x: x, y: y }, { duration: 200 }); } else { el.context.x = x; el.context.y = y; } } } }, { key: "_tipsPointerHide", value: function _tipsPointerHide(e) { //legend等组件上面的tips是没有xAxis等轴信息的 if (!e.eventInfo || !e.eventInfo.xAxis) { return; } ; var _coord = this.app.getComponent({ name: 'coord' }); //目前只实现了直角坐标系的tipsPointer if (!_coord || _coord.type != 'rect') return; if (!this.pointer || !this._tipsPointer) return; this._tipsPointer.destroy(); this._tipsPointer = null; } }, { key: "_tipsPointerMove", value: function _tipsPointerMove(e) { //legend等组件上面的tips是没有xAxis等轴信息的 if (!e.eventInfo || !e.eventInfo.xAxis) { return; } ; var _coord = this.app.getComponent({ name: 'coord' }); //目前只实现了直角坐标系的tipsPointer if (!_coord || _coord.type != 'rect') return; if (!this.pointer || !this._tipsPointer) return; var el = this._tipsPointer; var x = _coord.origin.x + e.eventInfo.xAxis.x; if (this.pointer == "region") { var regionWidth = _coord._xAxis.getCellLengthOfPos(e.eventInfo.xAxis.x); x = _coord.origin.x + e.eventInfo.xAxis.x - regionWidth / 2; if (e.eventInfo.xAxis.ind < 0) { //当没有任何数据的时候, e.eventInfo.xAxis.ind==-1 x = _coord.origin.x; } } ; var y = _coord.origin.y - _coord.height; if (x == el.__targetX) { return; } ; if (this.pointerAnim && _coord._xAxis.layoutType != "proportion") { if (el.__animation) { el.__animation.stop(); } ; el.__targetX = x; el.__animation = el.animate({ x: x, y: y }, { duration: 200, onComplete: function onComplete() { delete el.__targetX; delete el.__animation; } }); } else { el.context.x = x; el.context.y = y; } } }], [{ key: "defaultProps", value: function defaultProps() { return { enabled: { detail: '是否开启Tips', default: true }, content: { detail: '自定义tips的内容(html)', default: null }, containerIsBody: { detail: 'tips的html内容是否放到body下面,默认true,false则放到图表自身的容器内', default: true }, css: { detail: '给tips设置额外的css text', default: '' }, defaultXVal: { detail: '图表初始化默认显示一个x轴值对应的tip', default: null }, borderRadius: { detail: 'tips的边框圆角半径', default: 5 }, strokeStyle: { detail: 'tips边框颜色', default: '#ccc' }, fillStyle: { detail: 'tips背景色', default: 'rgba(255,255,255,0.95)' }, fontColor: { detail: 'tips文本颜色', default: '#999999' }, positionOfPoint: { detail: 'tips在触发点的位置,默认在右侧', default: 'right' }, pointerEvent: { detail: 'tips的浮层是否开启事件监听,可以出发click等事件', default: false }, triggers: { detail: 'e.eventInfo.trigger中哪些trigger可以出发这个tips,默认全部,有配置的话就只有配置的才能出发,比如 "this.node"', default: null }, offsetX: { detail: 'tips内容到鼠标位置的偏移量x', default: 10 }, offsetY: { detail: 'tips内容到鼠标位置的偏移量y', default: 10 }, positionInRange: { detail: 'tip的浮层是否限定在画布区域(废弃)', default: false }, followPointer: { detail: 'mousemove的时候是否改动tips的位置', default: true }, viewPath: { detail: '内容的目标viewPath', default: null }, viewWidth: { detail: '内容的目标width', default: 'auto' }, viewHeight: { detail: '内容的目标Height', default: 'auto' }, innerHTMLEnd: { detail: '和viewPath组合使用,当设置了tips的viewPath的时候,用户可以通过innerHTMLEnd去初始化viewPath对应的magixView', default: null }, innerHTMLClear: { detail: '清除tips后的回调,和innerHTMLEnd成对使用', default: null }, pointer: { detail: '触发tips的时候的指针样式', default: 'line', documentation: 'tips的指针,默认为直线,可选为:"line" | "region"(柱状图中一般用region)' }, pointerColor: { detail: 'tips指针样式的颜色', default: "#ccc" }, pointerLineWidth: { detail: 'pointer为line的时候,设置指针line的线宽,默认1.5', default: 1 }, pointerRegionAlpha: { detail: 'pointer为region的时候,设置指针region的透明度', default: 0.38 }, pointerAnim: { detail: 'tips移动的时候,指针是否开启动画', default: true }, linkageName: { detail: 'tips的多图表联动,相同的图表会执行事件联动,这个属性注意要保证y轴的width是一致的', default: null }, onshow: { detail: 'show的时候的事件', default: function _default() {} }, onmove: { detail: 'move的时候的事件', default: function _default() {} }, onhide: { detail: 'hide的时候的事件', default: function _default() {} } }; } }]); }(_component.default); _component.default.registerComponent(Tips, 'tips'); var _default2 = exports.default = Tips;