Line chart

선 차트

Plots a value at successive points in time and connects them — the chart for "how has this changed."

Also known as: Line graph꺾은선 그래프
···
html
<div class="viz">
  <svg id="svg"></svg>
</div>
css
body{display:block}
.viz{position:relative;width:100%;height:100%}
svg{display:block;width:100%;height:100%;overflow:visible}
.axis{stroke:var(--line);stroke-width:1}
.tick{fill:var(--muted);font-weight:600}
.val{fill:var(--fg);font-weight:700}
.minor{display:none}
@media (min-width:460px){.minor{display:inline}}
.ln{fill:none;stroke:var(--accent);stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round}
.dot{fill:var(--accent)}
.ring{fill:var(--surface)}
js
var svg = document.getElementById('svg');
var NS = 'http://www.w3.org/2000/svg';
function se(t){ return document.createElementNS(NS,t); }
function sa(e,o){ for (var k in o) e.setAttribute(k,o[k]); }
function R(a,b){ return a + Math.random()*(b-a); }

var N = 9;
var cur = new Array(N).fill(50);
var W=300, H=200;

function measure(){
  var r = svg.getBoundingClientRect();
  W = Math.max(r.width,10); H = Math.max(r.height,10);
  sa(svg,{ viewBox: '0 0 ' + W + ' ' + H });
}

function draw(){
  svg.innerHTML = '';
  var padL=26, padR=14, padT=16, padB=20;
  var pw=W-padL-padR, ph=H-padT-padB;
  var fs = Math.max(8, Math.min(12, W/85));
  var pts = cur.map(function(v,i){
    return { x: padL + (pw*i)/(N-1), y: padT + ph - (v/100)*ph };
  });

  [0,100].forEach(function(v){
    var y = padT + ph - (v/100)*ph;
    var g = se('line');
    sa(g,{x1:padL,y1:y,x2:W-padR,y2:y,class:'axis' + (v===100?' minor':'')});
    svg.appendChild(g);
    var t = se('text');
    sa(t,{x:padL-5,y:y+3,'text-anchor':'end',class:'tick minor','font-size':fs-1});
    t.textContent = v;
    svg.appendChild(t);
  });

  var d = pts.map(function(p,i){ return (i===0?'M':'L') + p.x.toFixed(1) + ',' + p.y.toFixed(1); }).join(' ');
  var path = se('path');
  sa(path,{d:d,class:'ln'});
  svg.appendChild(path);

  pts.forEach(function(p,i){
    if (i%2===0 || i===pts.length-1){
      var ring = se('circle');
      sa(ring,{cx:p.x,cy:p.y,r:4.5,class:'ring minor'});
      svg.appendChild(ring);
      var dot = se('circle');
      sa(dot,{cx:p.x,cy:p.y,r:2.6,class:'dot minor'});
      svg.appendChild(dot);
    }
  });

  var last = pts[pts.length-1];
  var ring2 = se('circle');
  sa(ring2,{cx:last.x,cy:last.y,r:5,class:'ring'});
  svg.appendChild(ring2);
  var dot2 = se('circle');
  sa(dot2,{cx:last.x,cy:last.y,r:3.2,class:'dot'});
  svg.appendChild(dot2);
  var lab = se('text');
  sa(lab,{x:last.x-6,y:last.y-8,'text-anchor':'end',class:'val','font-size':fs});
  lab.textContent = Math.round(cur[cur.length-1]);
  svg.appendChild(lab);
}

function animate(target){
  var from = cur.slice();
  var t0 = performance.now();
  function step(t){
    var p = Math.min(1,(t-t0)/750);
    var e = 1-Math.pow(1-p,3);
    cur = from.map(function(v,i){ return v+(target[i]-v)*e; });
    draw();
    if (p<1) requestAnimationFrame(step);
  }
  requestAnimationFrame(step);
}

function nextTarget(){
  var v = 50, out = [];
  for (var i=0;i<N;i++){ v = Math.max(10,Math.min(92, v + R(-22,22))); out.push(v); }
  return out;
}

new ResizeObserver(function(){ measure(); draw(); }).observe(svg);
measure();
draw();
animate(nextTarget());
setInterval(function(){ animate(nextTarget()); }, 3600);

If bars are about magnitude, lines are about trend. Slope encodes rate of change, so stretching or squashing the aspect ratio can make the same data look gentle or dramatic (the "banking to 45°" convention keeps the average slope near 45° to avoid that).

Keep the stroke thin (~2px) and label the endpoint directly — a number on every point goes unread. Past 3-4 series, lines start crossing into spaghetti; highlight the one series that matters and gray out the rest, or split into small multiples.

Unlike bars, a line chart doesn't have to start its y-axis at zero — the shape of change matters more than the absolute value. But when you do that, say so explicitly near the axis.

When to use

Use it for continuous change over time. For comparing discrete categories, a bar chart reads more precisely.