|  | /* | 
|  | * 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 DataZoomView from './DataZoomView'; | 
|  | import sliderMove from '../helper/sliderMove'; | 
|  | import * as roams from './roams'; | 
|  | import InsideZoomModel from './InsideZoomModel'; | 
|  | import GlobalModel from '../../model/Global'; | 
|  | import ExtensionAPI from '../../core/ExtensionAPI'; | 
|  | import { bind } from 'zrender/src/core/util'; | 
|  | import RoamController, {RoamEventParams} from '../helper/RoamController'; | 
|  | import { AxisBaseModel } from '../../coord/AxisBaseModel'; | 
|  | import Polar from '../../coord/polar/Polar'; | 
|  | import SingleAxis from '../../coord/single/SingleAxis'; | 
|  | import { DataZoomCoordSysMainType, DataZoomReferCoordSysInfo } from './helper'; | 
|  |  | 
|  |  | 
|  | class InsideZoomView extends DataZoomView { | 
|  | static type = 'dataZoom.inside'; | 
|  | type = 'dataZoom.inside'; | 
|  |  | 
|  | /** | 
|  | * 'throttle' is used in this.dispatchAction, so we save range | 
|  | * to avoid missing some 'pan' info. | 
|  | */ | 
|  | range: number[]; | 
|  |  | 
|  | render(dataZoomModel: InsideZoomModel, ecModel: GlobalModel, api: ExtensionAPI) { | 
|  | super.render.apply(this, arguments as any); | 
|  |  | 
|  | if (dataZoomModel.noTarget()) { | 
|  | this._clear(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Hence the `throttle` util ensures to preserve command order, | 
|  | // here simply updating range all the time will not cause missing | 
|  | // any of the the roam change. | 
|  | this.range = dataZoomModel.getPercentRange(); | 
|  |  | 
|  | // Reset controllers. | 
|  | roams.setViewInfoToCoordSysRecord( | 
|  | api, | 
|  | dataZoomModel, | 
|  | { | 
|  | pan: bind(getRangeHandlers.pan, this), | 
|  | zoom: bind(getRangeHandlers.zoom, this), | 
|  | scrollMove: bind(getRangeHandlers.scrollMove, this) | 
|  | } | 
|  | ); | 
|  | } | 
|  |  | 
|  | dispose() { | 
|  | this._clear(); | 
|  | super.dispose.apply(this, arguments as any); | 
|  | } | 
|  |  | 
|  | private _clear() { | 
|  | roams.disposeCoordSysRecordIfNeeded(this.api, this.dataZoomModel as InsideZoomModel); | 
|  | this.range = null; | 
|  | } | 
|  | } | 
|  |  | 
|  | interface DataZoomGetRangeHandler< | 
|  | T extends RoamEventParams['zoom'] | RoamEventParams['scrollMove'] | RoamEventParams['pan'] | 
|  | > { | 
|  | ( | 
|  | coordSysInfo: DataZoomReferCoordSysInfo, | 
|  | coordSysMainType: DataZoomCoordSysMainType, | 
|  | controller: RoamController, | 
|  | e: T | 
|  | ): [number, number] | 
|  | } | 
|  |  | 
|  | const getRangeHandlers: { | 
|  | pan: DataZoomGetRangeHandler<RoamEventParams['pan']> | 
|  | zoom: DataZoomGetRangeHandler<RoamEventParams['zoom']> | 
|  | scrollMove: DataZoomGetRangeHandler<RoamEventParams['scrollMove']> | 
|  | } & ThisType<InsideZoomView> = { | 
|  |  | 
|  | zoom(coordSysInfo, coordSysMainType, controller, e: RoamEventParams['zoom']) { | 
|  | const lastRange = this.range; | 
|  | const range = lastRange.slice() as [number, number]; | 
|  |  | 
|  | // Calculate transform by the first axis. | 
|  | const axisModel = coordSysInfo.axisModels[0]; | 
|  | if (!axisModel) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | const directionInfo = getDirectionInfo[coordSysMainType]( | 
|  | null, [e.originX, e.originY], axisModel, controller, coordSysInfo | 
|  | ); | 
|  | const percentPoint = ( | 
|  | directionInfo.signal > 0 | 
|  | ? (directionInfo.pixelStart + directionInfo.pixelLength - directionInfo.pixel) | 
|  | : (directionInfo.pixel - directionInfo.pixelStart) | 
|  | ) / directionInfo.pixelLength * (range[1] - range[0]) + range[0]; | 
|  |  | 
|  | const scale = Math.max(1 / e.scale, 0); | 
|  | range[0] = (range[0] - percentPoint) * scale + percentPoint; | 
|  | range[1] = (range[1] - percentPoint) * scale + percentPoint; | 
|  |  | 
|  | // Restrict range. | 
|  | const minMaxSpan = this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan(); | 
|  |  | 
|  | sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan); | 
|  |  | 
|  | this.range = range; | 
|  |  | 
|  | if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) { | 
|  | return range; | 
|  | } | 
|  | }, | 
|  |  | 
|  | pan: makeMover(function (range, axisModel, coordSysInfo, coordSysMainType, controller, e: RoamEventParams['pan']) { | 
|  | const directionInfo = getDirectionInfo[coordSysMainType]( | 
|  | [e.oldX, e.oldY], [e.newX, e.newY], axisModel, controller, coordSysInfo | 
|  | ); | 
|  |  | 
|  | return directionInfo.signal | 
|  | * (range[1] - range[0]) | 
|  | * directionInfo.pixel / directionInfo.pixelLength; | 
|  | }), | 
|  |  | 
|  | scrollMove: makeMover( | 
|  | function (range, axisModel, coordSysInfo, coordSysMainType, controller, e: RoamEventParams['scrollMove'] | 
|  | ) { | 
|  | const directionInfo = getDirectionInfo[coordSysMainType]( | 
|  | [0, 0], [e.scrollDelta, e.scrollDelta], axisModel, controller, coordSysInfo | 
|  | ); | 
|  | return directionInfo.signal * (range[1] - range[0]) * e.scrollDelta; | 
|  | }) | 
|  | }; | 
|  |  | 
|  | export type DataZoomGetRangeHandlers = typeof getRangeHandlers; | 
|  |  | 
|  | function makeMover( | 
|  | getPercentDelta: ( | 
|  | range: [number, number], | 
|  | axisModel: AxisBaseModel, | 
|  | coordSysInfo: DataZoomReferCoordSysInfo, | 
|  | coordSysMainType: DataZoomCoordSysMainType, | 
|  | controller: RoamController, | 
|  | e: RoamEventParams['scrollMove']| RoamEventParams['pan'] | 
|  | ) => number | 
|  | ) { | 
|  | return function ( | 
|  | this: InsideZoomView, | 
|  | coordSysInfo: DataZoomReferCoordSysInfo, | 
|  | coordSysMainType: DataZoomCoordSysMainType, | 
|  | controller: RoamController, | 
|  | e: RoamEventParams['scrollMove']| RoamEventParams['pan'] | 
|  | ): [number, number] { | 
|  | const lastRange = this.range; | 
|  | const range = lastRange.slice() as [number, number]; | 
|  |  | 
|  | // Calculate transform by the first axis. | 
|  | const axisModel = coordSysInfo.axisModels[0]; | 
|  | if (!axisModel) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | const percentDelta = getPercentDelta( | 
|  | range, axisModel, coordSysInfo, coordSysMainType, controller, e | 
|  | ); | 
|  |  | 
|  | sliderMove(percentDelta, range, [0, 100], 'all'); | 
|  |  | 
|  | this.range = range; | 
|  |  | 
|  | if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) { | 
|  | return range; | 
|  | } | 
|  | }; | 
|  | } | 
|  |  | 
|  | interface DirectionInfo { | 
|  | pixel: number | 
|  | pixelLength: number | 
|  | pixelStart: number | 
|  | signal: -1 | 1 | 
|  | } | 
|  | interface GetDirectionInfo { | 
|  | ( | 
|  | oldPoint: number[], | 
|  | newPoint: number[], | 
|  | axisModel: AxisBaseModel, | 
|  | controller: RoamController, | 
|  | coordSysInfo: DataZoomReferCoordSysInfo | 
|  | ): DirectionInfo | 
|  | } | 
|  |  | 
|  | const getDirectionInfo: Record<'grid' | 'polar' | 'singleAxis', GetDirectionInfo> = { | 
|  |  | 
|  | grid(oldPoint, newPoint, axisModel, controller, coordSysInfo) { | 
|  | const axis = axisModel.axis; | 
|  | const ret = {} as DirectionInfo; | 
|  | const rect = coordSysInfo.model.coordinateSystem.getRect(); | 
|  | oldPoint = oldPoint || [0, 0]; | 
|  |  | 
|  | if (axis.dim === 'x') { | 
|  | ret.pixel = newPoint[0] - oldPoint[0]; | 
|  | ret.pixelLength = rect.width; | 
|  | ret.pixelStart = rect.x; | 
|  | ret.signal = axis.inverse ? 1 : -1; | 
|  | } | 
|  | else { // axis.dim === 'y' | 
|  | ret.pixel = newPoint[1] - oldPoint[1]; | 
|  | ret.pixelLength = rect.height; | 
|  | ret.pixelStart = rect.y; | 
|  | ret.signal = axis.inverse ? -1 : 1; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | }, | 
|  |  | 
|  | polar(oldPoint, newPoint, axisModel, controller, coordSysInfo) { | 
|  | const axis = axisModel.axis; | 
|  | const ret = {} as DirectionInfo; | 
|  | const polar = coordSysInfo.model.coordinateSystem as Polar; | 
|  | const radiusExtent = polar.getRadiusAxis().getExtent(); | 
|  | const angleExtent = polar.getAngleAxis().getExtent(); | 
|  |  | 
|  | oldPoint = oldPoint ? polar.pointToCoord(oldPoint) : [0, 0]; | 
|  | newPoint = polar.pointToCoord(newPoint); | 
|  |  | 
|  | if (axisModel.mainType === 'radiusAxis') { | 
|  | ret.pixel = newPoint[0] - oldPoint[0]; | 
|  | // ret.pixelLength = Math.abs(radiusExtent[1] - radiusExtent[0]); | 
|  | // ret.pixelStart = Math.min(radiusExtent[0], radiusExtent[1]); | 
|  | ret.pixelLength = radiusExtent[1] - radiusExtent[0]; | 
|  | ret.pixelStart = radiusExtent[0]; | 
|  | ret.signal = axis.inverse ? 1 : -1; | 
|  | } | 
|  | else { // 'angleAxis' | 
|  | ret.pixel = newPoint[1] - oldPoint[1]; | 
|  | // ret.pixelLength = Math.abs(angleExtent[1] - angleExtent[0]); | 
|  | // ret.pixelStart = Math.min(angleExtent[0], angleExtent[1]); | 
|  | ret.pixelLength = angleExtent[1] - angleExtent[0]; | 
|  | ret.pixelStart = angleExtent[0]; | 
|  | ret.signal = axis.inverse ? -1 : 1; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | }, | 
|  |  | 
|  | singleAxis(oldPoint, newPoint, axisModel, controller, coordSysInfo) { | 
|  | const axis = axisModel.axis as SingleAxis; | 
|  | const rect = coordSysInfo.model.coordinateSystem.getRect(); | 
|  | const ret = {} as DirectionInfo; | 
|  |  | 
|  | oldPoint = oldPoint || [0, 0]; | 
|  |  | 
|  | if (axis.orient === 'horizontal') { | 
|  | ret.pixel = newPoint[0] - oldPoint[0]; | 
|  | ret.pixelLength = rect.width; | 
|  | ret.pixelStart = rect.x; | 
|  | ret.signal = axis.inverse ? 1 : -1; | 
|  | } | 
|  | else { // 'vertical' | 
|  | ret.pixel = newPoint[1] - oldPoint[1]; | 
|  | ret.pixelLength = rect.height; | 
|  | ret.pixelStart = rect.y; | 
|  | ret.signal = axis.inverse ? -1 : 1; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  | }; | 
|  |  | 
|  | export default InsideZoomView; |