SunburstSeries.ts 7.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
* 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.
*/

20 21
import * as zrUtil from 'zrender/src/core/util';
import SeriesModel from '../../model/Series';
P
pissang 已提交
22
import Tree, { TreeNode } from '../../data/Tree';
23
import {wrapTreePathInfo} from '../helper/treeHelper';
P
pissang 已提交
24 25 26 27 28 29
import {
    SeriesOption,
    CircleLayoutOptionMixin,
    LabelOption,
    ItemStyleOption,
    OptionDataValue,
30 31 32
    CallbackDataParams,
    StatesOptionMixin,
    OptionDataItemObject
P
pissang 已提交
33 34 35
} from '../../util/types';
import GlobalModel from '../../model/Global';

P
pissang 已提交
36
interface SunburstLabelOption extends Omit<LabelOption, 'rotate' | 'position'> {
P
pissang 已提交
37 38 39
    rotate?: 'radial' | 'tangential' | number
    minAngle?: number
    silent?: boolean
P
pissang 已提交
40
    position?: LabelOption['position'] | 'outside'
P
pissang 已提交
41 42 43 44 45 46
}

interface SunburstDataParams extends CallbackDataParams {
    treePathInfo: {
        name: string,
        dataIndex: number
47
        value: SunburstSeriesNodeItemOption['value']
P
pissang 已提交
48 49
    }[]
}
O
Ovilia 已提交
50

P
pissang 已提交
51 52 53 54 55 56
interface ExtraStateOption {
    emphasis?: {
        focus?: 'descendant' | 'ancestor'
    }
}

57 58 59 60
export interface SunburstStateOption {
    itemStyle?: ItemStyleOption
    label?: SunburstLabelOption
}
O
Ovilia 已提交
61

62
export interface SunburstSeriesNodeItemOption extends
P
pissang 已提交
63
    SunburstStateOption, StatesOptionMixin<SunburstStateOption, ExtraStateOption>,
64 65
    OptionDataItemObject<OptionDataValue>
{
P
pissang 已提交
66 67 68 69 70
    nodeClick?: 'rootToNode' | 'link'
    // Available when nodeClick is link
    link?: string
    target?: string

71
    children?: SunburstSeriesNodeItemOption[]
P
pissang 已提交
72 73 74 75 76

    collapsed?: boolean

    cursor?: string
}
77
export interface SunburstSeriesLevelOption extends SunburstStateOption, StatesOptionMixin<SunburstStateOption> {
P
pissang 已提交
78 79 80 81 82
    highlight?: {
        itemStyle?: ItemStyleOption
        label?: SunburstLabelOption
    }
}
83
export interface SunburstSeriesOption extends
P
pissang 已提交
84
    SeriesOption<SunburstStateOption, ExtraStateOption>, SunburstStateOption,
85
    CircleLayoutOptionMixin {
86

P
pissang 已提交
87
    type?: 'sunburst'
O
Ovilia 已提交
88

P
pissang 已提交
89 90 91
    clockwise?: boolean
    startAngle?: number
    minAngle?: number
92
    /**
P
pissang 已提交
93
     * If still show when all data zero.
O
Ovilia 已提交
94
     */
P
pissang 已提交
95 96 97 98 99 100
    stillShowZeroSum?: boolean
    /**
     * Policy of highlighting pieces when hover on one
     * Valid values: 'none' (for not downplay others), 'descendant',
     * 'ancestor', 'self'
     */
101
    // highlightPolicy?: 'descendant' | 'ancestor' | 'self'
P
pissang 已提交
102 103 104 105 106 107

    nodeClick?: 'rootToNode' | 'link'

    renderLabelForZeroData?: boolean

    levels?: SunburstSeriesLevelOption[]
O
Ovilia 已提交
108

P
pissang 已提交
109 110 111 112 113 114 115 116
    animationType?: 'expansion' | 'scale'

    sort?: 'desc' | 'asc' | ((a: TreeNode, b: TreeNode) => number)
}

interface SunburstSeriesModel {
    getFormattedLabel(
        dataIndex: number,
117
        state?: 'emphasis' | 'normal' | 'highlight' | 'blur' | 'select'
P
pissang 已提交
118 119 120 121
    ): string
}
class SunburstSeriesModel extends SeriesModel<SunburstSeriesOption> {

1
100pah 已提交
122 123
    static readonly type = 'series.sunburst';
    readonly type = SunburstSeriesModel.type;
P
pissang 已提交
124

1
100pah 已提交
125
    private _viewRoot: TreeNode;
P
pissang 已提交
126 127

    getInitialData(option: SunburstSeriesOption, ecModel: GlobalModel) {
128
        // Create a virtual root.
129
        const root = { name: option.name, children: option.data };
130 131 132

        completeTreeValue(root);

133
        const levels = option.levels || [];
134 135 136

        // levels = option.levels = setDefault(levels, ecModel);

137
        const treeOption = {
P
pissang 已提交
138 139
            levels: levels
        };
140 141 142 143 144

        // Make sure always a new tree is created when setOption,
        // in TreemapView, we check whether oldTree === newTree
        // to choose mappings approach among old shapes and new shapes.
        return Tree.createTree(root, this, treeOption).data;
P
pissang 已提交
145
    }
O
Ovilia 已提交
146

P
pissang 已提交
147
    optionUpdated() {
148
        this.resetViewRoot();
P
pissang 已提交
149
    }
O
Ovilia 已提交
150 151 152 153

    /*
     * @override
     */
P
pissang 已提交
154
    getDataParams(dataIndex: number) {
155
        const params = super.getDataParams.apply(this, arguments as any) as SunburstDataParams;
156

157
        const node = this.getData().tree.getNodeByDataIndex(dataIndex);
158
        params.treePathInfo = wrapTreePathInfo<SunburstSeriesNodeItemOption['value']>(node, this);
159

O
Ovilia 已提交
160
        return params;
P
pissang 已提交
161
    }
O
Ovilia 已提交
162

P
pissang 已提交
163
    static defaultOption: SunburstSeriesOption = {
O
Ovilia 已提交
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
        zlevel: 0,
        z: 2,

        // 默认全局居中
        center: ['50%', '50%'],
        radius: [0, '75%'],
        // 默认顺时针
        clockwise: true,
        startAngle: 90,
        // 最小角度改为0
        minAngle: 0,

        // If still show when all data zero.
        stillShowZeroSum: true,

O
Ovilia 已提交
179 180
        // 'rootToNode', 'link', or false
        nodeClick: 'rootToNode',
181

O
Ovilia 已提交
182 183
        renderLabelForZeroData: false,

O
Ovilia 已提交
184
        label: {
O
Ovilia 已提交
185 186 187
            // could be: 'radial', 'tangential', or 'none'
            rotate: 'radial',
            show: true,
188
            opacity: 1,
O
Ovilia 已提交
189 190
            // 'left' is for inner side of inside, and 'right' is for outter
            // side for inside
O
Ovilia 已提交
191 192 193
            align: 'center',
            position: 'inside',
            distance: 5,
P
pissang 已提交
194
            silent: true
O
Ovilia 已提交
195 196
        },
        itemStyle: {
O
Ovilia 已提交
197 198
            borderWidth: 1,
            borderColor: 'white',
199 200 201 202 203
            borderType: 'solid',
            shadowBlur: 0,
            shadowColor: 'rgba(0, 0, 0, 0.2)',
            shadowOffsetX: 0,
            shadowOffsetY: 0,
P
pissang 已提交
204 205
            opacity: 1
        },
206

207 208 209 210
        emphasis: {
            focus: 'descendant'
        },

211
        blur: {
P
pissang 已提交
212
            itemStyle: {
213
                opacity: 0.2
214 215
            },
            label: {
216
                opacity: 0.1
217
            }
O
Ovilia 已提交
218 219 220 221
        },

        // Animation type canbe expansion, scale
        animationType: 'expansion',
O
Ovilia 已提交
222
        animationDuration: 1000,
O
Ovilia 已提交
223
        animationDurationUpdate: 500,
O
Ovilia 已提交
224

O
Ovilia 已提交
225 226
        data: [],

O
Ovilia 已提交
227 228
        levels: [],

O
Ovilia 已提交
229 230 231 232 233 234 235 236 237 238 239 240
        /**
         * Sort order.
         *
         * Valid values: 'desc', 'asc', null, or callback function.
         * 'desc' and 'asc' for descend and ascendant order;
         * null for not sorting;
         * example of callback function:
         * function(nodeA, nodeB) {
         *     return nodeA.getValue() - nodeB.getValue();
         * }
         */
        sort: 'desc'
1
100pah 已提交
241
    };
242

P
pissang 已提交
243
    getViewRoot() {
244
        return this._viewRoot;
P
pissang 已提交
245
    }
246

P
pissang 已提交
247
    resetViewRoot(viewRoot?: TreeNode) {
248 249 250 251
        viewRoot
            ? (this._viewRoot = viewRoot)
            : (viewRoot = this._viewRoot);

252
        const root = this.getRawData().tree.root;
253 254 255 256 257 258

        if (!viewRoot
            || (viewRoot !== root && !root.contains(viewRoot))
        ) {
            this._viewRoot = root;
        }
O
Ovilia 已提交
259
    }
P
pissang 已提交
260
}
O
Ovilia 已提交
261

262 263


264
function completeTreeValue(dataNode: SunburstSeriesNodeItemOption) {
265 266 267
    // Postorder travel tree.
    // If value of none-leaf node is not set,
    // calculate it by suming up the value of all children.
268
    let sum = 0;
269 270 271 272 273

    zrUtil.each(dataNode.children, function (child) {

        completeTreeValue(child);

274
        let childValue = child.value;
P
pissang 已提交
275
        // TODO First value of array must be a number
276
        zrUtil.isArray(childValue) && (childValue = childValue[0]);
P
pissang 已提交
277
        sum += childValue as number;
278 279
    });

280
    let thisValue = dataNode.value as number;
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
    if (zrUtil.isArray(thisValue)) {
        thisValue = thisValue[0];
    }

    if (thisValue == null || isNaN(thisValue)) {
        thisValue = sum;
    }
    // Value should not less than 0.
    if (thisValue < 0) {
        thisValue = 0;
    }

    zrUtil.isArray(dataNode.value)
        ? (dataNode.value[0] = thisValue)
        : (dataNode.value = thisValue);
}
P
pissang 已提交
297 298 299 300 301


SeriesModel.registerClass(SunburstSeriesModel);

export default SunburstSeriesModel;