chart/cl.chart.js

/**
 * Copyright (c) 2018-present clchart Contributors.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 */

// Following is the entity definition of ClChart
// generally only use this class to implement the function
// A ClChart class is just a drawing of the chart belonging to the dispatch framework, mouse and keyboard events, and does not store data
// When multiple maps are linked, the subgraphs are all here to get relevant information.

import {
  copyJsonOfDeep
} from '../util/cl.tool'
import ClChartLine from './cl.chart.line'
import ClChartOrder from './cl.chart.order'
import { setColor, setStandard } from '../chart/cl.chart.init'

const DEFAULT_LINKINFO = {
  showMode: 'last',
  // 'last' Targeted by the latest data, maxIndex=-1 shows the latest data
  // 'move' Set this parameter when the move occurs, -- may not be required
  // 'fixed' Fixed display of a certain range of time-shared charts and 5-day lines.
  // 'locked' Specify a record in the middle
  fixed: { // 对应fixed模式
    min: -1, // 最小记录, 为-1表示最小记录加上left
    max: -1, // 最大记录, 为-1表式最大记录减去right
    left: 20,
    right: 20
  },
  locked: { // 只有当showMode=='locked'模式才有作用
    index: -1, // 当前锁定的记录号,
    set: 0.5 // 表示锁定在中间,1表示锁定在最后一条记录,当前记录的百分比
  },
  minIndex: -1, // 当前画面的起始记录号 -1 表示第一次
  maxIndex: -1, // 当前画面的最大记录号 -1 表示第一次
  hotIndex: -1, // 循环时需要定位当前数据的指针定位
  showCursorLine: false, // 是否显示光标信息
  moveIndex: -1, // 当前鼠标所在位置的记录号 -1 表示没有鼠标事件或第一次
  spaceX: 2, // 每个数据的间隔像素,可以根据实际情况变化,但不能系统参数里设定的spaceX小
  unitX: 5, // 每天数据的宽度 默认为5, 可以在1..50之间切换
  rightMode: 'no', // 除权模式
  hideInfo: false // 是否显示价格
}
/**
 * Class representing ClChart
 * @export
 * @class ClChart
 */
export default class ClChart {
  /**

   * Creates an instance of ClChart.
   * @param {Object} syscfg
   */
  constructor (syscfg) {
    this.context = syscfg.mainCanvas.context
    this.cursorCanvas = syscfg.cursorCanvas
    this.sysColor = syscfg.sysColor
    // Use this to determine if it is the root element
    this.father = undefined
  }
  /**
   * Re-initialize Chart to clean up all charts and data
   * @param {any} dataLayer data layer
   * @param {any} eventLayer event layer
   * @memberof ClChart
   */
  initChart (dataLayer, eventLayer) {
    // linkInfo 是所有子chart公用的参数集合,也是dataLayer应用的集合
    this.linkInfo = copyJsonOfDeep(DEFAULT_LINKINFO)
    // this.checkConfig();
    // 初始化子chart为空
    this.childCharts = {}
    // 设置数据层,同时对外提供设置接口
    this.setDataLayer(dataLayer)
    // 设置事件层,同时对外提供设置接口
    this.setEventLayer(eventLayer)
  }
  /**
   * clear current data and recharge linkinfo
   *
   * @memberof ClChart
   */
  clear () {
    this.childCharts = {}
    this.fastDraw = false
    this.dataLayer.clearData()
    // this.eventLayer.clear();
    this.linkInfo = copyJsonOfDeep(DEFAULT_LINKINFO)
  }
  /**
   * get child chart by key
   * @param {String} key child chart key
   * @return {Object} child chart
   * @memberof ClChart
   */
  getChart (key) {
    return this.childCharts[key]
  }

  // 以下是设置chart的事件关联接口,element表示映射的chart类
  // 所有事件由外部获取事件后传递到eventLayer后,再统一分发给相应的图表
  // eventLayer中有针对html5的鼠标键盘事件处理接口,其他事件处理接口另外再做

  /**
   * get event layer
   * @return {Object} event layer
   * @memberof ClChart
   */
  getEventLayer () {
    return this.eventLayer
  }
  setEventLayer (layer) {
    if (layer === undefined) return
    this.eventLayer = layer
    this.eventLayer.bindChart && this.eventLayer.bindChart(this) // 把chart绑定到事件处理层
  }
  // bindEvent (chart) {
  //   this.eventLayer.bindEvent(chart);
  // }

  /**
   * get data layer
   * @return {Object} data layer
   * @memberof ClChart
   */
  getDataLayer () {
    return this.dataLayer
  }
  /**
   * set data layer
   * @param {Object} layer
   * @memberof ClChart
   */
  setDataLayer (layer) {
    if (layer === undefined) return
    this.dataLayer = layer
    layer.father = this
    this.static = this.dataLayer.static
  }
  /**
   * Set the corresponding basic data key of the chart
   * @param {Object} chart
   * @param {String} key
   * @memberof ClChart
   */
  bindData (chart, key) {
    if (chart.hotKey !== key) {
      this.linkInfo.showMode = 'last' // 切换数据后需要重新画图
      this.linkInfo.minIndex = -1
      chart.hotKey = key // hotKey 针对chart的数据都调用该数据源
      this.fastDrawEnd() // 热点数据改变,就重新计算
    }
  }
  /**
   * init data
   * @param {Number} tradeDate trade date
   * @param {Number} tradetime  trade time
   * @memberof ClChart
   */
  initData (tradeDate, tradetime) {
    this.dataLayer.initData(tradeDate, tradetime)
  }
  /**
   * set data
   * @param {String} key data key
   * @param {Object} fields data field definition
   * @param {any} value
   * @memberof ClChart
   */
  setData (key, fields, value) {
    let info = value
    if (typeof value === 'string') info = JSON.parse(value)
    this.dataLayer.setData(key, fields, info)
    this.fastDrawEnd() // 新的数据被设置,就重新计算
  }
  /**
   * get data from datalayer by key
   * @param {String} key
   * @return {Array}
   * @memberof ClChart
   */
  getData (key) {
    if (this.fastDraw) {
      if (this.fastBuffer[key] !== undefined) {
        return this.fastBuffer[key]
      }
    }
    const out = this.dataLayer.getData(key, this.linkInfo.rightMode)
    if (this.fastDraw) {
      this.fastBuffer[key] = out
    }
    return out
  }
  /**
   * init line data
   * @param {Array} data
   * @param {Array} lines
   * @memberof ClChart
   */
  readyData (data, lines) {
    for (let k = 0; k < lines.length; k++) {
      if (lines[k].formula === undefined) continue
      if (!this.fastDraw || (this.fastDraw && this.fastBuffer[lines[k].formula.key] === undefined)) {
        this.dataLayer.makeLineData(
          { data, minIndex: this.linkInfo.minIndex, maxIndex: this.linkInfo.maxIndex },
          lines[k].formula.key,
          lines[k].formula.command
        )
      }
    }
  }
  /**
   * create chart
   * @param {String} name name is a unique name, the same name will override the previous class with the same name
   * @param {String} className class name
   * @param {Object} usercfg user custom config
   * @param {Function} callback callback
   * @return {Object} chart instance
   * @memberof ClChart
   */
  createChart (name, className, usercfg, callback) {
    let chart
    switch (className) {
      case 'CHART.ORDER': chart = new ClChartOrder(this); break
      case 'CHART.LINE': chart = new ClChartLine(this); break
      default : chart = new ClChartLine(this); break
    }

    chart.name = name
    this.childCharts[name] = chart

    // this.bindEvent(chart);
    chart.init(usercfg, callback) // 根据用户配置初始化信息框

    return chart
  }

  /**
   * draw all the layers contained in this area
   * @param {Object} chart
   * @memberof ClChart
   */
  onPaint (chart) {
    if (typeof this.context._beforePaint === 'function') {
      this.context._beforePaint()
    }
    this.fastDrawBegin()

    for (const key in this.childCharts) {
      if (chart !== undefined) {
        if (this.childCharts[key] === chart) {
          this.childCharts[key].onPaint()
        }
      } else {
        this.childCharts[key].onPaint()
      }
    }
    // this.fastDrawEnd();
    if (typeof this.context._afterPaint === 'function') {
      this.context._afterPaint()
    }
  }
  /**
   * Used for the same group of multi-graph only take data once, this can speed up the display, the program structure will not be chaotic
   * @memberof ClChart
   */
  fastDrawBegin () {
    if (!this.fastDraw) {
      this.fastBuffer = []
      this.fastDraw = true
    }
  }
  /**
   * set whether to turn on quick drawing
   * @memberof ClChart
   */
  fastDrawEnd () {
    this.fastDraw = false
  }

  /**
   * Set whether to hide the information bar
   * @param {String} isHideInfo
   * @memberof ClChart
   */
  setHideInfo (isHideInfo) {
    if (isHideInfo === undefined) return
    if (isHideInfo !== this.linkInfo.hideInfo) {
      this.linkInfo.hideInfo = isHideInfo
      this.onPaint()
    }
  }
  /**
   * Set the background color
   * @param {String} sysColor
   * @param {Object | null} chart
   * @memberof ClChart
   */
  setColor (sysColor, chart) {
    this.color = setColor(sysColor)
    if (chart === undefined) chart = this
    for (const key in chart.childCharts) {
      chart.childCharts[key].color = this.color
      this.setColor(sysColor, chart.childCharts[key])
    }
    // 需要将其子配置的颜色也一起改掉
    for (const key in chart.childDraws) {
      chart.childDraws[key].color = this.color
      this.setColor(sysColor, chart.childDraws[key])
    }
    for (const key in chart.childLines) {
      chart.childLines[key].color = this.color
      this.setColor(sysColor, chart.childLines[key])
    }
    this.sysColor = sysColor
  }
  /**
   * setting drafting standards
   * @param {String} standard
   * @memberof ClChart
   */
  setStandard (standard) {
    setStandard(standard)
    this.setColor(this.sysColor)
    this.onPaint()
  }
  // this.makeLineData = function(data, key, formula, punit) {
  //   return this.dataLayer.makeLineData(data, key, this.linkInfo.minIndex, this.linkInfo.maxIndex, formula);
  // }
}