UNPKG

echarts

Version:

Apache ECharts is a powerful, interactive charting and data visualization library for browser

615 lines (611 loc) 28.7 kB
/* * 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. */ /** * AUTO-GENERATED FILE. DO NOT MODIFY. */ /* * 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 { __extends } from "tslib"; import BoundingRect, { boundingRectApplyTransform, boundingRectCalculateTransform, boundingRectContain, boundingRectCopy, boundingRectCreate } from 'zrender/lib/core/BoundingRect.js'; import Transformable, { copyTransform, transformableCreate, transformableGetLocalTransform } from 'zrender/lib/core/Transformable.js'; import { isPositionSizeOptionPercent, mathAbs, parsePercent } from '../util/number.js'; import { assert, each } from 'zrender/lib/core/util.js'; import { getViewOfComponentOrSeries } from '../core/ExtensionAPI.js'; import { decomposeTransform, payloadDisableAnimation, updateProps, WH, XY } from '../util/graphic.js'; import { invert as matrixInvert, mul as matrixMul, create as matrixCreate, copy as matrixCopy } from 'zrender/lib/core/matrix.js'; import { applyTransform as vectorApplyTransform, copy as vectorCopy, set as vectorSet } from 'zrender/lib/core/vector.js'; /** * @tutorial [VIEW_COORD_SYS_ANIMATION] * * Some VIEW_COORD_SYS may need to support roaming animation, which may be required when the original * inputs (such as `center/zoom/left/top/right/bottomwidth/height/etc.`) are changed via `setOption`. * Roaming animation requires strict visual alignment between VIEW_COORD_SYS itself (consider "geo map") * and its content (e.g., scatter series). * A logically correct implementation is probably applying animation to VIEW_COORD_SYS's inputs and * calculate VIEW_COORD_SYS's transformation and lay out its dependent series per frame via * `dispatchAction`-ish pattern. But this approach require more mechanism to be introduced and careful * refactoring. * Instead, we currently still follow the conventional implementation - create animation separately on * each element (typically via `graphic.updateProps`) . See VIEW_COORD_SYS_ANIMATION_PROPS_CONSTRAINT. * * [VIEW_COORD_SYS_ANIMATION_PROPS_CONSTRAINT]: * The animation should only interpolate `x/y/scaleX/scaleY`, thereby ensuring visual alignment, even * though `MapDraw` applies animations only on a "VIEW_COORD_SYS_TRANS_ROAM" but scatter series applies * animation on a "VIEW_COORD_SYS_TRANS_OVERALL". * But this constraint must always be respected by all relevant components and series. * * [VIEW_COORD_SYS_ANIMATION_INTERRUPTION]: * The animation should be able to be interrupted, such as when users roam it by mouse or touch. * When interrupted, `center` and `zoom` must be synchronized back to the VIEW_COORD_SYS and host models * (e.g., geo components or map series). Otherwise, a next `setOption` can cause unexpected result due to * the outdated `center` and `zoom`. * But see FIXME_VIEW_COORD_SYS_SYNC_BACK for some existing bugs. * * [VIEW_COORD_SYS_UPDATE_FLOW]: * To avoid bidirectional flow, we consider the original inputs (center/zoom/dataRect/viewRect/etc.) as * the single source of truth, and always calculate transformations based on that. * - Roaming interactions ('pan'/'zoom') can trigger an update to the original inputs via * `dispatchAction({type: 'xxxRoam', ...})`. * - Roaming animation can synchronize back to the original inputs when interrupted. * See `syncBackEl`: It is zrender elements that are assigned with VIEW_COORD_SYS_TRANSFORMATION, * and may be modified outside, typically by animation. */ /** * @tutorial [VIEW_COORD_SYS_TRANSFORMATION] * * @tutorial [VIEW_COORD_SYS_TRANS_RAW] * It is the transformation from `ViewInner['dataRect']` to `ViewInner['viewRect']`. * - ViewInner['dataRect']: * A rect in a source space (or say, data space). * The unit is defined by the source. For example, * - for geo source, the unit defined as lat/lng, * - for SVG source, the unit is the same as the width/height defined in SVG. * - for series.graph/series.tree/series.sankey, the unit defined as px directly. * - ViewInner['viewRect']: * A rect in the canvas pixel space. * For example, it can be ec option left/right/top/bottom/width/height of a component or series. * NOTICE: * - ViewInner['dataRect'] and ViewInner['viewRect'] affect the behavior when centerOption contains * absolute values. See VIEW_COORD_SYS_CENTER_ZOOM_DEFINITION . * * @tutorial [VIEW_COORD_SYS_TRANS_ROAM] * It is built with respect to `RoamOptionMixin['center']` and `RoamOptionMixin['zoom']`. * [VIEW_COORD_SYS_CENTER_ZOOM_DEFINITION]: * {pxSpace} <-roamTrans- {viewRectSpace} <-rawTrans- {dataRectSpace} <-- ... * | | * zoomOption centerOption * `RoamOptionMixin['center']` is a user specified point on the space of `ViewInner['dataRect']`, * which will be located to the center of `ViewInner['viewRect']`. * It can also be a percent value (e.g. '50%'), based on ViewInner['dataRect'].width/height` (since v6). * Under this definition, users can use '0%' to map the top-left of `ViewInner['dataRect']` * to the center of `ViewInner['viewRect']`. * NOTICE: * - `RoamOptionMixin['center']` is in a linear space regardless of `GeoCommonOptionMixin['projection']` * - Absolute values in centerOption may hardly have a rigorous meaningful definition if the layout * is auto-calculated, since users are unlikely to known the exact value of the bounding rect. * And it is affected by the settings of ViewInner['dataRect'] and ViewInner['viewRect']. * Therefore, percent centerOption is preferred. * Use cases: * geo and map series (`MapDraw`) use this transformation for roamming, since the source * has been converted with respect to VIEW_COORD_SYS_TRANS_RAW. * * @tutorial [VIEW_COORD_SYS_TRANS_OVERALL] * This is the result of * `matrix_left_multiply(VIEW_COORD_SYS_TRANS_ROAM, VIEW_COORD_SYS_TRANS_RAW)`. * Use case: * sankey/tree/graph series use this transformation for roamming. * * @tutorial [VIEW_COORD_SYS_TRANSFORMATION_FORMULA] * [Basic]: * {pxSpace} <-roamTrans- {viewRectSpace} <-rawTrans- {dataRectSpace} <-- ... * pxPoint = roamTrans * rawTrans * dataPoint * overallTrans = roamTrans * rawTrans * [e.g., geo case]: * pxPoint = roamTrans * (rawTrans * (projection * dataPoint)) * Hence: pxPoint = group_syncBackType_ROAM * (rawTrans * (projection * dataPoint)) * Hence: pxPoint = group_syncBackType_ROAM * rawTrans_projection_precalculated_point * [e.g., graph series case]: * pxPoint = (roamTrans * rawTrans) * upper_els * Hence: pxPoint = group_syncBackType_OVERALL * upper_els * (NOTE: upper_els can have their own transformations) * * @see viewCoordSysCopyTrans for fetching transformations. */ export var VIEW_COORD_SYS_TRANS_RAW = 0; export var VIEW_COORD_SYS_TRANS_ROAM = 1; export var VIEW_COORD_SYS_TRANS_OVERALL = 2; function inner(viewCoordSys) { return viewCoordSys; } export var VIEW_COORD_SYS_TYPE = 'view'; /** * [VIEW_COORD_SYS] * * @final [NOTICE] Inheritance of this class is not recommended. Use composition instead. */ var View = /** @class */function (_super) { __extends(View, _super); function View(invertY, legacyCenterBase, legacyGeo) { var _this = _super.call(this) || this; _this.type = VIEW_COORD_SYS_TYPE; _this.dimensions = ['x', 'y']; var viewInner = inner(_this); viewInner.invertY = invertY; viewInner.lgCt = legacyCenterBase; viewInner.lgGeo = legacyGeo; var trans = viewInner.trans = []; trans[VIEW_COORD_SYS_TRANS_RAW] = transformableCreate(); trans[VIEW_COORD_SYS_TRANS_ROAM] = transformableCreate(); trans[VIEW_COORD_SYS_TRANS_OVERALL] = transformableCreate(); viewInner.mtRaw = matrixCreate(); viewInner.mtRawInv = matrixCreate(); viewInner.mtOverall = matrixCreate(); viewInner.mtOverallInv = matrixCreate(); viewInner.zoom = 1; return _this; } /** * @implements CoordinateSystem['getBoundingRect'] * @see VIEW_COORD_SYS_TRANS_RAW * * This is a rect in data space. * For historicall reason, the name is `getBoundingRect` - preserve it for backward compatibility. */ View.prototype.getBoundingRect = function () { return viewCoordSysCopyBoundingRect(null, this); }; /** * @implements CoordinateSystem['getViewRect'] * @see VIEW_COORD_SYS_TRANS_RAW */ View.prototype.getViewRect = function () { return viewCoordSysCopyViewRect(null, this); }; /** * @implements CoordinateSystem['getRoamTransform'] */ View.prototype.getRoamTransform = function () { return transformableGetLocalTransform(inner(this).trans[VIEW_COORD_SYS_TRANS_ROAM]); }; View.prototype.dataToPoint = function (data, noRoam, out) { var transform = noRoam ? inner(this).mtRaw : inner(this).mtOverall; out = out || []; return transform ? vectorApplyTransform(out, data, transform) : vectorCopy(out, data); }; View.prototype.pointToData = function (point, reserved, out) { out = out || []; var invTransform = inner(this).mtOverallInv; return invTransform ? vectorApplyTransform(out, point, invTransform) : vectorCopy(out, point); }; View.prototype.convertToPixel = function (ecModel, finder, value) { var coordSys = getCoordSys(finder); return coordSys === this ? coordSys.dataToPoint(value) : null; }; View.prototype.convertFromPixel = function (ecModel, finder, pixel) { var coordSys = getCoordSys(finder); return coordSys === this ? coordSys.pointToData(pixel) : null; }; View.prototype.containPoint = function (point) { var viewInner = inner(this); boundingRectCopy(tmpPixelRectForContain, viewInner.dataRect); boundingRectApplyTransform(tmpPixelRectForContain, tmpPixelRectForContain, viewInner.mtOverall); return boundingRectContain(tmpPixelRectForContain, point[0], point[1]); }; View.dimensions = ['x', 'y']; return View; }(Transformable); var tmpPixelRectForContain = boundingRectCreate(); export function viewCoordSysCopyOverallMatrix(out, viewCoordSys) { return matrixCopy(out || [], inner(viewCoordSys).mtOverall); } export function viewCoordSysGetZoomOption(viewCoordSys) { return inner(viewCoordSys).zoom; } export function viewCoordSysCopyBoundingRect(out, viewCoordSys) { return boundingRectCopy(out || boundingRectCreate(), inner(viewCoordSys).dataRect); } export function viewCoordSysCopyViewRect(out, viewCoordSys) { return boundingRectCopy(out || boundingRectCreate(), inner(viewCoordSys).viewRect); } export function viewCoordSysCopyTrans(out, viewCoordSys, transKind // @return The input `out` or create an object. ) { return copyTransform(out || transformableCreate(), inner(viewCoordSys).trans[transKind]); } function viewCoordSysIsInputReady(viewInner) { return !!(viewInner.dataRect && viewInner.viewRect); // NOTE: Other parameters has default values. Only parameters // above are required to be input. } function calcOverallTransFromSyncBackEl(out, viewInner, syncBackEl, syncBackType) { if (syncBackType === VIEW_COORD_SYS_TRANS_ROAM) { calcOverallTrans(out, viewInner.trans[VIEW_COORD_SYS_TRANS_RAW], syncBackEl); } else { copyTransform(out, syncBackEl); } } function calcRoamTransFromOverallTrans(out, viewInner, overallTrans) { // Convert overallTrans to roamTrans. If update animation is applied to overallTrans, // we assume that rawTrans is changed instantly, and roamTrans takes that animation. // Formula: // overallTrans = roamTrans * rawTrans // overallTrans * invert(rawTrans) = roamTrans transformableGetLocalTransform(overallTrans, tmpMtRTO); matrixMul(tmpMtRTO, tmpMtRTO, viewInner.mtRawInv); decomposeTransform(out, tmpMtRTO); } var tmpMtRTO = matrixCreate(); /** * [NOTICE] * The definition of this center has always been irrelevant to some other series center like * 'series-pie.center' - this center is a point in the space of `ViewInner['dataRect'].x/y`, * rather than canvas viewport, and the unit is not necessarily pixel (e.g., in geo case). */ export function viewCoordSysSetRoamOptionFromModel(viewCoordSys, hostModel) { var viewInner = inner(viewCoordSys); viewInner.centerOption = hostModel.getShallow('center'); var zoomLimit = viewInner.zoomLimit = hostModel.getShallow('scaleLimit'); var zoomOption = hostModel.getShallow('zoom'); viewInner.zoom = clampByZoomLimit(zoomOption || 1, zoomLimit) || 1; if (viewCoordSysIsInputReady(viewInner)) { viewCoordSysUpdateTransform(viewInner); } } /** * @see ViewInner['dataRect'] */ export function viewCoordSysSetBoundingRect(viewCoordSys, x, y, width, height) { var viewInner = inner(viewCoordSys); viewInner.dataRect = new BoundingRect(x, y, width, height); if (viewCoordSysIsInputReady(viewInner)) { viewCoordSysUpdateTransform(viewInner); } } /** * @see ViewInner['viewRect'] */ export function viewCoordSysSetViewRect(viewCoordSys, x, y, width, height) { var viewInner = inner(viewCoordSys); viewInner.viewRect = new BoundingRect(x, y, width, height); if (viewCoordSysIsInputReady(viewInner)) { viewCoordSysUpdateTransform(viewInner); } } function viewCoordSysUpdateTransform(viewInner) { // The order matters. viewCoordSysUpdateRawTrans(viewInner); viewCoordSysUpdateRoamTrans(viewInner); viewCoordSysUpdateOverallTrans(viewInner); } function viewCoordSysUpdateRawTrans(viewInner) { var dataRect = viewInner.dataRect; var viewRect = viewInner.viewRect; var rawTrans = viewInner.trans[VIEW_COORD_SYS_TRANS_RAW]; var invertY = viewInner.invertY; if (invertY) { dataRect = boundingRectCopy(tmpRectURT, dataRect); dataRect.y = -dataRect.y - dataRect.height; } boundingRectCalculateTransform(tmpMtURT, dataRect, viewRect); decomposeTransform(rawTrans, tmpMtURT); if (invertY) { rawTrans.scaleY = -rawTrans.scaleY; } var mtRaw = transformableGetLocalTransform(rawTrans, viewInner.mtRaw); matrixInvert(viewInner.mtRawInv, mtRaw); } var tmpMtURT = matrixCreate(); var tmpRectURT = boundingRectCreate(); /** * NOTICE: It depends on `viewCoordSysUpdateRawTrans`. */ function viewCoordSysUpdateRoamTrans(viewInner) { var viewRectCenter = viewCoordSysGetViewRectCenter(viewInner); var roamViewCenter = parseCenterOption(tmpCenterURT, viewInner, viewInner.centerOption) ? vectorApplyTransform(tmpCenterURT, tmpCenterURT, viewInner.mtRaw) : viewRectCenter; var zoom = viewInner.zoom; var roamTrans = viewInner.trans[VIEW_COORD_SYS_TRANS_ROAM]; roamTrans.x = viewRectCenter[0] - zoom * roamViewCenter[0]; roamTrans.y = viewRectCenter[1] - zoom * roamViewCenter[1]; roamTrans.scaleX = roamTrans.scaleY = zoom; // [VIEW_COORD_SYS_APPLY_ROAM_CENTER_AND_ZOOM] // Ordinarily, the definition is: // roamTrans.originX = roamViewCenter[0]; // roamTrans.originY = roamViewCenter[1]; // roamTrans.x = viewRectCenter[0] - roamViewCenter[0]; // roamTrans.y = viewRectCenter[1] - roamViewCenter[1]; // roamTrans.scaleX = roamTrans.scaleY = zoom; // But `el.originX`/`originY` should not be set. // (see VIEW_COORD_SYS_ANIMATION_PROPS_CONSTRAINT for the reason), // so we use the above formula instead. } var tmpCenterURT = []; /** * NOTICE: It depends on `viewCoordSysUpdateRoamTrans` and `viewCoordSysUpdateRawTrans` */ function viewCoordSysUpdateOverallTrans(viewInner) { var trans = viewInner.trans; var roamTrans = trans[VIEW_COORD_SYS_TRANS_ROAM]; var rawTrans = trans[VIEW_COORD_SYS_TRANS_RAW]; var overallTrans = trans[VIEW_COORD_SYS_TRANS_OVERALL]; calcOverallTrans(overallTrans, rawTrans, roamTrans); var mtOverall = transformableGetLocalTransform(overallTrans, viewInner.mtOverall); var mtOverallInv = matrixInvert(viewInner.mtOverallInv, mtOverall); legacyCopyOverallTrans(viewInner, overallTrans, mtOverall, mtOverallInv); legacyCopyOverallTrans(viewInner.lgGeo, overallTrans, mtOverall, mtOverallInv); } /** * [VIEW_COORD_SYS_TRANS_OVERALL_BACKWARD_COMPATIBILITY] * VIEW_COORD_SYS_TRANS_OVERALL transformable has long been View or Geo instance itself. * We keep backward compatibility, since some users may have visited it directly (e.g. * for drawing a computed bounding rect). */ function legacyCopyOverallTrans(target, overallTrans, mtOverall, mtOverallInv) { if (target) { copyTransform(target, overallTrans); matrixCopy(target.transform || (target.transform = []), mtOverall); matrixCopy(target.invTransform || (target.invTransform = []), mtOverallInv); } } function calcOverallTrans(out, rawTrans, roamTrans) { transformableGetLocalTransform(rawTrans, tmpMtCOT1); transformableGetLocalTransform(roamTrans, tmpMtCOT2); matrixMul(tmpMtCOT2, tmpMtCOT2, tmpMtCOT1); decomposeTransform(out, tmpMtCOT2); } var tmpMtCOT1 = matrixCreate(); var tmpMtCOT2 = matrixCreate(); function getCoordSys(finder) { var seriesModel = finder.seriesModel; return seriesModel ? seriesModel.coordinateSystem : null; // e.g., graph. } function viewCoordSysGetViewRectCenter(viewInner) { var viewRect = viewInner.viewRect; tmpViewRectCenter[0] = viewRect.x + viewRect.width / 2; tmpViewRectCenter[1] = viewRect.y + viewRect.height / 2; return tmpViewRectCenter; } var tmpViewRectCenter = []; export function isViewCoordSys(coordSys) { return coordSys && coordSys.type === 'view'; } /** * NOTICE: * - `syncBackEl` should be in the pixel space without any other transformation * in its accesters, otherwise the roaming may incorrect. * - `syncBackEl` can be a `Group`, having its own descendants and transformation. * But in this case, `dataToPoint` can only reach the space of `syncBackEl` itself. */ export function applyViewCoordSysTransToElement(syncBackEl, syncBackType, viewCoordSys, // If NullUndefined, no animation. // Typically, there should be no animation in the first render and in `__updateOnOwnRoam`. animatableModel) { var viewInner = inner(viewCoordSys); viewInner.syncBackEl = syncBackEl; viewInner.syncBackType = syncBackType; if (!animatableModel) { viewCoordSysCopyTrans(syncBackEl, viewCoordSys, syncBackType); syncBackEl.dirty(); } else { updateProps(syncBackEl, viewCoordSysCopyTrans(null, viewCoordSys, syncBackType), animatableModel); } } /** * FIXME: [FIXME_VIEW_COORD_SYS_SYNC_BACK] * Currently the "sync back" is only performed on roam action. But these cases are not * covered: * 1. Consider both center change and series data adding is requested via `setOption` * and animation is not finished, the new added series symbols should lay out based * on the "intermediate state" rather than the "final state" of the VIEW_COORD_SYS, * otherwise, visual artifacts can arise. * 2. See the case of FIXME_SYMBOL_CLIP_CONSIDERING_COORD_SYS_ALIGNMENT_DURING_ANIMATION . */ function viewCoordSysSyncBack(viewCoordSys, hostModel, otherModelsToSync, payload) { var viewInner = inner(viewCoordSys); var syncBackEl = viewInner.syncBackEl; if (syncBackEl) { if (process.env.NODE_ENV !== 'production') { assert(viewInner.syncBackType != null); } // @see VIEW_COORD_SYS_ANIMATION_INTERRUPTION syncBackEl.stopAnimation(); // syncBackEl may have been changed regardless of animation, // therefore, need to sync back. calcOverallTransFromSyncBackEl(tmpTransSB1, viewInner, syncBackEl, viewInner.syncBackType); } else { copyTransform(tmpTransSB1, viewInner.trans[VIEW_COORD_SYS_TRANS_OVERALL]); } // Sync back to model. calcRoamTransFromOverallTrans(tmpTransSB2, viewInner, tmpTransSB1); payload ? applyRoamPayloadToOverallTrans(tmpTransSB1, tmpTransSB2, viewInner, payload) : copyTransform(tmpTransSB1, tmpTransSB2); calcRoamTransFromOverallTrans(tmpTransSB1, viewInner, tmpTransSB1); syncBackToRoamOptionFromRoamTrans(viewInner, hostModel, otherModelsToSync, tmpTransSB1); } var tmpTransSB1 = transformableCreate(); var tmpTransSB2 = transformableCreate(); /** * Invert to `center` and `zoom` of VIEW_COORD_SYS and host models based on * - `syncBackEl` or VIEW_COORD_SYS_TRANS_OVERALL * - Delta in `payload`. * * Should be only called in action handlers. * * @see RoamHostView['__updateOnOwnRoam'] */ export function ownRoamModelCoordSysUpdateInAction(payload, hostModel, // This is only used for MAP_SERIES_GROUP, where the change need to sync to all series, // otherwise when legend filter some series and the other series take the responsibility // to draw map, or roam then legend restored, the result will be incorrect. otherModelsToSync) { // NOTE: VIEW_COORD_SYS instances are depended on by this action handler. // Under the current design, actions should not be triggered before `setOption` // being called, therefore coord sys instances always exist. var viewCoordSys = getOwnRoamViewCoordSys(hostModel); if (viewCoordSys) { // Sync back to model. viewCoordSysSyncBack(viewCoordSys, hostModel, otherModelsToSync, payload); // Recalculate from model. viewCoordSysSetRoamOptionFromModel(viewCoordSys, hostModel); } } export function getOwnRoamViewCoordSys(hostModel) { return hostModel.__ownRoamView ? hostModel.__ownRoamView() : null; } export function ownRoamViewUpdateDirectlyInAction(payload, componentOrSeries, ecModel, api) { // Tricky: disable animation in `updateProps` of `graphic.ts`. ecModel.setUpdatePayload(payloadDisableAnimation(payload)); var componentOrSeriesView = getViewOfComponentOrSeries(api, componentOrSeries); if (componentOrSeriesView && componentOrSeriesView.__updateOnOwnRoam) { componentOrSeriesView.__updateOnOwnRoam(payload, componentOrSeries, api); } } function applyRoamPayloadToOverallTrans(targetOverallTrans, roamTrans, viewInner, payload) { // NOTE: payload.dx/dy should always applied to pixel space, i.e., "overallTrans". if (payload.dx != null && payload.dy != null) { targetOverallTrans.x += payload.dx; targetOverallTrans.y += payload.dy; } var deltaZoom = payload.zoom; if (deltaZoom != null) { // Although `zoomOption` is defined on roamTrans var oldZoom = getZoomFromRoamTrans(roamTrans); var newZoom = clampByZoomLimit(oldZoom * deltaZoom, viewInner.zoomLimit); var deltaZoom2 = newZoom / oldZoom; // Keep the mouse center when scaling. targetOverallTrans.x -= (payload.originX - targetOverallTrans.x) * (deltaZoom2 - 1); targetOverallTrans.y -= (payload.originY - targetOverallTrans.y) * (deltaZoom2 - 1); // Although `zoomOption` is defined on roamTrans, // deltaZoom can be applied to overallTrans directly. targetOverallTrans.scaleX *= deltaZoom2; targetOverallTrans.scaleY *= deltaZoom2; } } function getZoomFromRoamTrans(trans) { if (process.env.NODE_ENV !== 'production') { // scaleX and scaleY should be the same even during animation. // See VIEW_COORD_SYS_APPLY_ROAM_CENTER_AND_ZOOM . assert(mathAbs(trans.scaleX - trans.scaleY) < 1e-5); } return trans.scaleX; } function parseCenterOption( // `out` is a center in `dataRect` space. out, viewInner, centerOption // @return Whether a valid center is obtained. ) { if (process.env.NODE_ENV !== 'production') { assert(viewCoordSysIsInputReady(viewInner)); } var dataRect = viewInner.dataRect; if (!centerOption) { return false; } // #16904 introduced percentage string here, such as '33%'. But it was based on canvas // width/height, which is not reasonable - the unit may incorrect, and it is unpredictable if // the `ViewInner['dataRect']` is not calculated based on the current canvas rect. Therefore the percentage // value is changed to based on `ViewInner['dataRect'].width/height` since v6. Under this definition, users // can use '0%' to map the top-left of `ViewInner['dataRect']` to the center of `ViewInner['viewRect']`. var lgCt = viewInner.lgCt; if (lgCt) { vectorSet(out, parsePercent(centerOption[0], lgCt.w), parsePercent(centerOption[1], lgCt.h)); } else if (dataRect) { vectorSet(out, parsePercent(centerOption[0], dataRect.width, dataRect.x), parsePercent(centerOption[1], dataRect.height, dataRect.y)); } return true; } /** * An inverse operation to `parseCenterOption`. * Mainly for percentage center option. */ function invertBackToCenterOption(viewInner, center) { var lastCenterOption = viewInner.centerOption; var dataRect = viewInner.dataRect; if (process.env.NODE_ENV !== 'production') { assert(center && dataRect); } return !lastCenterOption || viewInner.lgCt ? center.slice() : [invertToPercentPerCenterDim(0, center, lastCenterOption, dataRect), invertToPercentPerCenterDim(1, center, lastCenterOption, dataRect)]; } function invertToPercentPerCenterDim(dimIdx, center, lastCenterOption, dataRect) { return lastCenterOption && dataRect && dataRect[WH[dimIdx]] && isPositionSizeOptionPercent(lastCenterOption[dimIdx]) ? (center[dimIdx] - dataRect[XY[dimIdx]]) / dataRect[WH[dimIdx]] * 100 + '%' : center[dimIdx]; } export function useLegacyViewCoordSysCenterBase(ecModel, api) { return api && ecModel && ecModel.getShallow('legacyViewCoordSysCenterBase') ? { w: api.getWidth(), h: api.getHeight() } : null; } /** * @see VIEW_COORD_SYS_ANIMATION_INTERRUPTION */ function syncBackToRoamOptionFromRoamTrans(viewInner, hostModel, otherModelsToSync, roamTrans) { // This is the inverse operation of `viewCoordSysSetRoamOptionFromModel`. var viewRectCenter = viewCoordSysGetViewRectCenter(viewInner); var zoom = getZoomFromRoamTrans(roamTrans); var notZoomNearZero = mathAbs(zoom) > 1e-6; tmpCenterITR[0] = notZoomNearZero ? (viewRectCenter[0] - roamTrans.x) / zoom : viewRectCenter[0]; // Unlikely to occur. tmpCenterITR[1] = notZoomNearZero ? (viewRectCenter[1] - roamTrans.y) / zoom : viewRectCenter[1]; // Unlikely to occur. vectorApplyTransform(tmpCenterITR, tmpCenterITR, viewInner.mtRawInv); var centerOption = invertBackToCenterOption(viewInner, tmpCenterITR); syncBackRoamOptionToRoamHostModel(hostModel, centerOption, zoom); each(otherModelsToSync, function (otherModel) { if (otherModel !== hostModel) { syncBackRoamOptionToRoamHostModel(otherModel, centerOption.slice(), zoom); } }); } var tmpCenterITR = []; /** * Models should be updated, otherwise consequent `setOption()` can cause outdated `center` * and `zoom` to be used. */ function syncBackRoamOptionToRoamHostModel(hostModel, center, zoom) { var option = hostModel.option; option.center = center; option.zoom = zoom; } export function clampByZoomLimit(zoom, zoomLimit) { if (zoomLimit) { var zoomMin = zoomLimit.min || 0; var zoomMax = zoomLimit.max || Infinity; zoom = Math.max(Math.min(zoomMax, zoom), zoomMin); } return zoom; } export function calcCompensationScaleToPreserveNodeSize(viewCoordSys, model) { var nodeScaleRatio = model.getShallow('nodeScaleRatio', true) || 1; var viewInner = inner(viewCoordSys); // Scale node when zoom changes return ((viewInner.zoom - 1) * nodeScaleRatio + 1) / (viewInner.trans[VIEW_COORD_SYS_TRANS_OVERALL].scaleX || 1); } export default View;