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 * as zrUtil from 'zrender/src/core/util'; | |
| import ChartView from '../../view/Chart'; | |
| import * as graphic from '../../util/graphic'; | |
| import { setStatesStylesFromModel, toggleHoverEmphasis } from '../../util/states'; | |
| import Path, { PathProps } from 'zrender/src/graphic/Path'; | |
| import BoxplotSeriesModel, { BoxplotDataItemOption } from './BoxplotSeries'; | |
| import GlobalModel from '../../model/Global'; | |
| import ExtensionAPI from '../../core/ExtensionAPI'; | |
| import SeriesData from '../../data/SeriesData'; | |
| import { BoxplotItemLayout } from './boxplotLayout'; | |
| import { saveOldStyle } from '../../animation/basicTransition'; | |
| class BoxplotView extends ChartView { | |
| static type = 'boxplot'; | |
| type = BoxplotView.type; | |
| private _data: SeriesData; | |
| render(seriesModel: BoxplotSeriesModel, ecModel: GlobalModel, api: ExtensionAPI) { | |
| const data = seriesModel.getData(); | |
| const group = this.group; | |
| const oldData = this._data; | |
| // There is no old data only when first rendering or switching from | |
| // stream mode to normal mode, where previous elements should be removed. | |
| if (!this._data) { | |
| group.removeAll(); | |
| } | |
| const constDim = seriesModel.get('layout') === 'horizontal' ? 1 : 0; | |
| data.diff(oldData) | |
| .add(function (newIdx) { | |
| if (data.hasValue(newIdx)) { | |
| const itemLayout = data.getItemLayout(newIdx) as BoxplotItemLayout; | |
| const symbolEl = createNormalBox(itemLayout, data, newIdx, constDim, true); | |
| data.setItemGraphicEl(newIdx, symbolEl); | |
| group.add(symbolEl); | |
| } | |
| }) | |
| .update(function (newIdx, oldIdx) { | |
| let symbolEl = oldData.getItemGraphicEl(oldIdx) as BoxPath; | |
| // Empty data | |
| if (!data.hasValue(newIdx)) { | |
| group.remove(symbolEl); | |
| return; | |
| } | |
| const itemLayout = data.getItemLayout(newIdx) as BoxplotItemLayout; | |
| if (!symbolEl) { | |
| symbolEl = createNormalBox(itemLayout, data, newIdx, constDim); | |
| } | |
| else { | |
| saveOldStyle(symbolEl); | |
| updateNormalBoxData(itemLayout, symbolEl, data, newIdx); | |
| } | |
| group.add(symbolEl); | |
| data.setItemGraphicEl(newIdx, symbolEl); | |
| }) | |
| .remove(function (oldIdx) { | |
| const el = oldData.getItemGraphicEl(oldIdx); | |
| el && group.remove(el); | |
| }) | |
| .execute(); | |
| this._data = data; | |
| } | |
| remove(ecModel: GlobalModel) { | |
| const group = this.group; | |
| const data = this._data; | |
| this._data = null; | |
| data && data.eachItemGraphicEl(function (el) { | |
| el && group.remove(el); | |
| }); | |
| } | |
| } | |
| class BoxPathShape { | |
| points: number[][]; | |
| } | |
| interface BoxPathProps extends PathProps { | |
| shape?: Partial<BoxPathShape> | |
| } | |
| class BoxPath extends Path<BoxPathProps> { | |
| readonly type = 'boxplotBoxPath'; | |
| shape: BoxPathShape; | |
| constructor(opts?: BoxPathProps) { | |
| super(opts); | |
| } | |
| getDefaultShape() { | |
| return new BoxPathShape(); | |
| } | |
| buildPath(ctx: CanvasRenderingContext2D, shape: BoxPathShape) { | |
| const ends = shape.points; | |
| let i = 0; | |
| ctx.moveTo(ends[i][0], ends[i][1]); | |
| i++; | |
| for (; i < 4; i++) { | |
| ctx.lineTo(ends[i][0], ends[i][1]); | |
| } | |
| ctx.closePath(); | |
| for (; i < ends.length; i++) { | |
| ctx.moveTo(ends[i][0], ends[i][1]); | |
| i++; | |
| ctx.lineTo(ends[i][0], ends[i][1]); | |
| } | |
| } | |
| } | |
| function createNormalBox( | |
| itemLayout: BoxplotItemLayout, | |
| data: SeriesData, | |
| dataIndex: number, | |
| constDim: number, | |
| isInit?: boolean | |
| ) { | |
| const ends = itemLayout.ends; | |
| const el = new BoxPath({ | |
| shape: { | |
| points: isInit | |
| ? transInit(ends, constDim, itemLayout) | |
| : ends | |
| } | |
| }); | |
| updateNormalBoxData(itemLayout, el, data, dataIndex, isInit); | |
| return el; | |
| } | |
| function updateNormalBoxData( | |
| itemLayout: BoxplotItemLayout, | |
| el: BoxPath, | |
| data: SeriesData, | |
| dataIndex: number, | |
| isInit?: boolean | |
| ) { | |
| const seriesModel = data.hostModel; | |
| const updateMethod = graphic[isInit ? 'initProps' : 'updateProps']; | |
| updateMethod( | |
| el, | |
| {shape: {points: itemLayout.ends}}, | |
| seriesModel, | |
| dataIndex | |
| ); | |
| el.useStyle(data.getItemVisual(dataIndex, 'style')); | |
| el.style.strokeNoScale = true; | |
| el.z2 = 100; | |
| const itemModel = data.getItemModel<BoxplotDataItemOption>(dataIndex); | |
| const emphasisModel = itemModel.getModel('emphasis'); | |
| setStatesStylesFromModel(el, itemModel); | |
| toggleHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled')); | |
| } | |
| function transInit(points: number[][], dim: number, itemLayout: BoxplotItemLayout) { | |
| return zrUtil.map(points, function (point) { | |
| point = point.slice(); | |
| point[dim] = itemLayout.initBaseline; | |
| return point; | |
| }); | |
| } | |
| export default BoxplotView; | |