微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在画布图表的每个蜡烛上方放置几个符号

如何解决在画布图表的每个蜡烛上方放置几个符号

创建了我所需的简化示例。该系列中有50件物品。每个项目都有一个垂直的列表,其中包含五个+符号。

const data = [...Array(50).keys()];

const container = document.querySelector('d3fc-canvas');
const xScale = d3.scaleLinear().domain([0,data.length - 1]);
const yScale = d3.scaleLinear().domain(fc.extentLinear()(data));

const series = fc
  .seriesCanvasPoint()
  .xScale(xScale)
  .yScale(yScale)
  .crossValue((_,i) => i)
  .mainValue(d => d)
  .size((_,i) => 0)
  .decorate((context,datum) => {

    // Draw 5 symbols above current value

    for (let i = 0; i < 5; i++) {

      const y = yScale.invert(datum + i)

      context.textAlign = 'center';
      context.fillStyle = '#000';
      context.font = '25px Arial';
      context.fillText('+',0);
      context.translate(0,y);
    }
  });

d3.select(container)
  .on('draw',() => {
    series(data);
  })
  .on('measure',() => {
    const {
      width,height
    } = event.detail;
    xScale.range([0,width]);
    yScale.range([height,0]);

    const ctx = container.querySelector('canvas').getContext('2d');
    series.context(ctx);
  });

container.requestRedraw();
body {
  color: #1b1e23;
  font-size: small;
  font-family: sans-serif;
  height: calc(100vh - 2em);
  margin: 1em;
  display: flex;
}

body>* {
  flex: auto;
}

.domain,.gridline-y,.gridline-x,.annotation-line>line {
  stroke: currentColor;
  stroke-opacity: 0.1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://unpkg.com/d3fc/build/d3fc.js"></script>
<d3fc-canvas use-device-pixel-ratio></d3fc-canvas>

现在,我正在尝试使其与实际的财务图表配合使用,并将这五个+符号垂直放置在每个蜡烛中。它不起作用,因为我不知道如何正确地将每个+的Y值转换为画布的坐标系。

const stream = fc.randomFinancial().stream();
const data = stream.take(20);
const scaleX = d3.scalePoint();
const scaleY = d3.scaleLinear();
const yExtent = fc.extentLinear().accessors([d => d.high,d => d.low]);
const xExtent = fc.extentLinear().accessors([d => d.date.getTime()]);
const gridlines = fc.annotationCanvasGridline();
const candlestick = fc.seriesCanvasCandlestick()
  .crossValue((o,i) => o.date.getTime())
  .lowValue((o,i) => o.low)
  .highValue((o,i) => o.high)
  .openValue((o,i) => o.open)
  .closeValue((o,i) => o.close);

const points = fc
  .seriesCanvasPoint()
  .crossValue((o,i) => o.date.getTime())
  .mainValue(d => d.close)
  .size((_,datum) => {

    const arrows = getMarks(datum.open,datum.close);

    //context.translate(0,0);

    // Draw 5 symbols above current value

    for (let i = 0; i < arrows.length; i++) {

      const posCurrent = scaleY.invert(arrows[i])

      //context.translate(0,15);
      context.translate(0,posCurrent); // move to current arrow's position
      context.textAlign = 'center';
      context.fillStyle = '#000';
      context.font = '25px Arial';
      context.fillText('+',-posCurrent); // reset and go back to start point
    }
  });

const multi = fc
  .seriesCanvasMulti()
  .series([gridlines,candlestick,points]);

const chart = fc
  .chartCartesian(scaleX,scaleY)
  .canvasPlotArea(multi);

function getMarks(open,close) {

  let price = close;

  const arrows = [];
  const gap = Math.abs(close - open) / 5;

  for (let i = 0; i < 5; i++) {
    arrows.push(close > open ? price -= gap : price += gap);
  }

  return arrows;
}

function renderChart() {

  data.push(stream.next());
  data.shift();

  scaleX.domain(data.map(o => o.date.getTime()));
  scaleY.domain(yExtent(data));

  chart
    .xDomain(scaleX.domain())
    .yDomain(scaleY.domain());

  d3.select('#chart')
    .datum(data)
    .call(chart);
}

renderChart();
setInterval(renderChart,1000);
#chart {
  height: 500px;
}
<script src="https://unpkg.com/d3"></script>
<script src="https://unpkg.com/d3fc"></script>
<div id="chart"></div>

有没有办法在蜡烛上方正确显示这些标记

交叉引用Github以获得更多详细信息和示例。

https://github.com/d3fc/d3fc/issues/1635

解决方法

首先,scale.invert用于具有像素坐标但不具有相应基准值的情况。例如,当您在屏幕上单击时,这是相关的。在这种情况下,它不是完成这项工作的正确工具。您已经可以访问datum,因此只需致电scaleY(datum.close)

但是,d3fc使用canvas.translate()将零点更改为.mainvalue()。因此,您不能简单地使用scaleY(datum.close)来获取关闭值的y坐标。您要绘制的每个点将变为(x,y),而不是(0,y(arrow) - y(d.close))。因此,您需要更改逻辑以反映这一点。

需要自己动手清理context.translate()是解决错误的秘诀,避免这种情况几乎总是更好。


最后一点,我对您的getMarks函数有些困惑。如果要绘制5条均匀分布在条形上的箭头,则需要有条形高度的1/4的间隙。可以把它想象成街道上的树木。如果树木相距10英尺,而街道长100英尺,那么您将拥有11棵树,而不是10棵树。因此,我画了6个加号,而不是5个,因为否则看起来有些失衡。

const stream = fc.randomFinancial().stream();
const data = stream.take(20);
const scaleX = d3.scalePoint();
const scaleY = d3.scaleLinear();
const yExtent = fc.extentLinear().accessors([d => d.high,d => d.low]);
const xExtent = fc.extentLinear().accessors([d => d.date.getTime()]);
const gridlines = fc.annotationCanvasGridline();
const candlestick = fc.seriesCanvasCandlestick()
  .crossValue((o,i) => o.date.getTime())
  .lowValue((o,i) => o.low)
  .highValue((o,i) => o.high)
  .openValue((o,i) => o.open)
  .closeValue((o,i) => o.close);

const points = fc
  .seriesCanvasPoint()
  .crossValue((o,i) => o.date.getTime())
  .mainValue(d => d.close)
  .size((_,i) => 0)
  .decorate((context,datum) => {
    const arrows = getMarks(datum.open,datum.close);
    const top = scaleY(datum.close);

    // We need a little bit of offset,because the "+" is 20 pixels high
    // and we want it in the middle of the point,not above it.
    const offset = 10;

    // Draw 5 symbols above current value
    for (let i = 0; i < arrows.length; i++) {
      context.textAlign = 'center';
      context.fillStyle = '#000';
      context.font = '25px Arial';
      context.fillText('+',scaleY(arrows[i]) - top + offset);
    }
  });

const multi = fc
  .seriesCanvasMulti()
  .series([gridlines,candlestick,points]);

const chart = fc
  .chartCartesian(scaleX,scaleY)
  .canvasPlotArea(multi);

function getMarks(open,close) {

  let price = Math.min(open,close);

  const arrows = [];
  const gap = Math.abs(close - open) / 5;

  for (let i = 0; i < 6; i++) {
    arrows.push(price + i * gap);
  }

  return arrows;
}

function renderChart() {

  data.push(stream.next());
  data.shift();

  scaleX.domain(data.map(o => o.date.getTime()));
  scaleY.domain(yExtent(data));

  chart
    .xDomain(scaleX.domain())
    .yDomain(scaleY.domain());

  d3.select('#chart')
    .datum(data)
    .call(chart);
}

renderChart();
setInterval(renderChart,1000);
#chart {
  height: 500px;
}
<script src="https://unpkg.com/d3"></script>
<script src="https://unpkg.com/d3fc"></script>
<div id="chart"></div>

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?