You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于SVG的价格数据线条图表绘制逻辑求助

问题描述

我设置了5条间距为30px的水平线,第一条对应价格最大值,最后一条对应价格最小值。现有一个包含13个不同数值的价格数组,希望根据每个价格在对应水平位置绘制绿色线条(制作简易图表)。按照@Mina推荐的方案实现后无法正常运行,相关代码如下:

原代码

HTML

<svg id="main" xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="220"></svg>

JavaScript

var prices = [];
prices.push(28990, 30240, 30890, 32162, 31000, 31820, 31770, 30080, 28340, 28620, 28640, 27930, 28850);

var minPrice = Math.floor(Math.min(...prices)/1000)*1000;
var maxPrice = Math.ceil(Math.max(...prices)/1000)*1000;

function addText(x, y, t) {
  var text = document.createElementNS('http://www.w3.org/2000/svg','text');
  
  text.setAttribute('x', x);     
  text.setAttribute('y', y); 
  text.setAttribute('fill', 'black');

  var textNode = document.createTextNode(t);
  text.appendChild(textNode);
  
  document.getElementById('main').appendChild(text);
}

function addLine(id, x1, y1, x2, y2, sc,sw) {
  const line = document.createElementNS('http://www.w3.org/2000/svg','line');
  
  line.setAttribute('id', id);
  line.setAttribute('x1', x1);
  line.setAttribute('y1', y1);
  line.setAttribute('x2', x2);
  line.setAttribute('y2', y2);
  line.setAttribute('stroke', sc);
  line.setAttribute('stroke-width', sw);

  document.getElementById("main").appendChild(line);
}

function addHorizontalLinesPrice() {
  var m = maxPrice;
  addText(20, 55, m);
  addText(20, 85, m = m-1500);
  addText(20, 115, m = m-1500);
  addText(20, 145, m = m-1500);
  addText(20, 175, m = m-1500);
}

addHorizontalLinesPrice();

function addHorizontalLines() {
  var y = 50.5;
  
  for (var i = 0; i < 5; i++) {
    addLine("horizontalLine"+(i+1), 90, y, window.innerWidth-20, y, "black", 0.5);
    y = y + 30;
  }
}

addHorizontalLines();

var totalHeight = 100;

function getYPosition(price) {
    var scale = (price - minPrice) / (maxPrice - minPrice);
    return totalHeight - (scale * totalHeight);
}

prices.forEach(price => {
    var x = 20;
    var yPos = getYPosition(price);

    addLine("", x, yPos, x+200, yPos, "green", 3);
    x = x + 100;
});

问题分析及修正方案

原代码存在3个关键错误导致图表无法正常显示:

  1. Y坐标映射基准错误:水平线的Y范围是50.5到170.5,总高度为120px而非100px;且SVG Y轴从上到下递增,价格越高对应Y值应越小,原计算逻辑完全错位。
  2. X值未循环递增:forEach循环中每次重置x=20,导致所有绿线重叠。
  3. 硬编码刻度维护性差:手动写死刻度数值,若价格范围变化需逐一修改,易出错。

修正后的完整代码

HTML

<svg id="main" xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="220"></svg>

JavaScript

var prices = [28990, 30240, 30890, 32162, 31000, 31820, 31770, 30080, 28340, 28620, 28640, 27930, 28850];

// 计算价格上下限(取1000的倍数)
var minPrice = Math.floor(Math.min(...prices)/1000)*1000;
var maxPrice = Math.ceil(Math.max(...prices)/1000)*1000;

// 图表基础配置
const baseY = 50.5;       // 第一条水平线的Y坐标
const lineGap = 30;       // 水平线间距
const totalLines = 5;     // 水平线总数
const priceRange = maxPrice - minPrice;
const priceGap = priceRange / (totalLines - 1); // 每条线对应的价格差

function addText(x, y, t) {
  var text = document.createElementNS('http://www.w3.org/2000/svg','text');
  
  text.setAttribute('x', x);     
  text.setAttribute('y', y); 
  text.setAttribute('fill', 'black');
  text.setAttribute('font-size', '14px');

  var textNode = document.createTextNode(t);
  text.appendChild(textNode);
  
  document.getElementById('main').appendChild(text);
}

function addLine(id, x1, y1, x2, y2, sc, sw) {
  const line = document.createElementNS('http://www.w3.org/2000/svg','line');
  
  line.setAttribute('id', id);
  line.setAttribute('x1', x1);
  line.setAttribute('y1', y1);
  line.setAttribute('x2', x2);
  line.setAttribute('y2', y2);
  line.setAttribute('stroke', sc);
  line.setAttribute('stroke-width', sw);

  document.getElementById("main").appendChild(line);
}

// 绘制价格刻度文本
function addHorizontalLinesPrice() {
  for (let i = 0; i < totalLines; i++) {
    const currentPrice = maxPrice - i * priceGap;
    const y = baseY + i * lineGap + 5; // 文本在水平线下方5px处
    addText(20, y, currentPrice);
  }
}

// 绘制水平线
function addHorizontalLines() {
  for (let i = 0; i < totalLines; i++) {
    const y = baseY + i * lineGap;
    addLine(`horizontalLine${i+1}`, 90, y, window.innerWidth - 20, y, "black", 0.5);
  }
}

// 计算价格对应的Y坐标
function getYPosition(price) {
  const ratio = (maxPrice - price) / priceRange;
  return baseY + ratio * (totalLines - 1) * lineGap;
}

// 绘制每个价格对应的绿色线条
function drawPriceLines() {
  let x = 100; // 起始X坐标(避开左侧刻度)
  const lineLength = 80; // 单条绿线长度
  prices.forEach(price => {
    const yPos = getYPosition(price);
    addLine(`priceLine${price}`, x, yPos, x + lineLength, yPos, "green", 3);
    x += 50; // 绿线之间的间距
  });
}

// 执行所有绘制逻辑
addHorizontalLinesPrice();
addHorizontalLines();
drawPriceLines();

修正说明

  • 调整Y坐标计算逻辑,确保价格最大值对应第一条水平线,最小值对应最后一条水平线,完全匹配视觉预期。
  • 将X值移到循环外部,每条绿线依次排列,不再重叠。
  • 用循环替代硬编码绘制刻度和水平线,价格范围变化时无需手动修改数值,提升维护性。
  • 增加字体大小、线长等可配置参数,方便快速调整图表样式。

内容的提问来源于stack exchange,提问作者JCss

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 11:42:06