티스토리 뷰

HTML에서의 속성과 CSS의 스타일

D3에서 가장 중요한 특징 중 하나는 HTML에서의 attr와 CSS의 style을 동시에 사용할 수 있다는 점이다.

사용 문법은 다음과 같다.

.attr("속성의 이름", "값");
.style("프로퍼티", "값");

지난 시간 예제를 attr와 style을 이용해 꾸며보자.

const provinces = ['AB', 'BC', 'MB', 'NB', 'NL', 'NT', 'NS', 'NU', 'ON', 'PE', 'QC', 'SK', 'YT'];

// Append paragraphs and highlight one element
let p = d3.select('body').selectAll('p')
    .data(provinces)
    .enter()
  .append('p')
    .text(d => d)
    .attr('class', 'custom-paragraph')
    .style('font-weight', 'bold')
    .style('color', d => {
      if(d == 'BC')
        return 'blue';
      else
        return 'red';
    });

text 아래로 코드를 살펴보자. 먼저 (뭔지는 모르겠지만) CSS의 클래스인 'custom-paragraph'를 입히고, 스타일로 폰트를 굵게, 그리고 색은 조건문을 이용하여 빨강과 파랑으로 분기해 주었다. 해당 코드의 결과는 다음과 같다.

 

Add SVG rectangles and set properties

 

paragraph에 이어서 svg도 d3를 이용할 수 있다.

const numericData = [1, 2, 4, 8, 16]

const svg = d3.select('body').append('svg')
    .attr('width', 300)
    .attr('height', 50);

svg.selectAll('circle')
    .data(numericData)
    .enter()
    .append('circle')
    .attr('fill', 'red')
    .attr('cx', (d, i) => i * 60 + 25)
    .attr('cy', 25)
    .attr('r', 25)

다음 코드를 살펴보자. 앞선 예제에서는 append가 아니라 모든 p를 선택하는 selectAll 메서드를 썼지만 이번에는 append를 쓴다. 왜냐? HTML 코드에 p가 선언되고 main.js와 연동되어 있는 전 예제와는 달리 해당 예제에서는 HTML 코드에 svg를 선언해주지 않았기 때문(마찬가지로 HTML 코드에 svg를 선언해주면, 당연히 select 메서드를 사용해도 됨).

 

HTML 파일 내에 SVG 코드를 작성하는 것과 구조는 비슷하다. 먼저 body를 선택 후, svg를 선언하고 그림이 표현될 크기를 정해준다. 이어서 svg를 조작해 주면 되는데 전 예제에서와 구조는 마찬가지다. 그리고 싶은 도형과 크기를 지정해 주면 된다. 원의 간격을 일정하게 유지하기 위해서 필요한 건 numericData의 요소가 아니라 인덱스이다. 따라서 cx의 간격을 조절할 때에는 d가 아닌 i를 이용하였다. 해당 코드를 실행하면 다음과 같은 그림이 그려진다.

 

practice 1

 

지금까지 배운 개념들을 이용해서 아래와 같은 그림을 표현해보자.

데이터가 주어질 것이다(sandwiches).

 

1. 데이터 중 size가 large인 것은 반지름이 20, 아니면 10이다.

2. 가격이 7달러 아래면 녹색, 아니면 노란색이며 검은색 테두리가 적용되어 있다.

3. 각 원의 중심사이의 거리는 50이며, 맨 왼쪽 원의 중심의 좌표는 (30, 30)이다.

const sandwiches = [
    { name: "Thesis", price: 7.95, size: "large" },
    { name: "Dissertation", price: 8.95, size: "large" },
    { name: "Highlander", price: 6.50, size: "small" },
    { name: "Just Tuna", price: 6.50, size: "small" },
    { name: "So-La", price: 7.95, size: "large" },
    { name: "Special", price: 12.50, size: "small" }
];

const svg = d3.select('body').append('svg')
    .attr('width', 310)
    .attr('height', 60)

svg.selectAll('circle')
    .data(sandwiches)
    .enter()
    .append('circle')
    .attr('cx', (d, i) => i * 50 + 30)
    .attr('cy', 30)
    .attr('r', d => {
        if (d.size == 'large') {
            return 20;
        } else {
            return 10;
        }
    })
    .attr('fill',d => {
        if (d.price < 7)    return "green";
        else    return "yellow";
    })
    .attr('stroke', 'black')

 

practice 2 (with data mapping)

이번 예제는 주어진 데이터로 2차원 좌표 위에 도시 별 인구수를 표시하는 것이다. 위 예제와 비슷하지만, 외부에서 csv file을 가져와 데이터 가공을 해준 뒤 표현하는 방법이 추가로 요구된다.

 

1. 데이터를 매핑해 준다. 모든 csv의 요소는 텍스트이므로, 데이터의 키 중 x, y 및 인구수는 '+'를 붙여 정수형으로 변환해 준다.

2. 해당 예제는 유럽 도시의 인구 수만을 나타내고 싶어 한다. 필터링을 통해 유럽의 도시만 가져온다.

3. 화면 좌측 상단에는 도시의 개수를 나타내준다.

4. 인구수의 크기가 백만 명이 넘으면 반지름의 길이가 8, 아니면 4이다.

5. 모든 원은 갈색이다.

6. 모든 원 위에 도시 이름을 작성하는데, 글씨는 원 위로 15px 떨어져 있으며 원의 중심과 이름의 중심을 일치시킨다.

7. 도시 이름의 크기는 11px이며, 인구수가 백만 이상인 도시에만 이름을 표시해 준다.

const populatons = d3.csv('data/cities_and_population.csv')
    .then(data => {
        const populationData = data.map(d => {
            return {
                country: d.country,
                city: d.city,
                population: +d.population,
                x: +d.x,
                y: +d.y,
                eu: d.eu
            }
        }).filter(d => { return (d.eu == "true"); })

        const svg = d3.select('body').append('svg')
            .attr('width', 700)
            .attr('height', 550)
        
        svg.append('text')
            .attr('x', 10)
            .attr('y', 20)
            .text(`Number of cities: ${populationData.length}`)

        svg.selectAll('circle')
            .data(populationData)
            .enter()
            .append('circle')
            .attr('cx', d => d.x)
            .attr('cy', d => d.y)
            .attr('r', d => {
                if (d.population < 1000000) return 4;
                else    return 8;
            })
            .attr('fill', 'brown')
        
        svg.selectAll('text')
            .data(populationData)
            .enter()
            .append('text')
            .attr('x', d => d.x)
            .attr('y', d => d.y - 15)
            .attr('class', 'city-label')
            .attr('text-anchor', 'middle')
            .attr('font-size', 11)
            .attr('opacity', d => {
                if (d.population < 1000000) return 0;
            })
            .text(d => d.city)
            
    })
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함