Barchart

image.png

Json Data

barchart.js

const svg = d3
  .select(".canvas")
  .append("svg")
  .attr("width", 600)
  .attr("height", 600);

// 차트에 여백 만들기
const margin = { top: 20, right: 20, bottom: 100, left: 100 };
const graphWidth = 600 - margin.left - margin.right;
const graphHeight = 600 - margin.top - margin.bottom;

const graph = svg
  .append("g")
  .attr("width", graphWidth)
  .attr("height", graphHeight)
  .attr("transform", `translate(${margin.left}, ${margin.top})`);

const xAxisGroup = graph
  .append("g")
  .attr("transform", `translate(0, ${graphHeight})`); // x축 아래로 translate
const yAxisGroup = graph.append("g");

// Data 적용해서 차트 만들기
d3.json("../menu.json").then(data => {
  const rects = graph.selectAll("rect").data(data);

  const y = d3
    .scaleLinear() // 한계치 설정
    .domain([0, d3.max(data, d => d.orders)])
    .range([graphHeight, 0]);

  const min = d3.min(data, d => d.orders); // 가장 작은 수 반환
  const max = d3.max(data, d => d.orders); // 가장 큰 수 반환
  const extent = d3.extent(data, d => d.orders); // [min, max] 반환

  const x = d3
    .scaleBand()
    .domain(data.map(item => item.name)) // data 각각의 이름 설정
    .range([0, 500])
    .paddingInner(0.2) // 0.2 padding
    .paddingOuter(0.2);

  rects
    .attr("width", x.bandwidth)
    .attr("height", d => graphHeight - y(d.orders)) // data의 orders 값 적용
    .attr("fill", "orange")
    .attr("x", d => x(d.name)) // data index 값 * 70
    .attr("y", d => y(d.orders));

  // 반환되지 못한 나머지 data 가상 DOM으로 생성
  rects
    .enter()
    .append("rect")
    .attr("width", x.bandwidth)
    .attr("height", d => graphHeight - y(d.orders))
    .attr("fill", "orange")
    .attr("x", d => x(d.name))
    .attr("y", d => y(d.orders)); // 위에 있는 그래프 뒤집기

  // x축 y축 (axis) 생성
  const xAxis = d3.axisBottom(x);
  const yAxis = d3
    .axisLeft(y)
    .ticks(3) //ticks 는 y축 눈금 갯수
    .tickFormat(d => d + " orders"); // 눈금 값 설정

  // 축 적용
  xAxisGroup.call(xAxis);
  yAxisGroup.call(yAxis);

  xAxisGroup
    .selectAll("text") // x축 눈금 값. text 선택
    .attr("transform", "rotate(-40)")
    .attr("text-anchor", "end")
    .attr("fill", "orange");
});

차트에 적용한 Data
menu.json

[{
        "name": "veg soup",
        "orders": 200
    },
    {
        "name": "veg curry",
        "orders": 600
    },
    {
        "name": "veg pasta",
        "orders": 300
    },
    {
        "name": "veg surprise",
        "orders": 900
    },
    {
        "name": "veg burger",
        "orders": 1500
    }
]

image.png

Firebase Data

Json data를 사용해서 만들었던 Barchart를 Firebase db에 연동해서 같은 차트를 만들어 보자.

image.png

Firebase에 데이터베이스를 생성해주고 스크립트를 연결해준다.

index.html

<script src="https://www.gstatic.com/firebasejs/7.2.0/firebase-app.js"></script>
  <script src="https://www.gstatic.com/firebasejs/7.2.0/firebase-firestore.js"></script>
  <script src="https://www.gstatic.com/firebasejs/7.2.0/firebase-analytics.js"></script>
  <script>
    // Your web app's Firebase configuration
    var firebaseConfig = {
      apiKey: //
      authDomain: //
      databaseURL: //
      projectId: //
      storageBucket: //
      messagingSenderId: //
      appId: //
      measurementId://
    };
    // Initialize Firebase
    firebase.initializeApp(firebaseConfig);
    firebase.analytics();
    const db = firebase.firestore();    // data베이스 연결
  </script>

데이터를 연결하는 부분만 수정해주면 된다.

barchart.js

// Firebase Data 적용해서 차트 만들기
db.collection("dishes")
  .get()
  .then(res => {
    let data = [];
    res.docs.forEach(doc => {
      data.push(doc.data());
    });

    console.log(data); // firebase data 배열

모듈화

차트를 만드는 함수를 데이터에 따라 재사용할 수 있게 모듈화 시킨다.

// ***** update function 모듈화 하기 ******
const update = data => {
  // updating scale domains
  y.domain([0, d3.max(data, d => d.orders)]);
  x.domain(data.map(item => item.name));

  // data 와 rects 연결
  const rects = graph.selectAll("rect").data(data);

  // remove exit selection
  rects.exit().remove();

  // update current shape in hte DOM
  rects
    .attr("width", x.bandwidth)
    .attr("height", d => graphHeight - y(d.orders)) // data의 orders 값 적용
    .attr("fill", "orange")
    .attr("x", d => x(d.name)) // data index 값 * 70
    .attr("y", d => y(d.orders));

  // 반환되지 못한 나머지 data 가상 DOM으로 생성
  rects
    .enter()
    .append("rect")
    .attr("width", x.bandwidth)
    .attr("height", d => graphHeight - y(d.orders))
    .attr("fill", "orange")
    .attr("x", d => x(d.name))
    .attr("y", d => y(d.orders)); // 위에 있는 그래프 뒤집기

  // 축 적용
  xAxisGroup.call(xAxis);
  yAxisGroup.call(yAxis);
};

// Firebase Data 적용해서 차트 만들기
db.collection("dishes")
  .get()
  .then(res => {
    let data = [];
    res.docs.forEach(doc => {
      data.push(doc.data());
    });
    console.log(data); // firebase data 배열

    update(data); // 한번 호출로 차트 생성

실시간 업데이트

onSnapshot() 메서드로 문서를 수신 대기할 수 있습니다. 사용자가 제공하는 콜백이 최초로 호출될 때는 단일 문서의 현재 내용으로 문서 스냅샷이 즉시 생성됩니다. 그런 다음 내용이 변경될 때마다 콜백이 호출되어 문서 스냅샷을 업데이트합니다.

data type에 따라 실시간으로 상태를 업데이트한다.

  • added - 데이터 추가
  • modified - 데이터 수정
  • removed - 데이터 삭제

let data = [];

// 실시간 업데이트 함수
db.collection("dishes").onSnapshot(res => {
  res.docChanges().forEach(change => {
    const doc = { ...change.doc.data(), id: change.doc.id };

    switch (change.type) {
      case "added":
        data.push(doc);
        break;
      case "modified":
        const index = data.findIndex(item => item.id == doc.id);
        data[index] = doc;
        break;
      case "removed":
        data = data.filter(item => item.id !== doc.id);
        break;
      default:
        break;
    }
  });

   updatae(data);

Transition

차트가 아래에서 위로 올라오는 transition 효과

rects
    .enter()
    .append("rect")
    .attr("width", x.bandwidth)
    .attr("height", d => 0)
    .attr("fill", "orange")
    .attr("x", d => x(d.name))
    .attr("y", graphHeight)
    .transition() // transition 효과 주기
    .duration(500)
    .attr("y", d => y(d.orders)) // 위에 있는 그래프 뒤집기
    .attr("height", d => graphHeight - y(d.orders));