| /* |
| * 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. |
| */ |
| import * as zrUtil from 'zrender/src/core/util'; |
| import * as graphic from '../../util/graphic'; |
| var NodeHighlightPolicy = { |
| NONE: 'none', |
| // not downplay others |
| DESCENDANT: 'descendant', |
| ANCESTOR: 'ancestor', |
| SELF: 'self' |
| }; |
| var DEFAULT_SECTOR_Z = 2; |
| var DEFAULT_TEXT_Z = 4; |
| /** |
| * Sunburstce of Sunburst including Sector, Label, LabelLine |
| * @constructor |
| * @extends {module:zrender/graphic/Group} |
| */ |
| |
| function SunburstPiece(node, seriesModel, ecModel) { |
| graphic.Group.call(this); |
| var sector = new graphic.Sector({ |
| z2: DEFAULT_SECTOR_Z |
| }); |
| sector.seriesIndex = seriesModel.seriesIndex; |
| var text = new graphic.Text({ |
| z2: DEFAULT_TEXT_Z, |
| silent: node.getModel('label').get('silent') |
| }); |
| this.add(sector); |
| this.add(text); |
| this.updateData(true, node, 'normal', seriesModel, ecModel); // Hover to change label and labelLine |
| |
| function onEmphasis() { |
| text.ignore = text.hoverIgnore; |
| } |
| |
| function onNormal() { |
| text.ignore = text.normalIgnore; |
| } |
| |
| this.on('emphasis', onEmphasis).on('normal', onNormal).on('mouseover', onEmphasis).on('mouseout', onNormal); |
| } |
| |
| var SunburstPieceProto = SunburstPiece.prototype; |
| |
| SunburstPieceProto.updateData = function (firstCreate, node, state, seriesModel, ecModel) { |
| this.node = node; |
| node.piece = this; |
| seriesModel = seriesModel || this._seriesModel; |
| ecModel = ecModel || this._ecModel; |
| var sector = this.childAt(0); |
| sector.dataIndex = node.dataIndex; |
| var itemModel = node.getModel(); |
| var layout = node.getLayout(); // if (!layout) { |
| // console.log(node.getLayout()); |
| // } |
| |
| var sectorShape = zrUtil.extend({}, layout); |
| sectorShape.label = null; |
| var visualColor = getNodeColor(node, seriesModel, ecModel); |
| fillDefaultColor(node, seriesModel, visualColor); |
| var normalStyle = itemModel.getModel('itemStyle').getItemStyle(); |
| var style; |
| |
| if (state === 'normal') { |
| style = normalStyle; |
| } else { |
| var stateStyle = itemModel.getModel(state + '.itemStyle').getItemStyle(); |
| style = zrUtil.merge(stateStyle, normalStyle); |
| } |
| |
| style = zrUtil.defaults({ |
| lineJoin: 'bevel', |
| fill: style.fill || visualColor |
| }, style); |
| |
| if (firstCreate) { |
| sector.setShape(sectorShape); |
| sector.shape.r = layout.r0; |
| graphic.updateProps(sector, { |
| shape: { |
| r: layout.r |
| } |
| }, seriesModel, node.dataIndex); |
| sector.useStyle(style); |
| } else if (typeof style.fill === 'object' && style.fill.type || typeof sector.style.fill === 'object' && sector.style.fill.type) { |
| // Disable animation for gradient since no interpolation method |
| // is supported for gradient |
| graphic.updateProps(sector, { |
| shape: sectorShape |
| }, seriesModel); |
| sector.useStyle(style); |
| } else { |
| graphic.updateProps(sector, { |
| shape: sectorShape, |
| style: style |
| }, seriesModel); |
| } |
| |
| this._updateLabel(seriesModel, visualColor, state); |
| |
| var cursorStyle = itemModel.getShallow('cursor'); |
| cursorStyle && sector.attr('cursor', cursorStyle); |
| |
| if (firstCreate) { |
| var highlightPolicy = seriesModel.getShallow('highlightPolicy'); |
| |
| this._initEvents(sector, node, seriesModel, highlightPolicy); |
| } |
| |
| this._seriesModel = seriesModel || this._seriesModel; |
| this._ecModel = ecModel || this._ecModel; |
| }; |
| |
| SunburstPieceProto.onEmphasis = function (highlightPolicy) { |
| var that = this; |
| this.node.hostTree.root.eachNode(function (n) { |
| if (n.piece) { |
| if (that.node === n) { |
| n.piece.updateData(false, n, 'emphasis'); |
| } else if (isNodeHighlighted(n, that.node, highlightPolicy)) { |
| n.piece.childAt(0).trigger('highlight'); |
| } else if (highlightPolicy !== NodeHighlightPolicy.NONE) { |
| n.piece.childAt(0).trigger('downplay'); |
| } |
| } |
| }); |
| }; |
| |
| SunburstPieceProto.onNormal = function () { |
| this.node.hostTree.root.eachNode(function (n) { |
| if (n.piece) { |
| n.piece.updateData(false, n, 'normal'); |
| } |
| }); |
| }; |
| |
| SunburstPieceProto.onHighlight = function () { |
| this.updateData(false, this.node, 'highlight'); |
| }; |
| |
| SunburstPieceProto.onDownplay = function () { |
| this.updateData(false, this.node, 'downplay'); |
| }; |
| |
| SunburstPieceProto._updateLabel = function (seriesModel, visualColor, state) { |
| var itemModel = this.node.getModel(); |
| var normalModel = itemModel.getModel('label'); |
| var labelModel = state === 'normal' || state === 'emphasis' ? normalModel : itemModel.getModel(state + '.label'); |
| var labelHoverModel = itemModel.getModel('emphasis.label'); |
| var text = zrUtil.retrieve(seriesModel.getFormattedLabel(this.node.dataIndex, state, null, null, 'label'), this.node.name); |
| |
| if (getLabelAttr('show') === false) { |
| text = ''; |
| } |
| |
| var layout = this.node.getLayout(); |
| var labelMinAngle = labelModel.get('minAngle'); |
| |
| if (labelMinAngle == null) { |
| labelMinAngle = normalModel.get('minAngle'); |
| } |
| |
| labelMinAngle = labelMinAngle / 180 * Math.PI; |
| var angle = layout.endAngle - layout.startAngle; |
| |
| if (labelMinAngle != null && Math.abs(angle) < labelMinAngle) { |
| // Not displaying text when angle is too small |
| text = ''; |
| } |
| |
| var label = this.childAt(1); |
| graphic.setLabelStyle(label.style, label.hoverStyle || {}, normalModel, labelHoverModel, { |
| defaultText: labelModel.getShallow('show') ? text : null, |
| autoColor: visualColor, |
| useInsideStyle: true |
| }); |
| var midAngle = (layout.startAngle + layout.endAngle) / 2; |
| var dx = Math.cos(midAngle); |
| var dy = Math.sin(midAngle); |
| var r; |
| var labelPosition = getLabelAttr('position'); |
| var labelPadding = getLabelAttr('distance') || 0; |
| var textAlign = getLabelAttr('align'); |
| |
| if (labelPosition === 'outside') { |
| r = layout.r + labelPadding; |
| textAlign = midAngle > Math.PI / 2 ? 'right' : 'left'; |
| } else { |
| if (!textAlign || textAlign === 'center') { |
| r = (layout.r + layout.r0) / 2; |
| textAlign = 'center'; |
| } else if (textAlign === 'left') { |
| r = layout.r0 + labelPadding; |
| |
| if (midAngle > Math.PI / 2) { |
| textAlign = 'right'; |
| } |
| } else if (textAlign === 'right') { |
| r = layout.r - labelPadding; |
| |
| if (midAngle > Math.PI / 2) { |
| textAlign = 'left'; |
| } |
| } |
| } |
| |
| label.attr('style', { |
| text: text, |
| textAlign: textAlign, |
| textVerticalAlign: getLabelAttr('verticalAlign') || 'middle', |
| opacity: getLabelAttr('opacity') |
| }); |
| var textX = r * dx + layout.cx; |
| var textY = r * dy + layout.cy; |
| label.attr('position', [textX, textY]); |
| var rotateType = getLabelAttr('rotate'); |
| var rotate = 0; |
| |
| if (rotateType === 'radial') { |
| rotate = -midAngle; |
| |
| if (rotate < -Math.PI / 2) { |
| rotate += Math.PI; |
| } |
| } else if (rotateType === 'tangential') { |
| rotate = Math.PI / 2 - midAngle; |
| |
| if (rotate > Math.PI / 2) { |
| rotate -= Math.PI; |
| } else if (rotate < -Math.PI / 2) { |
| rotate += Math.PI; |
| } |
| } else if (typeof rotateType === 'number') { |
| rotate = rotateType * Math.PI / 180; |
| } |
| |
| label.attr('rotation', rotate); |
| |
| function getLabelAttr(name) { |
| var stateAttr = labelModel.get(name); |
| |
| if (stateAttr == null) { |
| return normalModel.get(name); |
| } else { |
| return stateAttr; |
| } |
| } |
| }; |
| |
| SunburstPieceProto._initEvents = function (sector, node, seriesModel, highlightPolicy) { |
| sector.off('mouseover').off('mouseout').off('emphasis').off('normal'); |
| var that = this; |
| |
| var onEmphasis = function () { |
| that.onEmphasis(highlightPolicy); |
| }; |
| |
| var onNormal = function () { |
| that.onNormal(); |
| }; |
| |
| var onDownplay = function () { |
| that.onDownplay(); |
| }; |
| |
| var onHighlight = function () { |
| that.onHighlight(); |
| }; |
| |
| if (seriesModel.isAnimationEnabled()) { |
| sector.on('mouseover', onEmphasis).on('mouseout', onNormal).on('emphasis', onEmphasis).on('normal', onNormal).on('downplay', onDownplay).on('highlight', onHighlight); |
| } |
| }; |
| |
| zrUtil.inherits(SunburstPiece, graphic.Group); |
| export default SunburstPiece; |
| /** |
| * Get node color |
| * |
| * @param {TreeNode} node the node to get color |
| * @param {module:echarts/model/Series} seriesModel series |
| * @param {module:echarts/model/Global} ecModel echarts defaults |
| */ |
| |
| function getNodeColor(node, seriesModel, ecModel) { |
| // Color from visualMap |
| var visualColor = node.getVisual('color'); |
| var visualMetaList = node.getVisual('visualMeta'); |
| |
| if (!visualMetaList || visualMetaList.length === 0) { |
| // Use first-generation color if has no visualMap |
| visualColor = null; |
| } // Self color or level color |
| |
| |
| var color = node.getModel('itemStyle').get('color'); |
| |
| if (color) { |
| return color; |
| } else if (visualColor) { |
| // Color mapping |
| return visualColor; |
| } else if (node.depth === 0) { |
| // Virtual root node |
| return ecModel.option.color[0]; |
| } else { |
| // First-generation color |
| var length = ecModel.option.color.length; |
| color = ecModel.option.color[getRootId(node) % length]; |
| } |
| |
| return color; |
| } |
| /** |
| * Get index of root in sorted order |
| * |
| * @param {TreeNode} node current node |
| * @return {number} index in root |
| */ |
| |
| |
| function getRootId(node) { |
| var ancestor = node; |
| |
| while (ancestor.depth > 1) { |
| ancestor = ancestor.parentNode; |
| } |
| |
| var virtualRoot = node.getAncestors()[0]; |
| return zrUtil.indexOf(virtualRoot.children, ancestor); |
| } |
| |
| function isNodeHighlighted(node, activeNode, policy) { |
| if (policy === NodeHighlightPolicy.NONE) { |
| return false; |
| } else if (policy === NodeHighlightPolicy.SELF) { |
| return node === activeNode; |
| } else if (policy === NodeHighlightPolicy.ANCESTOR) { |
| return node === activeNode || node.isAncestorOf(activeNode); |
| } else { |
| return node === activeNode || node.isDescendantOf(activeNode); |
| } |
| } // Fix tooltip callback function params.color incorrect when pick a default color |
| |
| |
| function fillDefaultColor(node, seriesModel, color) { |
| var data = seriesModel.getData(); |
| data.setItemVisual(node.dataIndex, 'color', color); |
| } |