Spaces:
Running
Running
| /* | |
| * 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 { isFunction, extend, createHashMap } from 'zrender/src/core/util'; | |
| import { StageHandler, CallbackDataParams, ZRColor, Dictionary, InnerDecalObject } | |
| from '../util/types'; | |
| import makeStyleMapper from '../model/mixin/makeStyleMapper'; | |
| import { ITEM_STYLE_KEY_MAP } from '../model/mixin/itemStyle'; | |
| import { LINE_STYLE_KEY_MAP } from '../model/mixin/lineStyle'; | |
| import SeriesModel from '../model/Series'; | |
| import Model from '../model/Model'; | |
| import { makeInner } from '../util/model'; | |
| const inner = makeInner<{scope: object}, SeriesModel>(); | |
| const defaultStyleMappers = { | |
| itemStyle: makeStyleMapper(ITEM_STYLE_KEY_MAP, true), | |
| lineStyle: makeStyleMapper(LINE_STYLE_KEY_MAP, true) | |
| }; | |
| const defaultColorKey = { | |
| lineStyle: 'stroke', | |
| itemStyle: 'fill' | |
| } as const; | |
| function getStyleMapper(seriesModel: SeriesModel, stylePath: string) { | |
| const styleMapper = seriesModel.visualStyleMapper | |
| || defaultStyleMappers[stylePath as 'itemStyle' | 'lineStyle']; | |
| if (!styleMapper) { | |
| console.warn(`Unknown style type '${stylePath}'.`); | |
| return defaultStyleMappers.itemStyle; | |
| } | |
| return styleMapper; | |
| } | |
| function getDefaultColorKey(seriesModel: SeriesModel, stylePath: string): 'stroke' | 'fill' { | |
| // return defaultColorKey[stylePath] || | |
| const colorKey = seriesModel.visualDrawType | |
| || defaultColorKey[stylePath as 'itemStyle' | 'lineStyle']; | |
| if (!colorKey) { | |
| console.warn(`Unknown style type '${stylePath}'.`); | |
| return 'fill'; | |
| } | |
| return colorKey; | |
| } | |
| type ColorCallback = (params: CallbackDataParams) => ZRColor; | |
| const seriesStyleTask: StageHandler = { | |
| createOnAllSeries: true, | |
| performRawSeries: true, | |
| reset(seriesModel, ecModel) { | |
| const data = seriesModel.getData(); | |
| const stylePath = seriesModel.visualStyleAccessPath | |
| || 'itemStyle'; | |
| // Set in itemStyle | |
| const styleModel = seriesModel.getModel(stylePath as any); | |
| const getStyle = getStyleMapper(seriesModel, stylePath); | |
| const globalStyle = getStyle(styleModel); | |
| const decalOption = styleModel.getShallow('decal') as InnerDecalObject; | |
| if (decalOption) { | |
| data.setVisual('decal', decalOption); | |
| decalOption.dirty = true; | |
| } | |
| // TODO | |
| const colorKey = getDefaultColorKey(seriesModel, stylePath); | |
| const color = globalStyle[colorKey]; | |
| // TODO style callback | |
| const colorCallback = isFunction(color) ? color as unknown as ColorCallback : null; | |
| const hasAutoColor = globalStyle.fill === 'auto' || globalStyle.stroke === 'auto'; | |
| // Get from color palette by default. | |
| if (!globalStyle[colorKey] || colorCallback || hasAutoColor) { | |
| // Note: If some series has color specified (e.g., by itemStyle.color), we DO NOT | |
| // make it effect palette. Because some scenarios users need to make some series | |
| // transparent or as background, which should better not effect the palette. | |
| const colorPalette = seriesModel.getColorFromPalette( | |
| // TODO series count changed. | |
| seriesModel.name, null, ecModel.getSeriesCount() | |
| ); | |
| if (!globalStyle[colorKey]) { | |
| globalStyle[colorKey] = colorPalette; | |
| data.setVisual('colorFromPalette', true); | |
| } | |
| globalStyle.fill = (globalStyle.fill === 'auto' || isFunction(globalStyle.fill)) | |
| ? colorPalette | |
| : globalStyle.fill; | |
| globalStyle.stroke = (globalStyle.stroke === 'auto' || isFunction(globalStyle.stroke)) | |
| ? colorPalette | |
| : globalStyle.stroke; | |
| } | |
| data.setVisual('style', globalStyle); | |
| data.setVisual('drawType', colorKey); | |
| // Only visible series has each data be visual encoded | |
| if (!ecModel.isSeriesFiltered(seriesModel) && colorCallback) { | |
| data.setVisual('colorFromPalette', false); | |
| return { | |
| dataEach(data, idx) { | |
| const dataParams = seriesModel.getDataParams(idx); | |
| const itemStyle = extend({}, globalStyle); | |
| itemStyle[colorKey] = colorCallback(dataParams); | |
| data.setItemVisual(idx, 'style', itemStyle); | |
| } | |
| }; | |
| } | |
| } | |
| }; | |
| const sharedModel = new Model(); | |
| const dataStyleTask: StageHandler = { | |
| createOnAllSeries: true, | |
| performRawSeries: true, | |
| reset(seriesModel, ecModel) { | |
| if (seriesModel.ignoreStyleOnData || ecModel.isSeriesFiltered(seriesModel)) { | |
| return; | |
| } | |
| const data = seriesModel.getData(); | |
| const stylePath = seriesModel.visualStyleAccessPath | |
| || 'itemStyle'; | |
| // Set in itemStyle | |
| const getStyle = getStyleMapper(seriesModel, stylePath); | |
| const colorKey = data.getVisual('drawType'); | |
| return { | |
| dataEach: data.hasItemOption ? function (data, idx) { | |
| // Not use getItemModel for performance considuration | |
| const rawItem = data.getRawDataItem(idx) as any; | |
| if (rawItem && rawItem[stylePath]) { | |
| sharedModel.option = rawItem[stylePath]; | |
| const style = getStyle(sharedModel); | |
| const existsStyle = data.ensureUniqueItemVisual(idx, 'style'); | |
| extend(existsStyle, style); | |
| if (sharedModel.option.decal) { | |
| data.setItemVisual(idx, 'decal', sharedModel.option.decal); | |
| sharedModel.option.decal.dirty = true; | |
| } | |
| if (colorKey in style) { | |
| data.setItemVisual(idx, 'colorFromPalette', false); | |
| } | |
| } | |
| } : null | |
| }; | |
| } | |
| }; | |
| // Pick color from palette for the data which has not been set with color yet. | |
| // Note: do not support stream rendering. No such cases yet. | |
| const dataColorPaletteTask: StageHandler = { | |
| performRawSeries: true, | |
| overallReset(ecModel) { | |
| // Each type of series uses one scope. | |
| // Pie and funnel are using different scopes. | |
| const paletteScopeGroupByType = createHashMap<object>(); | |
| ecModel.eachSeries((seriesModel: SeriesModel) => { | |
| const colorBy = seriesModel.getColorBy(); | |
| if (seriesModel.isColorBySeries()) { | |
| return; | |
| } | |
| const key = seriesModel.type + '-' + colorBy; | |
| let colorScope = paletteScopeGroupByType.get(key); | |
| if (!colorScope) { | |
| colorScope = {}; | |
| paletteScopeGroupByType.set(key, colorScope); | |
| } | |
| inner(seriesModel).scope = colorScope; | |
| }); | |
| ecModel.eachSeries((seriesModel: SeriesModel) => { | |
| if (seriesModel.isColorBySeries() || ecModel.isSeriesFiltered(seriesModel)) { | |
| return; | |
| } | |
| const dataAll = seriesModel.getRawData(); | |
| const idxMap: Dictionary<number> = {}; | |
| const data = seriesModel.getData(); | |
| const colorScope = inner(seriesModel).scope; | |
| const stylePath = seriesModel.visualStyleAccessPath | |
| || 'itemStyle'; | |
| const colorKey = getDefaultColorKey(seriesModel, stylePath); | |
| data.each(function (idx) { | |
| const rawIdx = data.getRawIndex(idx); | |
| idxMap[rawIdx] = idx; | |
| }); | |
| // Iterate on data before filtered. To make sure color from palette can be | |
| // Consistent when toggling legend. | |
| dataAll.each(function (rawIdx) { | |
| const idx = idxMap[rawIdx]; | |
| const fromPalette = data.getItemVisual(idx, 'colorFromPalette'); | |
| // Get color from palette for each data only when the color is inherited from series color, which is | |
| // also picked from color palette. So following situation is not in the case: | |
| // 1. series.itemStyle.color is set | |
| // 2. color is encoded by visualMap | |
| if (fromPalette) { | |
| const itemStyle = data.ensureUniqueItemVisual(idx, 'style'); | |
| const name = dataAll.getName(rawIdx) || (rawIdx + ''); | |
| const dataCount = dataAll.count(); | |
| itemStyle[colorKey] = seriesModel.getColorFromPalette(name, colorScope, dataCount); | |
| } | |
| }); | |
| }); | |
| } | |
| }; | |
| export { | |
| seriesStyleTask, | |
| dataStyleTask, | |
| dataColorPaletteTask | |
| }; | |