Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/coord/Axis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,8 @@ class Axis {
}

getViewLabels(): ReturnType<typeof createAxisLabels>['labels'] {
return createAxisLabels(this).labels;
const labels = createAxisLabels(this).labels;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change should be reverted in later commits.

return labels;
}

getLabelModel(): Model<AxisBaseOption['axisLabel']> {
Expand Down
7 changes: 6 additions & 1 deletion src/coord/axisCommonTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
AreaStyleOption, ComponentOption, ColorString,
AnimationOptionMixin, Dictionary, ScaleDataValue, CommonAxisPointerOption
} from '../util/types';
import { PrimaryTimeUnit } from '../util/time';
import { TextStyleProps } from 'zrender/src/graphic/Text';


Expand Down Expand Up @@ -159,9 +160,13 @@ export interface LogAxisBaseOption extends NumericAxisBaseOptionCommon {
axisLabel?: AxisLabelOption<'log'>;
logBase?: number;
}

export interface TimeAxisLabelOption extends AxisLabelOption<'time'> {
formatterMinUnit?: PrimaryTimeUnit
}
export interface TimeAxisBaseOption extends NumericAxisBaseOptionCommon {
type?: 'time';
axisLabel?: AxisLabelOption<'time'>;
axisLabel?: TimeAxisLabelOption;
}
interface AxisNameTextStyleOption extends TextCommonOption {
rich?: Dictionary<TextCommonOption>
Expand Down
3 changes: 0 additions & 3 deletions src/coord/axisDefault.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,6 @@ const valueAxis: AxisBaseOption = zrUtil.merge({
const timeAxis: AxisBaseOption = zrUtil.merge({
splitNumber: 6,
axisLabel: {
// To eliminate labels that are not nice
showMinLabel: false,
showMaxLabel: false,
rich: {
primary: {
fontWeight: 'bold'
Expand Down
5 changes: 4 additions & 1 deletion src/coord/axisHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
CategoryAxisBaseOption,
LogAxisBaseOption,
TimeAxisLabelFormatterOption,
TimeAxisLabelOption,
ValueAxisBaseOption
} from './axisCommonTypes';
import type CartesianAxisModel from './cartesian/AxisModel';
Expand Down Expand Up @@ -201,9 +202,11 @@ export function createScaleByModel(model: AxisBaseModel, axisType?: string): Sca
extent: [Infinity, -Infinity]
});
case 'time':
const axisLabel = model.getModel('axisLabel') as Model<TimeAxisLabelOption>;
return new TimeScale({
locale: model.ecModel.getLocaleModel(),
useUTC: model.ecModel.get('useUTC')
useUTC: model.ecModel.get('useUTC'),
formatterMinUnit: axisLabel.get('formatterMinUnit')
});
default:
// case 'value'/'interval', 'log', or others.
Expand Down
155 changes: 155 additions & 0 deletions src/coord/axisTickLabelBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ import { AxisBaseOption } from './axisCommonTypes';
import OrdinalScale from '../scale/Ordinal';
import { AxisBaseModel } from './AxisBaseModel';
import type Axis2D from './cartesian/Axis2D';
import { TimeScaleTick } from '../util/types';
import * as formatUtil from '../util/format';
import { BoundingRect } from 'zrender';

type CacheKey = string | number;

Expand Down Expand Up @@ -72,6 +75,8 @@ export function createAxisLabels(axis: Axis): {
// Only ordinal scale support tick interval
return axis.type === 'category'
? makeCategoryLabels(axis)
: axis.type === 'time'
? makeTimeLabels(axis)
: makeRealNumberLabels(axis);
}

Expand All @@ -90,6 +95,8 @@ export function createAxisTicks(axis: Axis, tickModel: AxisBaseModel): {
// Only ordinal scale support tick interval
return axis.type === 'category'
? makeCategoryTicks(axis, tickModel)
: axis.type === 'time'
? makeTimeTicks(axis, tickModel)
: {ticks: zrUtil.map(axis.scale.getTicks(), tick => tick.value) };
}

Expand All @@ -102,6 +109,15 @@ function makeCategoryLabels(axis: Axis) {
: result;
}

function makeTimeLabels(axis: Axis) {
const labelModel = axis.getLabelModel();
const result = makeTimeLabelsActually(axis, labelModel);

return (!labelModel.get('show') || axis.scale.isBlank())
? {labels: []}
: result;
}

function makeCategoryLabelsActually(axis: Axis, labelModel: Model<AxisBaseOption['axisLabel']>) {
const labelsCache = getListCache(axis, 'labels');
const optionLabelInterval = getOptionCategoryInterval(labelModel);
Expand Down Expand Up @@ -129,6 +145,144 @@ function makeCategoryLabelsActually(axis: Axis, labelModel: Model<AxisBaseOption
});
}

function makeTimeLabelsActually(axis: Axis, labelModel: Model<AxisBaseOption['axisLabel']>) {
const labelsCache = getListCache(axis, 'labels');
const timeKey = 'time'; // TODO: change key name
const result = listCacheGet(labelsCache, timeKey);

if (result) {
return result;
}

const labels = makeNonOverlappedTimeLabels(axis);

// Cache to avoid calling interval function repeatly.
return listCacheSet(labelsCache, timeKey, {
labels: labels
});
}

function makeNonOverlappedTimeLabels(axis: Axis): MakeLabelsResultObj[];
function makeNonOverlappedTimeLabels(axis: Axis, onlyTick: false): MakeLabelsResultObj[];
function makeNonOverlappedTimeLabels(axis: Axis, onlyTick: true): number[];
function makeNonOverlappedTimeLabels(axis: Axis, onlyTick?: boolean) {
const ticks = axis.scale.getTicks() as TimeScaleTick[];
const ordinalScale = axis.scale as OrdinalScale;
const labelFormatter = makeLabelFormatter(axis);
const labelModel = axis.getLabelModel();
const font = labelModel.getFont();
const padding = formatUtil.normalizeCssArray(labelModel.get('padding') || 0);
const paddingH = padding[1] + padding[3];

const result: (MakeLabelsResultObj | number)[] = [];
const boundingRects: BoundingRect[] = [];

function isOverlap(rect: BoundingRect) {
/**
* `rotate` is not considered because for time axis,
* the interval is a suggestion value, not a precise value.
* So if there is no overlap without rotate, there should be
* no overlap with rotate and we don't have to make tick labels
* as condense as possible as in the case of category axes.
*/
for (let i = 0; i < boundingRects.length; i++) {
if (rect.intersect(boundingRects[i])) {
return true;
}
}
return false;
}

function addItem(tickValue: number, tickLevel: number) {
const tickObj = { value: tickValue, level: tickLevel };
result.push(onlyTick
? tickValue
: {
formattedLabel: labelFormatter(tickObj),
rawLabel: ordinalScale.getLabel(tickObj), // TODO: ?
tickValue: tickValue,
level: tickLevel
}
);
}

let lastMaxLevel = Number.MAX_VALUE;
let maxLevel;
/**
* Loop through the ticks with larger levels to smaller levels so that if
* the ticks are overlapped, we can use the level of the higher level.
*/
while (true) {
maxLevel = -1;

for (let i = 0; i < ticks.length; i++) {
if (ticks[i].level > maxLevel && ticks[i].level < lastMaxLevel) {
maxLevel = ticks[i].level;
}
}

if (maxLevel < 0) {
break;
}

for (let i = 0; i < ticks.length; i++) {
const tick = ticks[i];
if (tick.level === maxLevel) {
// Check if this tick is overlapped with added ticks
const rect = textContain.getBoundingRect(
labelFormatter({
value: tick.value,
level: tick.level
}),
font,
'center',
'top'
);
rect.x += axis.dataToCoord(tick.value) - padding[3];
rect.width += paddingH;
if (!isOverlap(rect)) {
// Add the tick only if it has no overlap with current ones
addItem(tick.value, tick.level);
boundingRects.push(rect);
}
}
}

if (maxLevel <= 0) {
break;
}
lastMaxLevel = maxLevel;
}
return result;
}

function makeTimeTicks(axis: Axis, tickModel: AxisBaseModel) {
const ticksCache = getListCache(axis, 'ticks');
const result = listCacheGet(ticksCache, 'time');

if (result) {
return result;
}

let ticks: number[];

// Optimize for the case that large category data and no label displayed,
// we should not return all ticks.
if (!tickModel.get('show') || axis.scale.isBlank()) {
ticks = [];
}

const labelsResult = makeTimeLabelsActually(axis, axis.getLabelModel());
ticks = zrUtil.map(labelsResult.labels, function (labelItem) {
return labelItem.tickValue;
});

// Cache to avoid calling interval function repeatly.
return listCacheSet(ticksCache, 'time', {
ticks: ticks
});
}

function makeCategoryTicks(axis: Axis, tickModel: AxisBaseModel) {
const ticksCache = getListCache(axis, 'ticks');
const optionTickInterval = getOptionCategoryInterval(tickModel);
Expand Down Expand Up @@ -331,6 +485,7 @@ interface MakeLabelsResultObj {
formattedLabel: string
rawLabel: string
tickValue: number
level?: number
}

function makeLabelsByNumericCategoryInterval(axis: Axis, categoryInterval: number): MakeLabelsResultObj[];
Expand Down
Loading