mirror of
https://github.com/klinecharts/KLineChart.git
synced 2024-11-25 16:22:43 +08:00
refactor: refactor axis
This commit is contained in:
parent
3752c89124
commit
d5779483f1
12
src/Chart.ts
12
src/Chart.ts
@ -19,7 +19,7 @@ import { type KLineData } from './common/Data'
|
||||
import type Coordinate from './common/Coordinate'
|
||||
import type Point from './common/Point'
|
||||
import { UpdateLevel } from './common/Updater'
|
||||
import { type Styles, YAxisPosition } from './common/Styles'
|
||||
import { type Styles, YAxisPosition, YAxisType } from './common/Styles'
|
||||
import type Crosshair from './common/Crosshair'
|
||||
import { ActionType, type ActionCallback } from './common/Action'
|
||||
import type LoadMoreCallback from './common/LoadMoreCallback'
|
||||
@ -154,6 +154,9 @@ export default class ChartImp implements Chart {
|
||||
this._chartStore = new ChartStore(this, options)
|
||||
this._initPanes(options)
|
||||
this.adjustPaneViewport(true, true, true)
|
||||
if (isValid(options?.styles)) {
|
||||
this.setStyles(options?.styles)
|
||||
}
|
||||
}
|
||||
|
||||
private _initContainer (container: HTMLElement): void {
|
||||
@ -574,6 +577,13 @@ export default class ChartImp implements Chart {
|
||||
}
|
||||
if (isValid(realStyles?.yAxis?.type)) {
|
||||
(this._candlePane?.getAxisComponent() as unknown as AxisImp).setAutoCalcTickFlag(true)
|
||||
const type = realStyles?.yAxis?.type
|
||||
this.setPaneOptions({
|
||||
id: 'candle_pane',
|
||||
axisOptions: {
|
||||
name: type === YAxisType.Log ? 'logarithm' : type
|
||||
}
|
||||
})
|
||||
}
|
||||
this.adjustPaneViewport(true, true, true, true, true)
|
||||
}
|
||||
|
22
src/Event.ts
22
src/Event.ts
@ -253,15 +253,20 @@ export default class Event implements EventHandler {
|
||||
const difRange = range * scale
|
||||
const newFrom = from + difRange
|
||||
const newTo = to + difRange
|
||||
const newRealFrom = yAxis.convertToRealValue(newFrom)
|
||||
const newRealTo = yAxis.convertToRealValue(newTo)
|
||||
const newRealFrom = yAxis.valueToRealValue(newFrom, { range: this._prevYAxisRange })
|
||||
const newRealTo = yAxis.valueToRealValue(newTo, { range: this._prevYAxisRange })
|
||||
const newDisplayFrom = yAxis.realValueToDisplayValue(newRealFrom, { range: this._prevYAxisRange })
|
||||
const newDisplayTo = yAxis.realValueToDisplayValue(newRealTo, { range: this._prevYAxisRange })
|
||||
yAxis.setRange({
|
||||
from: newFrom,
|
||||
to: newTo,
|
||||
range: newTo - newFrom,
|
||||
realFrom: newRealFrom,
|
||||
realTo: newRealTo,
|
||||
realRange: newRealTo - newRealFrom
|
||||
realRange: newRealTo - newRealFrom,
|
||||
displayFrom: newDisplayFrom,
|
||||
displayTo: newDisplayTo,
|
||||
displayRange: newDisplayTo - newDisplayFrom
|
||||
})
|
||||
}
|
||||
const distance = event.x - this._startScrollCoordinate.x
|
||||
@ -298,15 +303,20 @@ export default class Event implements EventHandler {
|
||||
const difRange = (newRange - range) / 2
|
||||
const newFrom = from - difRange
|
||||
const newTo = to + difRange
|
||||
const newRealFrom = yAxis.convertToRealValue(newFrom)
|
||||
const newRealTo = yAxis.convertToRealValue(newTo)
|
||||
const newRealFrom = yAxis.valueToRealValue(newFrom, { range: this._prevYAxisRange })
|
||||
const newRealTo = yAxis.valueToRealValue(newTo, { range: this._prevYAxisRange })
|
||||
const newDisplayFrom = yAxis.realValueToDisplayValue(newRealFrom, { range: this._prevYAxisRange })
|
||||
const newDisplayTo = yAxis.realValueToDisplayValue(newRealTo, { range: this._prevYAxisRange })
|
||||
yAxis.setRange({
|
||||
from: newFrom,
|
||||
to: newTo,
|
||||
range: newRange,
|
||||
realFrom: newRealFrom,
|
||||
realTo: newRealTo,
|
||||
realRange: newRealTo - newRealFrom
|
||||
realRange: newRealTo - newRealFrom,
|
||||
displayFrom: newDisplayFrom,
|
||||
displayTo: newDisplayTo,
|
||||
displayRange: newDisplayTo - newDisplayFrom
|
||||
})
|
||||
this._chart.adjustPaneViewport(false, true, true, true)
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ import type VisibleRange from '../common/VisibleRange'
|
||||
import type DrawPane from '../pane/DrawPane'
|
||||
|
||||
import type Bounding from '../common/Bounding'
|
||||
import { type KLineData } from '../common/Data'
|
||||
import { type Indicator } from './Indicator'
|
||||
|
||||
export interface AxisTick {
|
||||
coord: number
|
||||
@ -27,41 +29,90 @@ export interface AxisTick {
|
||||
export interface AxisRange extends VisibleRange {
|
||||
readonly range: number
|
||||
readonly realRange: number
|
||||
readonly displayFrom: number
|
||||
readonly displayTo: number
|
||||
readonly displayRange: number
|
||||
}
|
||||
|
||||
export interface Axis {
|
||||
getTicks: () => AxisTick[]
|
||||
getRange: () => AxisRange
|
||||
getAutoSize: () => number
|
||||
convertToPixel: (value: number) => number
|
||||
convertFromPixel: (px: number) => number
|
||||
export interface AxisGap {
|
||||
top?: number
|
||||
bottom?: number
|
||||
}
|
||||
|
||||
export interface AxisValueToValueParams {
|
||||
range: AxisRange
|
||||
}
|
||||
|
||||
export type AxisValueToValueCallback = (value: number, params: AxisValueToValueParams) => number
|
||||
|
||||
export interface AxisCreateRangeParams {
|
||||
kLineDataList: KLineData[]
|
||||
indicators: Indicator[]
|
||||
visibleDataRange: VisibleRange
|
||||
defaultRange: AxisRange
|
||||
}
|
||||
|
||||
export type AxisCreateRangeCallback = (params: AxisCreateRangeParams) => AxisRange
|
||||
|
||||
export interface AxisCreateTicksParams {
|
||||
range: AxisRange
|
||||
bounding: Bounding
|
||||
defaultTicks: AxisTick[]
|
||||
}
|
||||
|
||||
export interface AxisCreateRangeParams {
|
||||
defaultRange: AxisRange
|
||||
}
|
||||
|
||||
export type AxisCreateRangeCallback = (params: AxisCreateRangeParams) => AxisRange
|
||||
|
||||
export type AxisCreateTicksCallback = (params: AxisCreateTicksParams) => AxisTick[]
|
||||
|
||||
export type AxisMinSpanCallback = (precision: number) => number
|
||||
|
||||
export interface Axis {
|
||||
override: (axis: AxisTemplate) => void
|
||||
getTicks: () => AxisTick[]
|
||||
getRange: () => AxisRange
|
||||
getAutoSize: () => number
|
||||
convertToPixel: (value: number) => number
|
||||
convertFromPixel: (px: number) => number
|
||||
}
|
||||
|
||||
export interface AxisTemplate {
|
||||
name: string
|
||||
reverse?: boolean
|
||||
scrollZoomEnabled?: boolean
|
||||
gap?: AxisGap
|
||||
valueToRealValue?: AxisValueToValueCallback
|
||||
realValueToDisplayValue?: AxisValueToValueCallback
|
||||
displayValueToRealValue?: AxisValueToValueCallback
|
||||
realValueToValue?: AxisValueToValueCallback
|
||||
displayValueToText?: (value: number, precision: number) => string
|
||||
minSpan?: AxisMinSpanCallback
|
||||
createRange?: AxisCreateRangeCallback
|
||||
createTicks?: AxisCreateTicksCallback
|
||||
}
|
||||
|
||||
export default abstract class AxisImp implements AxisTemplate, Axis {
|
||||
export type AxisCreate = Omit<AxisTemplate, 'valueToRealValue' | 'realValueToDisplayValue' | 'displayValueToRealValue' | 'realValueToValue' | 'minSpan'>
|
||||
|
||||
function getDefaultAxisRange (): AxisRange {
|
||||
return {
|
||||
from: 0,
|
||||
to: 0,
|
||||
range: 0,
|
||||
realFrom: 0,
|
||||
realTo: 0,
|
||||
realRange: 0,
|
||||
displayFrom: 0,
|
||||
displayTo: 0,
|
||||
displayRange: 0
|
||||
}
|
||||
}
|
||||
|
||||
export default abstract class AxisImp implements Axis {
|
||||
name: string
|
||||
scrollZoomEnabled = true
|
||||
createTicks: AxisCreateTicksCallback
|
||||
|
||||
private readonly _parent: DrawPane<Axis>
|
||||
|
||||
private _range: AxisRange = { from: 0, to: 0, range: 0, realFrom: 0, realTo: 0, realRange: 0 }
|
||||
private _prevRange: AxisRange = { from: 0, to: 0, range: 0, realFrom: 0, realTo: 0, realRange: 0 }
|
||||
private _range = getDefaultAxisRange()
|
||||
private _prevRange = getDefaultAxisRange()
|
||||
private _ticks: AxisTick[] = []
|
||||
|
||||
private _autoCalcTickFlag = true
|
||||
@ -70,23 +121,15 @@ export default abstract class AxisImp implements AxisTemplate, Axis {
|
||||
this._parent = parent
|
||||
}
|
||||
|
||||
name: string
|
||||
|
||||
getParent (): DrawPane<Axis> { return this._parent }
|
||||
|
||||
buildTicks (force: boolean): boolean {
|
||||
if (this._autoCalcTickFlag) {
|
||||
const defaultRange = this.createDefaultRange()
|
||||
this._range = this.createRange?.({ defaultRange }) ?? defaultRange
|
||||
this._range = this.createRangeImp()
|
||||
}
|
||||
if (this._prevRange.from !== this._range.from || this._prevRange.to !== this._range.to || force) {
|
||||
this._prevRange = this._range
|
||||
const defaultTicks = this.createDefaultTicks()
|
||||
this._ticks = this.createTicks?.({
|
||||
range: this._range,
|
||||
bounding: this.getBounding(),
|
||||
defaultTicks
|
||||
}) ?? defaultTicks
|
||||
this._ticks = this.createTicksImp()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@ -113,15 +156,13 @@ export default abstract class AxisImp implements AxisTemplate, Axis {
|
||||
|
||||
getAutoCalcTickFlag (): boolean { return this._autoCalcTickFlag }
|
||||
|
||||
protected abstract createDefaultRange (): AxisRange
|
||||
protected abstract createRangeImp (): AxisRange
|
||||
|
||||
protected abstract createDefaultTicks (): AxisTick[]
|
||||
protected abstract createTicksImp (): AxisTick[]
|
||||
|
||||
protected abstract getBounding (): Bounding
|
||||
|
||||
abstract createRange (params: AxisCreateRangeParams): AxisRange
|
||||
|
||||
abstract createTicks (params: AxisCreateTicksParams): AxisTick[]
|
||||
abstract override (axis: AxisTemplate): void
|
||||
|
||||
abstract getAutoSize (): number
|
||||
|
||||
|
@ -14,13 +14,17 @@
|
||||
|
||||
import type Nullable from '../common/Nullable'
|
||||
import type Bounding from '../common/Bounding'
|
||||
import { isFunction, isString } from '../common/utils/typeChecks'
|
||||
|
||||
import AxisImp, { type AxisTemplate, type Axis, type AxisRange, type AxisTick, type AxisCreateTicksParams, type AxisCreateRangeParams } from './Axis'
|
||||
import AxisImp, { type AxisTemplate, type Axis, type AxisRange, type AxisTick } from './Axis'
|
||||
|
||||
import type DrawPane from '../pane/DrawPane'
|
||||
import { TimeWeightConstants } from '../store/TimeScaleStore'
|
||||
import { FormatDateType } from '../Options'
|
||||
|
||||
export interface XAxis extends Axis {
|
||||
export type XAxisTemplate = Pick<AxisTemplate, 'name' | 'scrollZoomEnabled' | 'createTicks'>
|
||||
|
||||
export interface XAxis extends Axis, AxisTemplate {
|
||||
convertTimestampFromPixel: (pixel: number) => Nullable<number>
|
||||
convertTimestampToPixel: (timestamp: number) => number
|
||||
}
|
||||
@ -28,41 +32,73 @@ export interface XAxis extends Axis {
|
||||
export type XAxisConstructor = new (parent: DrawPane<Axis>) => XAxis
|
||||
|
||||
export default abstract class XAxisImp extends AxisImp implements XAxis {
|
||||
protected override createDefaultRange (): AxisRange {
|
||||
const chartStore = this.getParent().getChart().getChartStore()
|
||||
const { from, to } = chartStore.getTimeScaleStore().getVisibleRange()
|
||||
const af = from
|
||||
const at = to - 1
|
||||
const range = to - from
|
||||
return {
|
||||
from: af, to: at, range, realFrom: af, realTo: at, realRange: range
|
||||
}
|
||||
constructor (parent: DrawPane<Axis>, xAxis: XAxisTemplate) {
|
||||
super(parent)
|
||||
this.override(xAxis)
|
||||
}
|
||||
|
||||
protected override createDefaultTicks (): AxisTick[] {
|
||||
const timeTickList = this.getParent().getChart().getChartStore().getTimeScaleStore().getVisibleTimeTickList()
|
||||
return timeTickList.map(({ dataIndex, dateTime, weight, timestamp }) => {
|
||||
override (xAxis: XAxisTemplate): void {
|
||||
const {
|
||||
name,
|
||||
scrollZoomEnabled,
|
||||
createTicks
|
||||
} = xAxis
|
||||
if (!isString(name)) {
|
||||
this.name = name
|
||||
}
|
||||
this.scrollZoomEnabled = scrollZoomEnabled ?? this.scrollZoomEnabled
|
||||
this.createTicks = createTicks ?? this.createTicks
|
||||
}
|
||||
|
||||
protected override createRangeImp (): AxisRange {
|
||||
const chartStore = this.getParent().getChart().getChartStore()
|
||||
const visibleDataRange = chartStore.getTimeScaleStore().getVisibleRange()
|
||||
const { from, to } = visibleDataRange
|
||||
const af = from
|
||||
const at = to - 1
|
||||
const diff = to - from
|
||||
const range = {
|
||||
from: af,
|
||||
to: at,
|
||||
range: diff,
|
||||
realFrom: af,
|
||||
realTo: at,
|
||||
realRange: diff,
|
||||
displayFrom: af,
|
||||
displayTo: at,
|
||||
displayRange: diff
|
||||
}
|
||||
return range
|
||||
}
|
||||
|
||||
protected override createTicksImp (): AxisTick[] {
|
||||
const chartStore = this.getParent().getChart().getChartStore()
|
||||
const timeScaleStore = chartStore.getTimeScaleStore()
|
||||
const formatDate = chartStore.getCustomApi().formatDate
|
||||
const timeTickList = timeScaleStore.getVisibleTimeTickList()
|
||||
const dateTimeFormat = timeScaleStore.getDateTimeFormat()
|
||||
const ticks = timeTickList.map(({ dataIndex, weight, timestamp }) => {
|
||||
let text = ''
|
||||
switch (weight) {
|
||||
case TimeWeightConstants.Year: {
|
||||
text = dateTime.YYYY + '年'
|
||||
text = formatDate(dateTimeFormat, timestamp, 'YYYY', FormatDateType.XAxis)
|
||||
break
|
||||
}
|
||||
case TimeWeightConstants.Month: {
|
||||
text = `${dateTime.YYYY}年${dateTime.MM}月`
|
||||
text = formatDate(dateTimeFormat, timestamp, 'YYYY-MM', FormatDateType.XAxis)
|
||||
break
|
||||
}
|
||||
case TimeWeightConstants.Day: {
|
||||
text = `${dateTime.MM}月${dateTime.DD}日`
|
||||
text = formatDate(dateTimeFormat, timestamp, 'MM-DD', FormatDateType.XAxis)
|
||||
break
|
||||
}
|
||||
case TimeWeightConstants.Hour:
|
||||
case TimeWeightConstants.Minute: {
|
||||
text = `${dateTime.HH}-${dateTime.mm}`
|
||||
text = formatDate(dateTimeFormat, timestamp, 'HH:mm', FormatDateType.XAxis)
|
||||
break
|
||||
}
|
||||
default: {
|
||||
text = `${dateTime.HH}-${dateTime.mm}-${dateTime.ss}`
|
||||
text = formatDate(dateTimeFormat, timestamp, 'HH:mm:ss', FormatDateType.XAxis)
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -72,6 +108,14 @@ export default abstract class XAxisImp extends AxisImp implements XAxis {
|
||||
value: timestamp
|
||||
}
|
||||
})
|
||||
if (isFunction(this.createTicks)) {
|
||||
return this.createTicks({
|
||||
range: this.getRange(),
|
||||
bounding: this.getBounding(),
|
||||
defaultTicks: ticks
|
||||
})
|
||||
}
|
||||
return ticks
|
||||
}
|
||||
|
||||
override getAutoSize (): number {
|
||||
@ -134,14 +178,10 @@ export default abstract class XAxisImp extends AxisImp implements XAxis {
|
||||
return this.getParent().getChart().getChartStore().getTimeScaleStore().dataIndexToCoordinate(value)
|
||||
}
|
||||
|
||||
static extend (template: AxisTemplate): XAxisConstructor {
|
||||
static extend (template: XAxisTemplate): XAxisConstructor {
|
||||
class Custom extends XAxisImp {
|
||||
createRange (params: AxisCreateRangeParams): AxisRange {
|
||||
return template.createRange?.(params) ?? params.defaultRange
|
||||
}
|
||||
|
||||
createTicks (params: AxisCreateTicksParams): AxisTick[] {
|
||||
return template.createTicks?.(params) ?? params.defaultTicks
|
||||
constructor (parent: DrawPane<Axis>) {
|
||||
super(parent, template)
|
||||
}
|
||||
}
|
||||
return Custom
|
||||
|
@ -12,43 +12,92 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { YAxisType, YAxisPosition, CandleType } from '../common/Styles'
|
||||
import { YAxisPosition, CandleType } from '../common/Styles'
|
||||
import type Bounding from '../common/Bounding'
|
||||
import { isNumber, isValid } from '../common/utils/typeChecks'
|
||||
import { index10, log10, getPrecision, nice, round } from '../common/utils/number'
|
||||
import { isNumber, isString, isValid, merge } from '../common/utils/typeChecks'
|
||||
import { index10, getPrecision, nice, round } from '../common/utils/number'
|
||||
import { calcTextWidth } from '../common/utils/canvas'
|
||||
import { formatPrecision, formatThousands, formatFoldDecimal } from '../common/utils/format'
|
||||
|
||||
import AxisImp, { type AxisTemplate, type Axis, type AxisRange, type AxisTick, type AxisCreateTicksParams, type AxisCreateRangeParams } from './Axis'
|
||||
|
||||
import { type IndicatorFigure } from './Indicator'
|
||||
import AxisImp, {
|
||||
type AxisTemplate, type Axis, type AxisRange,
|
||||
type AxisTick, type AxisValueToValueCallback,
|
||||
type AxisMinSpanCallback, type AxisCreateRangeCallback
|
||||
} from './Axis'
|
||||
|
||||
import type DrawPane from '../pane/DrawPane'
|
||||
|
||||
import { PaneIdConstants } from '../pane/types'
|
||||
|
||||
interface FiguresResult {
|
||||
figures: IndicatorFigure[]
|
||||
result: any[]
|
||||
}
|
||||
export type YAxisTemplate = AxisTemplate
|
||||
|
||||
export interface YAxis extends Axis {
|
||||
const TICK_COUNT = 10
|
||||
|
||||
export interface YAxis extends Axis, YAxisTemplate {
|
||||
isFromZero: () => boolean
|
||||
isInCandle: () => boolean
|
||||
getType: () => YAxisType
|
||||
convertToNicePixel: (value: number) => number
|
||||
}
|
||||
|
||||
export type YAxisConstructor = new (parent: DrawPane<Axis>) => YAxis
|
||||
|
||||
export default abstract class YAxisImp extends AxisImp implements YAxis {
|
||||
protected override createDefaultRange (): AxisRange {
|
||||
reverse = false
|
||||
gap = {
|
||||
top: 0.2,
|
||||
bottom: 0.1
|
||||
}
|
||||
|
||||
createRange: AxisCreateRangeCallback = params => params.defaultRange
|
||||
minSpan: AxisMinSpanCallback = precision => index10(-precision)
|
||||
valueToRealValue: AxisValueToValueCallback = value => value
|
||||
realValueToDisplayValue: AxisValueToValueCallback = value => value
|
||||
displayValueToRealValue: AxisValueToValueCallback = value => value
|
||||
realValueToValue: AxisValueToValueCallback = value => value
|
||||
displayValueToText: ((value: number, precision: number) => string) = (value, precision) => formatPrecision(value, precision)
|
||||
|
||||
constructor (parent: DrawPane<Axis>, yAxis: YAxisTemplate) {
|
||||
super(parent)
|
||||
this.override(yAxis)
|
||||
}
|
||||
|
||||
override (yAxis: YAxisTemplate): void {
|
||||
const {
|
||||
name,
|
||||
reverse,
|
||||
scrollZoomEnabled,
|
||||
gap,
|
||||
minSpan,
|
||||
displayValueToText,
|
||||
valueToRealValue,
|
||||
realValueToDisplayValue,
|
||||
displayValueToRealValue,
|
||||
realValueToValue,
|
||||
createRange,
|
||||
createTicks
|
||||
} = yAxis
|
||||
if (!isString(name)) {
|
||||
this.name = name
|
||||
}
|
||||
this.scrollZoomEnabled = scrollZoomEnabled ?? this.scrollZoomEnabled
|
||||
this.reverse = reverse ?? this.reverse
|
||||
merge(this.gap, gap)
|
||||
this.displayValueToText = displayValueToText ?? this.displayValueToText
|
||||
this.minSpan = minSpan ?? this.minSpan
|
||||
this.valueToRealValue = valueToRealValue ?? this.valueToRealValue
|
||||
this.realValueToDisplayValue = realValueToDisplayValue ?? this.realValueToDisplayValue
|
||||
this.displayValueToRealValue = displayValueToRealValue ?? this.displayValueToRealValue
|
||||
this.realValueToValue = realValueToValue ?? this.realValueToValue
|
||||
this.createRange = createRange ?? this.createRange
|
||||
this.createTicks = createTicks ?? this.createTicks
|
||||
}
|
||||
|
||||
protected override createRangeImp (): AxisRange {
|
||||
const parent = this.getParent()
|
||||
const chart = parent.getChart()
|
||||
const chartStore = chart.getChartStore()
|
||||
let min = Number.MAX_SAFE_INTEGER
|
||||
let max = Number.MIN_SAFE_INTEGER
|
||||
const figuresResultList: FiguresResult[] = []
|
||||
let shouldOhlc = false
|
||||
let specifyMin = Number.MAX_SAFE_INTEGER
|
||||
let specifyMax = Number.MIN_SAFE_INTEGER
|
||||
@ -65,10 +114,6 @@ export default abstract class YAxisImp extends AxisImp implements YAxis {
|
||||
if (isNumber(indicator.maxValue)) {
|
||||
specifyMax = Math.max(specifyMax, indicator.maxValue)
|
||||
}
|
||||
figuresResultList.push({
|
||||
figures: indicator.figures ?? [],
|
||||
result: indicator.result ?? []
|
||||
})
|
||||
})
|
||||
|
||||
let precision = 4
|
||||
@ -104,10 +149,10 @@ export default abstract class YAxisImp extends AxisImp implements YAxis {
|
||||
}
|
||||
}
|
||||
}
|
||||
figuresResultList.forEach(({ figures, result }) => {
|
||||
const indicatorData = result[dataIndex] ?? {}
|
||||
indicators.forEach(({ result, figures }) => {
|
||||
const data = result[dataIndex] ?? {}
|
||||
figures.forEach(figure => {
|
||||
const value = indicatorData[figure.key]
|
||||
const value = data[figure.key]
|
||||
if (isNumber(value)) {
|
||||
min = Math.min(min, value)
|
||||
max = Math.max(max, value)
|
||||
@ -123,85 +168,70 @@ export default abstract class YAxisImp extends AxisImp implements YAxis {
|
||||
min = 0
|
||||
max = 10
|
||||
}
|
||||
|
||||
const type = this.getType()
|
||||
let dif: number
|
||||
switch (type) {
|
||||
case YAxisType.Percentage: {
|
||||
const fromData = chartStore.getVisibleFirstData()
|
||||
if (isValid(fromData) && isNumber(fromData.close)) {
|
||||
min = (min - fromData.close) / fromData.close * 100
|
||||
max = (max - fromData.close) / fromData.close * 100
|
||||
}
|
||||
dif = Math.pow(10, -2)
|
||||
break
|
||||
}
|
||||
case YAxisType.Log: {
|
||||
min = log10(min)
|
||||
max = log10(max)
|
||||
dif = 0.05 * index10(-precision)
|
||||
break
|
||||
}
|
||||
default: {
|
||||
dif = index10(-precision)
|
||||
}
|
||||
const defaultDiff = max - min
|
||||
const defaultRange = {
|
||||
from: min,
|
||||
to: max,
|
||||
range: defaultDiff,
|
||||
realFrom: min,
|
||||
realTo: max,
|
||||
realRange: defaultDiff,
|
||||
displayFrom: min,
|
||||
displayTo: max,
|
||||
displayRange: defaultDiff
|
||||
}
|
||||
|
||||
const range = this.createRange?.({
|
||||
kLineDataList: chartStore.getDataList(),
|
||||
visibleDataRange: chartStore.getTimeScaleStore().getVisibleRange(),
|
||||
indicators,
|
||||
defaultRange
|
||||
})
|
||||
let realFrom = range.realFrom
|
||||
let realTo = range.realTo
|
||||
let realRange = range.realRange
|
||||
const minSpan = this.minSpan(precision)
|
||||
if (
|
||||
min === max ||
|
||||
Math.abs(min - max) < dif
|
||||
realFrom === realTo || realRange < minSpan
|
||||
) {
|
||||
const minCheck = specifyMin === min
|
||||
const maxCheck = specifyMax === max
|
||||
min = minCheck ? min : (maxCheck ? min - 8 * dif : min - 4 * dif)
|
||||
max = maxCheck ? max : (minCheck ? max + 8 * dif : max + 4 * dif)
|
||||
const minCheck = specifyMin === realFrom
|
||||
const maxCheck = specifyMax === realTo
|
||||
const halfTickCount = TICK_COUNT / 2
|
||||
realFrom = minCheck ? realFrom : (maxCheck ? realFrom - TICK_COUNT * minSpan : realFrom - halfTickCount * minSpan)
|
||||
realTo = maxCheck ? realTo : (minCheck ? realTo + TICK_COUNT * minSpan : realTo + halfTickCount * minSpan)
|
||||
}
|
||||
|
||||
const height = this.getParent().getYAxisWidget()?.getBounding().height ?? 0
|
||||
const { gap: paneGap } = parent.getOptions()
|
||||
let topRate = paneGap?.top ?? 0.2
|
||||
const height = this.getBounding().height
|
||||
const { top, bottom } = this.gap
|
||||
let topRate = top
|
||||
if (topRate >= 1) {
|
||||
topRate = topRate / height
|
||||
}
|
||||
let bottomRate = paneGap?.bottom ?? 0.1
|
||||
let bottomRate = bottom
|
||||
if (bottomRate >= 1) {
|
||||
bottomRate = bottomRate / height
|
||||
}
|
||||
let range = Math.abs(max - min)
|
||||
// gap
|
||||
min = min - range * bottomRate
|
||||
max = max + range * topRate
|
||||
range = Math.abs(max - min)
|
||||
let realMin: number
|
||||
let realMax: number
|
||||
let realRange: number
|
||||
if (type === YAxisType.Log) {
|
||||
realMin = index10(min)
|
||||
realMax = index10(max)
|
||||
realRange = Math.abs(realMax - realMin)
|
||||
} else {
|
||||
realMin = min
|
||||
realMax = max
|
||||
realRange = range
|
||||
}
|
||||
realRange = realTo - realFrom
|
||||
realFrom = realFrom - realRange * bottomRate
|
||||
realTo = realTo + realRange * topRate
|
||||
|
||||
const from = this.realValueToValue(realFrom, { range })
|
||||
const to = this.realValueToValue(realTo, { range })
|
||||
const displayFrom = this.realValueToDisplayValue(realFrom, { range })
|
||||
const displayTo = this.realValueToDisplayValue(realTo, { range })
|
||||
return {
|
||||
from: min, to: max, range, realFrom: realMin, realTo: realMax, realRange
|
||||
from,
|
||||
to,
|
||||
range: to - from,
|
||||
realFrom,
|
||||
realTo,
|
||||
realRange: realTo - realFrom,
|
||||
displayFrom,
|
||||
displayTo,
|
||||
displayRange: displayTo - displayFrom
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部值转换成坐标
|
||||
* @param value
|
||||
* @return {number}
|
||||
* @private
|
||||
*/
|
||||
_innerConvertToPixel (value: number): number {
|
||||
const height = this.getParent().getYAxisWidget()?.getBounding().height ?? 0
|
||||
const { from, range } = this.getRange()
|
||||
const rate = (value - from) / range
|
||||
return this.isReverse() ? Math.round(rate * height) : Math.round((1 - rate) * height)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是蜡烛图轴
|
||||
* @return {boolean}
|
||||
@ -210,17 +240,6 @@ export default abstract class YAxisImp extends AxisImp implements YAxis {
|
||||
return this.getParent().getId() === PaneIdConstants.CANDLE
|
||||
}
|
||||
|
||||
/**
|
||||
* y轴类型
|
||||
* @return {YAxisType}
|
||||
*/
|
||||
getType (): YAxisType {
|
||||
if (this.isInCandle()) {
|
||||
return this.getParent().getChart().getStyles().yAxis.type
|
||||
}
|
||||
return YAxisType.Normal
|
||||
}
|
||||
|
||||
getPosition (): string {
|
||||
return this.getParent().getChart().getStyles().yAxis.position
|
||||
}
|
||||
@ -249,16 +268,17 @@ export default abstract class YAxisImp extends AxisImp implements YAxis {
|
||||
)
|
||||
}
|
||||
|
||||
protected override createDefaultTicks (): AxisTick[] {
|
||||
const { realFrom, realTo, realRange } = this.getRange()
|
||||
protected override createTicksImp (): AxisTick[] {
|
||||
const range = this.getRange()
|
||||
const { displayFrom, displayTo, displayRange } = range
|
||||
const ticks: AxisTick[] = []
|
||||
|
||||
if (realRange >= 0) {
|
||||
const interval = nice(realRange / 10)
|
||||
if (displayRange >= 0) {
|
||||
const interval = nice(displayRange / TICK_COUNT)
|
||||
const precision = getPrecision(interval)
|
||||
|
||||
const first = round(Math.ceil(realFrom / interval) * interval, precision)
|
||||
const last = round(Math.floor(realTo / interval) * interval, precision)
|
||||
const first = round(Math.ceil(displayFrom / interval) * interval, precision)
|
||||
const last = round(Math.floor(displayTo / interval) * interval, precision)
|
||||
let n = 0
|
||||
let f = first
|
||||
|
||||
@ -271,16 +291,12 @@ export default abstract class YAxisImp extends AxisImp implements YAxis {
|
||||
}
|
||||
}
|
||||
}
|
||||
return this._optimalTicks(ticks)
|
||||
}
|
||||
|
||||
private _optimalTicks (ticks: AxisTick[]): AxisTick[] {
|
||||
const pane = this.getParent()
|
||||
const height = pane.getYAxisWidget()?.getBounding().height ?? 0
|
||||
const chartStore = pane.getChart().getChartStore()
|
||||
const customApi = chartStore.getCustomApi()
|
||||
const optimalTicks: AxisTick[] = []
|
||||
const type = this.getType()
|
||||
const indicators = chartStore.getIndicatorStore().getInstances(pane.getId())
|
||||
const thousandsSeparator = chartStore.getThousandsSeparator()
|
||||
const decimalFoldThreshold = chartStore.getDecimalFoldThreshold()
|
||||
@ -299,25 +315,15 @@ export default abstract class YAxisImp extends AxisImp implements YAxis {
|
||||
const textHeight = chartStore.getStyles().xAxis.tickText.size
|
||||
let validY: number
|
||||
ticks.forEach(({ value }) => {
|
||||
let v: string
|
||||
let y = this._innerConvertToPixel(+value)
|
||||
switch (type) {
|
||||
case YAxisType.Percentage: {
|
||||
v = `${formatPrecision(value, 2)}%`
|
||||
break
|
||||
}
|
||||
case YAxisType.Log: {
|
||||
y = this._innerConvertToPixel(log10(+value))
|
||||
v = formatPrecision(value, precision)
|
||||
break
|
||||
}
|
||||
default: {
|
||||
v = formatPrecision(value, precision)
|
||||
if (shouldFormatBigNumber) {
|
||||
v = customApi.formatBigNumber(value)
|
||||
}
|
||||
break
|
||||
}
|
||||
let v = this.displayValueToText(+value, precision)
|
||||
const y = this.convertToPixel(
|
||||
this.realValueToValue(
|
||||
this.displayValueToRealValue(+value, { range }),
|
||||
{ range }
|
||||
)
|
||||
)
|
||||
if (shouldFormatBigNumber) {
|
||||
v = customApi.formatBigNumber(value)
|
||||
}
|
||||
v = formatFoldDecimal(formatThousands(v, thousandsSeparator), decimalFoldThreshold)
|
||||
const validYNumber = isNumber(validY)
|
||||
@ -376,18 +382,16 @@ export default abstract class YAxisImp extends AxisImp implements YAxis {
|
||||
}
|
||||
})
|
||||
let precision = 2
|
||||
if (this.getType() !== YAxisType.Percentage) {
|
||||
if (this.isInCandle()) {
|
||||
const { price: pricePrecision } = chartStore.getPrecision()
|
||||
const lastValueMarkStyles = styles.indicator.lastValueMark
|
||||
if (lastValueMarkStyles.show && lastValueMarkStyles.text.show) {
|
||||
precision = Math.max(techPrecision, pricePrecision)
|
||||
} else {
|
||||
precision = pricePrecision
|
||||
}
|
||||
if (this.isInCandle()) {
|
||||
const { price: pricePrecision } = chartStore.getPrecision()
|
||||
const lastValueMarkStyles = styles.indicator.lastValueMark
|
||||
if (lastValueMarkStyles.show && lastValueMarkStyles.text.show) {
|
||||
precision = Math.max(techPrecision, pricePrecision)
|
||||
} else {
|
||||
precision = techPrecision
|
||||
precision = pricePrecision
|
||||
}
|
||||
} else {
|
||||
precision = techPrecision
|
||||
}
|
||||
let valueText = formatPrecision(this.getRange().to, precision)
|
||||
if (shouldFormatBigNumber) {
|
||||
@ -414,54 +418,21 @@ export default abstract class YAxisImp extends AxisImp implements YAxis {
|
||||
}
|
||||
|
||||
convertFromPixel (pixel: number): number {
|
||||
const height = this.getParent().getYAxisWidget()?.getBounding().height ?? 0
|
||||
const { from, range } = this.getRange()
|
||||
const height = this.getBounding().height
|
||||
const range = this.getRange()
|
||||
const { realFrom, realRange } = range
|
||||
const rate = this.isReverse() ? pixel / height : 1 - pixel / height
|
||||
const value = rate * range + from
|
||||
switch (this.getType()) {
|
||||
case YAxisType.Percentage: {
|
||||
const fromData = this.getParent().getChart().getChartStore().getVisibleFirstData()
|
||||
if (isValid(fromData) && isNumber(fromData.close)) {
|
||||
return fromData.close * value / 100 + fromData.close
|
||||
}
|
||||
return 0
|
||||
}
|
||||
case YAxisType.Log: {
|
||||
return index10(value)
|
||||
}
|
||||
default: {
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
convertToRealValue (value: number): number {
|
||||
let v = value
|
||||
if (this.getType() === YAxisType.Log) {
|
||||
v = index10(value)
|
||||
}
|
||||
return v
|
||||
const realValue = rate * realRange + realFrom
|
||||
return this.realValueToValue(realValue, { range })
|
||||
}
|
||||
|
||||
convertToPixel (value: number): number {
|
||||
let v = value
|
||||
switch (this.getType()) {
|
||||
case YAxisType.Percentage: {
|
||||
const fromData = this.getParent().getChart().getChartStore().getVisibleFirstData()
|
||||
if (isValid(fromData) && isNumber(fromData.close)) {
|
||||
v = (value - fromData.close) / fromData.close * 100
|
||||
}
|
||||
break
|
||||
}
|
||||
case YAxisType.Log: {
|
||||
v = log10(value)
|
||||
break
|
||||
}
|
||||
default: {
|
||||
v = value
|
||||
}
|
||||
}
|
||||
return this._innerConvertToPixel(v)
|
||||
const range = this.getRange()
|
||||
const realValue = this.valueToRealValue(value, { range })
|
||||
const height = this.getParent().getYAxisWidget()?.getBounding().height ?? 0
|
||||
const { realFrom, realRange } = range
|
||||
const rate = (realValue - realFrom) / realRange
|
||||
return this.isReverse() ? Math.round(rate * height) : Math.round((1 - rate) * height)
|
||||
}
|
||||
|
||||
convertToNicePixel (value: number): number {
|
||||
@ -470,14 +441,10 @@ export default abstract class YAxisImp extends AxisImp implements YAxis {
|
||||
return Math.round(Math.max(height * 0.05, Math.min(pixel, height * 0.98)))
|
||||
}
|
||||
|
||||
static extend (template: AxisTemplate): YAxisConstructor {
|
||||
static extend (template: YAxisTemplate): YAxisConstructor {
|
||||
class Custom extends YAxisImp {
|
||||
createRange (params: AxisCreateRangeParams): AxisRange {
|
||||
return template.createRange?.(params) ?? params.defaultRange
|
||||
}
|
||||
|
||||
createTicks (params: AxisCreateTicksParams): AxisTick[] {
|
||||
return template.createTicks?.(params) ?? params.defaultTicks
|
||||
constructor (parent: DrawPane<Axis>) {
|
||||
super(parent, template)
|
||||
}
|
||||
}
|
||||
return Custom
|
||||
|
@ -15,10 +15,10 @@
|
||||
import { type AxisTemplate } from '../../component/Axis'
|
||||
import XAxisImp, { type XAxisConstructor } from '../../component/XAxis'
|
||||
|
||||
import defaultXAxis from './default'
|
||||
import normal from './normal'
|
||||
|
||||
const xAxises: Record<string, XAxisConstructor> = {
|
||||
default: XAxisImp.extend(defaultXAxis)
|
||||
normal: XAxisImp.extend(normal)
|
||||
}
|
||||
|
||||
function registerXAxis (axis: AxisTemplate): void {
|
||||
|
@ -15,8 +15,7 @@
|
||||
import { type AxisTemplate } from '../../component/Axis'
|
||||
|
||||
const defaultXAxis: AxisTemplate = {
|
||||
name: 'default',
|
||||
createTicks: ({ defaultTicks }) => defaultTicks
|
||||
name: 'default'
|
||||
}
|
||||
|
||||
export default defaultXAxis
|
@ -15,10 +15,14 @@
|
||||
import { type AxisTemplate } from '../../component/Axis'
|
||||
import YAxisImp, { type YAxisConstructor } from '../../component/YAxis'
|
||||
|
||||
import defaultYAxis from './default'
|
||||
import normal from './normal'
|
||||
import percentage from './percentage'
|
||||
import logarithm from './logarithm'
|
||||
|
||||
const yAxises: Record<string, YAxisConstructor> = {
|
||||
default: YAxisImp.extend(defaultYAxis)
|
||||
normal: YAxisImp.extend(normal),
|
||||
percentage: YAxisImp.extend(percentage),
|
||||
logarithm: YAxisImp.extend(logarithm)
|
||||
}
|
||||
|
||||
function registerYAxis (axis: AxisTemplate): void {
|
||||
|
51
src/extension/y-axis/logarithm.ts
Normal file
51
src/extension/y-axis/logarithm.ts
Normal file
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Licensed 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 { log10, index10 } from '../../common/utils/number'
|
||||
import { type AxisTemplate } from '../../component/Axis'
|
||||
|
||||
const logarithm: AxisTemplate = {
|
||||
name: 'logarithm',
|
||||
minSpan: (precision) => 0.05 * index10(-precision),
|
||||
valueToRealValue: (value) => {
|
||||
return log10(value)
|
||||
},
|
||||
realValueToDisplayValue: (value) => {
|
||||
return index10(value)
|
||||
},
|
||||
displayValueToRealValue: (value) => {
|
||||
return log10(value)
|
||||
},
|
||||
realValueToValue: (value) => {
|
||||
return index10(value)
|
||||
},
|
||||
createRange: ({ defaultRange }) => {
|
||||
const { from, to, range } = defaultRange
|
||||
const realFrom = log10(from)
|
||||
const realTo = log10(to)
|
||||
return {
|
||||
from,
|
||||
to,
|
||||
range,
|
||||
realFrom,
|
||||
realTo,
|
||||
realRange: realTo - realFrom,
|
||||
displayFrom: from,
|
||||
displayTo: to,
|
||||
displayRange: range
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default logarithm
|
@ -14,9 +14,8 @@
|
||||
|
||||
import { type AxisTemplate } from '../../component/Axis'
|
||||
|
||||
const defaultYAxis: AxisTemplate = {
|
||||
name: 'default',
|
||||
createTicks: ({ defaultTicks }) => defaultTicks
|
||||
const normal: AxisTemplate = {
|
||||
name: 'normal'
|
||||
}
|
||||
|
||||
export default defaultYAxis
|
||||
export default normal
|
52
src/extension/y-axis/percentage.ts
Normal file
52
src/extension/y-axis/percentage.ts
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Licensed 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 { formatPrecision } from '../../common/utils/format'
|
||||
import { isValid } from '../../common/utils/typeChecks'
|
||||
import { type AxisTemplate } from '../../component/Axis'
|
||||
|
||||
const percentage: AxisTemplate = {
|
||||
name: 'percentage',
|
||||
minSpan: () => Math.pow(10, -2),
|
||||
displayValueToText: value => `${formatPrecision(value, 2)}%`,
|
||||
valueToRealValue: (value, { range }) => {
|
||||
return (value - range.from) / range.range * range.realRange + range.realFrom
|
||||
},
|
||||
realValueToValue: (value, { range }) => {
|
||||
return (value - range.realFrom) / range.realRange * range.range + range.from
|
||||
},
|
||||
createRange: ({ defaultRange, visibleDataRange, kLineDataList }) => {
|
||||
const kLineData = kLineDataList[visibleDataRange.from]
|
||||
if (isValid(kLineData)) {
|
||||
const { from, to, range } = defaultRange
|
||||
const realFrom = (defaultRange.from - kLineData.close) / kLineData.close * 100
|
||||
const realTo = (defaultRange.to - kLineData.close) / kLineData.close * 100
|
||||
const realRange = realTo - realFrom
|
||||
return {
|
||||
from,
|
||||
to,
|
||||
range,
|
||||
realFrom,
|
||||
realTo,
|
||||
realRange,
|
||||
displayFrom: realFrom,
|
||||
displayTo: realTo,
|
||||
displayRange: realRange
|
||||
}
|
||||
}
|
||||
return defaultRange
|
||||
}
|
||||
}
|
||||
|
||||
export default percentage
|
@ -39,7 +39,7 @@ export default abstract class DrawPane<C extends Axis = Axis> extends Pane {
|
||||
|
||||
private _axis: C
|
||||
|
||||
private readonly _options: PickPartial<DeepRequired<Omit<PaneOptions, 'id' | 'height'>>, 'position'> = { minHeight: PANE_MIN_HEIGHT, dragEnabled: true, gap: { top: 0.2, bottom: 0.1 }, axisOptions: { name: 'default', scrollZoomEnabled: true } }
|
||||
private readonly _options: PickPartial<DeepRequired<Omit<PaneOptions, 'id' | 'height'>>, 'position'> = { minHeight: PANE_MIN_HEIGHT, dragEnabled: true, gap: { top: 0.2, bottom: 0.1 }, axisOptions: { name: 'normal', scrollZoomEnabled: true } }
|
||||
|
||||
constructor (rootContainer: HTMLElement, afterElement: Nullable<HTMLElement>, chart: Chart, id: string, options: Omit<PaneOptions, 'id' | 'height'>) {
|
||||
super(rootContainer, afterElement, chart, id)
|
||||
@ -55,9 +55,14 @@ export default abstract class DrawPane<C extends Axis = Axis> extends Pane {
|
||||
(this._options.axisOptions.name !== name && isString(name)) ||
|
||||
!isValid(this._axis)
|
||||
) {
|
||||
this._axis = this.createAxisComponent(name ?? 'default')
|
||||
this._axis = this.createAxisComponent(name ?? 'normal')
|
||||
}
|
||||
merge(this._options, options)
|
||||
this._axis.override({
|
||||
name: name ?? 'normal',
|
||||
gap: this._options.gap,
|
||||
...this._options.axisOptions
|
||||
})
|
||||
let container: HTMLElement
|
||||
let cursor: string
|
||||
if (this.getId() === PaneIdConstants.X_AXIS) {
|
||||
|
@ -12,15 +12,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type AxisCreate } from '../component/Axis'
|
||||
|
||||
export interface PaneGap {
|
||||
top?: number
|
||||
bottom?: number
|
||||
}
|
||||
|
||||
export interface PaneAxisOptions {
|
||||
name?: string
|
||||
scrollZoomEnabled?: boolean
|
||||
}
|
||||
export type PaneAxisOptions = Partial<AxisCreate>
|
||||
|
||||
export const enum PanePosition {
|
||||
Top = 'top',
|
||||
|
@ -200,7 +200,10 @@ export default class TimeScaleStore {
|
||||
|
||||
adjustVisibleTimeTickList (): void {
|
||||
const tickTextStyles = this._chartStore.getStyles().xAxis.tickText
|
||||
const width = calcTextWidth('0000-00-00 00:00', tickTextStyles.size, tickTextStyles.weight, tickTextStyles.family)
|
||||
const width = Math.max(
|
||||
Math.ceil(this._totalBarSpace / 10),
|
||||
calcTextWidth('0000-00-00 00:00', tickTextStyles.size, tickTextStyles.weight, tickTextStyles.family)
|
||||
)
|
||||
const barCount = Math.ceil(width / this._barSpace)
|
||||
let tickList: TimeTick[] = []
|
||||
Array.from(this._timeTicks.keys()).sort((w1, w2) => w2 - w1).forEach(key => {
|
||||
|
@ -12,8 +12,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { YAxisType } from '../common/Styles'
|
||||
import { formatPrecision, formatThousands, formatFoldDecimal } from '../common/utils/format'
|
||||
import { formatThousands, formatFoldDecimal } from '../common/utils/format'
|
||||
import { isValid } from '../common/utils/typeChecks'
|
||||
|
||||
import View from './View'
|
||||
@ -45,14 +44,14 @@ export default class CandleLastPriceLabelView extends View {
|
||||
} else {
|
||||
backgroundColor = lastPriceMarkStyles.noChangeColor
|
||||
}
|
||||
let text: string
|
||||
if (yAxis.getType() === YAxisType.Percentage) {
|
||||
const fromData = chartStore.getVisibleFirstData()
|
||||
const fromClose = fromData!.close
|
||||
text = `${((close - fromClose) / fromClose * 100).toFixed(2)}%`
|
||||
} else {
|
||||
text = formatPrecision(close, precision.price)
|
||||
}
|
||||
const yAxisRange = yAxis.getRange()
|
||||
let text = yAxis.displayValueToText(
|
||||
yAxis.realValueToDisplayValue(
|
||||
yAxis.valueToRealValue(close, { range: yAxisRange }),
|
||||
{ range: yAxisRange }
|
||||
),
|
||||
precision.price
|
||||
)
|
||||
text = formatFoldDecimal(formatThousands(text, chartStore.getThousandsSeparator()), chartStore.getDecimalFoldThreshold())
|
||||
let x: number
|
||||
let textAlgin: CanvasTextAlign
|
||||
|
@ -14,13 +14,13 @@
|
||||
|
||||
import type Bounding from '../common/Bounding'
|
||||
import type Crosshair from '../common/Crosshair'
|
||||
import { type CrosshairStyle, type CrosshairDirectionStyle, YAxisType, type StateTextStyle } from '../common/Styles'
|
||||
import { type CrosshairStyle, type CrosshairDirectionStyle, type StateTextStyle } from '../common/Styles'
|
||||
import { isString } from '../common/utils/typeChecks'
|
||||
import { formatPrecision, formatThousands, formatFoldDecimal } from '../common/utils/format'
|
||||
import { formatThousands, formatFoldDecimal } from '../common/utils/format'
|
||||
import { createFont } from '../common/utils/canvas'
|
||||
|
||||
import { type Axis } from '../component/Axis'
|
||||
import { type YAxis } from '../component/YAxis'
|
||||
import type YAxis from '../component/YAxis'
|
||||
|
||||
import { type TextAttrs } from '../extension/figure/text'
|
||||
|
||||
@ -65,28 +65,30 @@ export default class CrosshairHorizontalLabelView<C extends Axis = YAxis> extend
|
||||
protected getText (crosshair: Crosshair, chartStore: ChartStore, axis: Axis): string {
|
||||
const yAxis = axis as unknown as YAxis
|
||||
const value = axis.convertFromPixel(crosshair.y!)
|
||||
let text: string
|
||||
if (yAxis.getType() === YAxisType.Percentage) {
|
||||
const fromData = chartStore.getVisibleFirstData()
|
||||
text = `${((value - fromData!.close) / fromData!.close * 100).toFixed(2)}%`
|
||||
let precision = 0
|
||||
let shouldFormatBigNumber = false
|
||||
if (yAxis.isInCandle()) {
|
||||
precision = chartStore.getPrecision().price
|
||||
} else {
|
||||
const indicators = chartStore.getIndicatorStore().getInstances(crosshair.paneId!)
|
||||
let precision = 0
|
||||
let shouldFormatBigNumber = false
|
||||
if (yAxis.isInCandle()) {
|
||||
precision = chartStore.getPrecision().price
|
||||
} else {
|
||||
indicators.forEach(indicator => {
|
||||
precision = Math.max(indicator.precision, precision)
|
||||
if (!shouldFormatBigNumber) {
|
||||
shouldFormatBigNumber = indicator.shouldFormatBigNumber
|
||||
}
|
||||
})
|
||||
}
|
||||
text = formatPrecision(value, precision)
|
||||
if (shouldFormatBigNumber) {
|
||||
text = chartStore.getCustomApi().formatBigNumber(text)
|
||||
}
|
||||
indicators.forEach(indicator => {
|
||||
precision = Math.max(indicator.precision, precision)
|
||||
if (!shouldFormatBigNumber) {
|
||||
shouldFormatBigNumber = indicator.shouldFormatBigNumber
|
||||
}
|
||||
})
|
||||
}
|
||||
const yAxisRange = yAxis.getRange()
|
||||
let text = yAxis.displayValueToText(
|
||||
yAxis.realValueToDisplayValue(
|
||||
yAxis.valueToRealValue(value, { range: yAxisRange }),
|
||||
{ range: yAxisRange }
|
||||
),
|
||||
precision
|
||||
)
|
||||
|
||||
if (shouldFormatBigNumber) {
|
||||
text = chartStore.getCustomApi().formatBigNumber(text)
|
||||
}
|
||||
return formatFoldDecimal(formatThousands(text, chartStore.getThousandsSeparator()), chartStore.getDecimalFoldThreshold())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user