feat(grid layout): add outerBoundsClamp to limit shrink.
diff --git a/src/coord/cartesian/Grid.ts b/src/coord/cartesian/Grid.ts
index 50e51e2..bd91338 100644
--- a/src/coord/cartesian/Grid.ts
+++ b/src/coord/cartesian/Grid.ts
@@ -23,7 +23,7 @@
* TODO Default cartesian
*/
-import {isObject, each, indexOf, retrieve3, keys, assert, eqNaN, find} from 'zrender/src/core/util';
+import {isObject, each, indexOf, retrieve3, keys, assert, eqNaN, find, retrieve2} from 'zrender/src/core/util';
import {BoxLayoutReferenceResult, createBoxLayoutReference, getLayoutRect, LayoutRect} from '../../util/layout';
import {
createScaleByModel,
@@ -38,7 +38,7 @@
import {ParsedModelFinder, ParsedModelFinderKnown, SINGLE_REFERRING} from '../../util/model';
// Depends on GridModel, AxisModel, which performs preprocess.
-import GridModel, { GridOption, OUTER_BOUNDS_DEFAULT } from './GridModel';
+import GridModel, { GridOption, OUTER_BOUNDS_CLAMP_DEFAULT, OUTER_BOUNDS_DEFAULT } from './GridModel';
import CartesianAxisModel from './AxisModel';
import GlobalModel from '../../model/Global';
import ExtensionAPI from '../../core/ExtensionAPI';
@@ -69,7 +69,7 @@
import { error, log } from '../../util/log';
import { AxisTickLabelComputingKind } from '../axisTickLabelBuilder';
import { injectCoordSysByOption } from '../../core/CoordinateSystem';
-import { mathMax } from '../../util/number';
+import { mathMax, parsePositionSizeOption } from '../../util/number';
type Cartesian2DDimensionName = 'x' | 'y';
@@ -237,18 +237,19 @@
);
}
noPxChange = layOutGridByOuterBounds(
- gridRect.clone(), 'axisLabel', gridRect, axesMap, axisBuilderSharedCtx, layoutRef
+ gridRect.clone(), 'axisLabel', null, gridRect, axesMap, axisBuilderSharedCtx, layoutRef
);
}
}
else {
- const {outerBoundsRect, parsedOuterBoundsContain} = prepareOuterBounds(
+ const {outerBoundsRect, parsedOuterBoundsContain, outerBoundsClamp} = prepareOuterBounds(
gridModel, gridRect, layoutRef
);
if (outerBoundsRect) {
// console.time('layOutGridByOuterBounds');
noPxChange = layOutGridByOuterBounds(
- outerBoundsRect, parsedOuterBoundsContain, gridRect, axesMap, axisBuilderSharedCtx, layoutRef
+ outerBoundsRect, parsedOuterBoundsContain, outerBoundsClamp,
+ gridRect, axesMap, axisBuilderSharedCtx, layoutRef
);
// console.timeEnd('layOutGridByOuterBounds');
}
@@ -740,6 +741,7 @@
function layOutGridByOuterBounds(
outerBoundsRect: BoundingRect,
outerBoundsContain: ParsedOuterBoundsContain,
+ outerBoundsClamp: number[] | NullUndefined,
gridRect: LayoutRect,
axesMap: AxesMap,
axisBuilderSharedCtx: AxisBuilderSharedContext,
@@ -775,7 +777,7 @@
fillMarginOnOneDimension(gridRect, 1, NaN);
const noPxChange = find(margin, item => item > 0) == null;
- expandOrShrinkRect(gridRect, margin, true, true);
+ expandOrShrinkRect(gridRect, margin, true, true, outerBoundsClamp);
updateAllAxisExtentTransByGridRect(axesMap, gridRect);
@@ -915,16 +917,17 @@
function prepareOuterBounds(
gridModel: GridModel,
- gridRect: BoundingRect,
+ rawRridRect: BoundingRect,
layoutRef: BoxLayoutReferenceResult,
): {
outerBoundsRect: BoundingRect | NullUndefined
parsedOuterBoundsContain: ParsedOuterBoundsContain
+ outerBoundsClamp: number[]
} {
let outerBoundsRect: BoundingRect | NullUndefined;
const optionOuterBoundsMode = gridModel.get('outerBoundsMode', true);
if (optionOuterBoundsMode === 'same') {
- outerBoundsRect = gridRect.clone();
+ outerBoundsRect = rawRridRect.clone();
}
else if (optionOuterBoundsMode == null || optionOuterBoundsMode === 'auto') {
outerBoundsRect = getLayoutRect(
@@ -952,7 +955,16 @@
parsedOuterBoundsContain = optionOuterBoundsContain;
}
- return {outerBoundsRect, parsedOuterBoundsContain};
+ const outerBoundsClamp = [
+ parsePositionSizeOption(
+ retrieve2(gridModel.get('outerBoundsClampWidth', true), OUTER_BOUNDS_CLAMP_DEFAULT[0]), rawRridRect.width
+ ),
+ parsePositionSizeOption(
+ retrieve2(gridModel.get('outerBoundsClampHeight', true), OUTER_BOUNDS_CLAMP_DEFAULT[1]), rawRridRect.height
+ )
+ ];
+
+ return {outerBoundsRect, parsedOuterBoundsContain, outerBoundsClamp};
}
const resolveAxisNameOverlapForGrid: AxisBuilderSharedContext['resolveAxisNameOverlap'] = (
diff --git a/src/coord/cartesian/GridModel.ts b/src/coord/cartesian/GridModel.ts
index c3f1938..5461319 100644
--- a/src/coord/cartesian/GridModel.ts
+++ b/src/coord/cartesian/GridModel.ts
@@ -29,6 +29,7 @@
// For backward compatibility, do not use a margin. Although the labels might touch the edge of
// the canvas, the chart canvas probably does not have an border or a different background color within a page.
export const OUTER_BOUNDS_DEFAULT = {left: 0, right: 0, top: 0, bottom: 0};
+export const OUTER_BOUNDS_CLAMP_DEFAULT = ['25%', '25%'];
export interface GridOption
extends ComponentOption, BoxLayoutOptionMixin, ShadowOptionMixin {
@@ -72,6 +73,15 @@
*/
outerBoundsContain?: 'all' | 'axisLabel' | 'auto' | NullUndefined;
+ /**
+ * Available only when `outerBoundsMode` is not 'none'.
+ * Offer a constraint to not to shrink the grid rect causing smaller that width/height.
+ * A string means percent, like '30%', based on the original rect size
+ * determined by `grid.top/right/bottom/left/width/height`.
+ */
+ outerBoundsClampWidth?: number | string;
+ outerBoundsClampHeight?: number | string;
+
backgroundColor?: ZRColor;
borderWidth?: number;
borderColor?: ZRColor;
@@ -120,6 +130,8 @@
outerBoundsMode: 'auto',
outerBounds: OUTER_BOUNDS_DEFAULT,
outerBoundsContain: 'all',
+ outerBoundsClampWidth: OUTER_BOUNDS_CLAMP_DEFAULT[0],
+ outerBoundsClampHeight: OUTER_BOUNDS_CLAMP_DEFAULT[1],
// width: {totalWidth} - left - right,
// height: {totalHeight} - top - bottom,
diff --git a/src/util/graphic.ts b/src/util/graphic.ts
index f6f5c01..4c5b996 100644
--- a/src/util/graphic.ts
+++ b/src/util/graphic.ts
@@ -597,7 +597,8 @@
rect: TRect,
delta: number[] | number | NullUndefined,
shrinkOrExpand: boolean,
- noNegative: boolean
+ noNegative: boolean,
+ minSize?: number[] // by default [0, 0].
): TRect {
if (delta == null) {
return rect;
@@ -623,25 +624,31 @@
_tmpExpandRectDelta[2] = -_tmpExpandRectDelta[2];
_tmpExpandRectDelta[3] = -_tmpExpandRectDelta[3];
}
- expandRectOnOneDimension(rect, _tmpExpandRectDelta, 'x', 'width', 3, 1);
- expandRectOnOneDimension(rect, _tmpExpandRectDelta, 'y', 'height', 0, 2);
+ expandRectOnOneDimension(rect, _tmpExpandRectDelta, 'x', 'width', 3, 1, minSize && minSize[0] || 0);
+ expandRectOnOneDimension(rect, _tmpExpandRectDelta, 'y', 'height', 0, 2, minSize && minSize[1] || 0);
return rect;
}
const _tmpExpandRectDelta = [0, 0, 0, 0];
function expandRectOnOneDimension(
- rect: RectLike, delta: number[], xy: 'x' | 'y', wh: 'width' | 'height', ltIdx: 3 | 0, rbIdx: 1 | 2
+ rect: RectLike,
+ delta: number[],
+ xy: 'x' | 'y',
+ wh: 'width' | 'height',
+ ltIdx: 3 | 0, rbIdx: 1 | 2,
+ minSize: number
): void {
const deltaSum = delta[rbIdx] + delta[ltIdx];
const oldSize = rect[wh];
rect[wh] += deltaSum;
- if (rect[wh] < 0) {
- rect[wh] = 0;
+ minSize = mathMax(0, mathMin(minSize, oldSize));
+ if (rect[wh] < minSize) {
+ rect[wh] = minSize;
// Try to make the position of the zero rect reasonable in most visual cases.
rect[xy] += (
delta[ltIdx] >= 0 ? -delta[ltIdx]
: delta[rbIdx] >= 0 ? oldSize + delta[rbIdx]
- : mathAbs(deltaSum) > 1e-8 ? oldSize * delta[ltIdx] / deltaSum
+ : mathAbs(deltaSum) > 1e-8 ? (oldSize - minSize) * delta[ltIdx] / deltaSum
: 0
);
}
diff --git a/test/axis-layout-0.html b/test/axis-layout-0.html
index 2e09b34..0c904f5 100755
--- a/test/axis-layout-0.html
+++ b/test/axis-layout-0.html
@@ -623,6 +623,28 @@
});
}
},
+ ...['Width', 'Height'].map(prop => {
+ return {
+ type: 'select',
+ text: `grid.outerBoundsClamp${prop}:`,
+ options: [
+ {value: undefined},
+ {id: 'absolute', input: {type: 'range', min: -10, max: 500}, text: 'absolute'},
+ {id: 'percent', input: {type: 'range', min: -10, max: 100, suffix: '%'}, text: 'percent'},
+ ],
+ onchange() {
+ let val = this.value;
+ if (this.optionId === 'percent') {
+ val += '%'
+ }
+ chartSetOption(chart, {
+ grid: {
+ [`outerBoundsClamp${prop}`]: val
+ }
+ });
+ }
+ };
+ }),
{
type: 'select',
text: 'grid.containLabel(deprecated):',