D3 및 React를 통한 농구 데이터 확보

73001 단어 reactd3datajavascript

2018-19시즌 로스앤젤레스 로스앤젤레스 로스앤젤레스의 득점 총수를 가시화하기 위해 도넛 튀김 그림을 제작한다.

데이터


우리가 데이터 시각화를 만들어야 하는 첫 번째 일은 데이터이다. 이것은 우연이 아니다.This well written article는 인터넷이 캡처한 일부 법률과 도덕적 결과를 설명했다.This repository 무료 공공 데이터 링크 제공.Dev.to 자체에는 데이터, 웹 캡처기, 시각화에 관한 많은 글이 있습니다.나의 2분의 1은 간단한 데이터 시각화 프로젝트에 좋은 오래된 크롬 개발 도구 자체가 데이터를 수집하고 만들 수 있다는 것이다.이 지나치게 간소화된 예를 봐라.
이름
나이.
르브라운 제임스
34
시안 윌리엄슨
십팔
조던
56
위 표에서 데이터를 마사지하려면 다음과 같이 하십시오.
  • Chrome 개발 도구 열기
  • 모든 행 격리
  • 결과를 노드 목록에서 수조로 변환하고 제목 줄을 제거합니다
  • 각 테이블 데이터 단원에서 텍스트를 추출하고 결과를 새로운 대상 그룹에 비추기
  • c(변수 이름)을 입력하고 Enter를 누르면 새 그룹이 콘솔
  • 에 표시됩니다.
  • 패턴을 마우스 오른쪽 버튼으로 클릭하고 Store as Global Variable 를 선택합니다.콘솔에서 볼 수 있습니다 temp1.
  • 내장 copy 함수를 사용하여 임시 변수를 클립보드에 복사 - copy(temp1)
  • 데이터를 JavaScript 또는 JSON 파일에 붙여넣습니다.
  • 🤯
  • var a = document.querySelectorAll('tr') // 2
    var b = Array.from(a).slice(1) // 3
    var c = b.map(el => {
      // 4
      var name = el.children[0].innerText
      var age = el.children[1].innerText
      return { name, age }
    })
    
    c // 5
    // right click array
    copy(temp1) // 7
    
    주의, 각 장면은 모두 다르다. 이 과정을 설명하는 데 도움을 주기 위해 우리는 이 예시를 간소화했다.그 밖에 상술한 모든 논리는 하나의 함수에 놓아 절차를 간소화할 수 있다.컨트롤러에 여러 줄 함수를 만들어서 새 줄을 만들 수 있다는 것을 기억하십시오.이 방법을 사용하면 JavaScript 101을 사용하여 웹을 수동으로 캡처할 수 있습니다.Shift+Enter 가지 말아야 할 곳에 가서 데이터를 수집하기 전에 반드시 사이트의 서비스 조항을 읽어야 한다.

    라운드 맵 생성하기


    D3와 React를 함께 작업하는 것은 사실 결코 복잡하지 않다.일반적으로 필요한 것은 DOM의 입구점과 페이지를 불러올 때 시각화된 논리를 초기화하는 것입니다.우리의 예시 프로젝트를 시작하기 위해서, 우리는 설치를 희망한다willy-nilly.첫 번째 단계는 새 프로젝트를 만드는 것입니다.내가 좋아하는 첫 번째 일은 목록을 지우고 create-react-appsrc만 남기는 것이다.낡은 App.js 문장을 삭제하는 것을 잊지 마세요.어떤 코드를 작성하기 전에, 우리는 몇 개의 의존항을 찾아야 한다.
    1 - D3 및 스타일 구성 요소를 다운로드합니다.
    npm i d3 styled-components
    
    2 - index.js 디렉토리에 새 파일 import, 심지어는 whatever-you-want.js 을 생성합니다.예제에 사용된 데이터는 사용 가능합니다in this gist.
    3 - D3+React+ 스타일 어셈블리라고도 하는 다양한 구성에 사용할 수 있는 기본 템플릿을 작성합니다.대부분의 개발자들처럼 나는 자신의 괴벽과 패턴을 가지고 있다.예를 들어 나는 data.js 검은색에 시달렸다. 그래서 나는 src, 나는 글씨체#000000 등을 좋아한다. 만약 네가 이전에 갈고리를 사용한 적이 없다면, 빈 #333333 의존 수조의 Raleway 갈고리는 React 클래스 구성 요소의 useEffect와 유사하다.번호의 주석은 곧 다가올 단계에 대응하고 이 단계의 코드를 삽입하는 곳이다.
    import React, { useRef, useEffect, useState } from 'react'
    import * as d3 from 'd3'
    import styled, { createGlobalStyle } from 'styled-components'
    import data from './data'
    
    const width = 1000
    const height = 600
    const black = '#333333'
    const title = 'My Data Visualization'
    
    // 4
    
    // 7
    
    export const GlobalStyle = createGlobalStyle`
    @import url('https://fonts.googleapis.com/css?family=Raleway:400,600&display=swap');
    
    body {
      font-family: 'Raleway', Arial, Helvetica, sans-serif;
      color: ${black};
      padding: 0;
      margin: 0;
    }
    `
    
    export const Container = styled.div`
      display: grid;
      grid-template-rows: 30px 1fr;
      align-items: center;
      .title {
        font-size: 25px;
        font-weight: 600;
        padding-left: 20px;
      }
    `
    
    export const Visualization = styled.div`
      justify-self: center;
      width: ${width}px;
      height: ${height}px;
    // 6
    `
    
    export default () => {
      const visualization = useRef(null)
    
      useEffect(() => {
        var svg = d3
          .select(visualization.current)
          .append('svg')
          .attr('width', width)
          .attr('height', height)
    // 5
    
    // 8
      }, [])
    
      return (
        <>
          <GlobalStyle/>
          <Container>
            <div className='title'>{title}</div>
            <Visualization ref={visualization} />
            {/*10*/}
          </Container>
        <>
      )
    }
    
    4. 우리는 우리의 튀김 도넛 그림을 위해 배색 방안과 차원을 세워야 한다.
    우리 과자의 반경.
    const radius = Math.min(width, height) / 2
    
    로스앤젤레스의 색깔 테마를 사용해야만 의미가 있다.
    var lakersColors = d3
      .scaleLinear()
      .domain([0, 1, 2, 3])
      .range(['#7E1DAF', '#C08BDA', '#FEEBBD', '#FDBB21'])
    
    D3[] 함수는 데이터를 케이크 슬라이스에 반영합니다.백그라운드에 componentDidMountpie 등의 필드를 추가함으로써 이를 실현합니다.우리는 카드를 씻고 슬라이스하는 순서에 선택할 수 있는 startAngle 함수를 사용하고 있다.이것을 가지고 놀면 그것을 전달하고endAngle, 심지어는 그것을 밖에 두어 다른 안배를 얻는다.마지막으로, 우리는 sort 함수를 사용하여 D3에 null 속성 분할 케이크 그림을 사용하도록 알려 줍니다.value 변수를 콘솔에 기록하여 D3 pie 함수가 데이터에 미치는 작용을 개념화하는 데 도움을 줍니다.
    var pie = d3
      .pie()
      .sort((a, b) => {
        return a.name.length - b.name.length
      })
      .value(d => d.points)(data)
    
    현재 우리는 points 함수를 사용하여 원형 레이아웃을 만들어야 한다.변수pie는 우리의 도넛 튀김 그림에 사용되고, arc는 잠시 후에 라벨 안내서로 사용됩니다.arc는 보조 함수이며 잠시 후에도 사용됩니다.
    var arc = d3
      .arc()
      .outerRadius(radius * 0.7)
      .innerRadius(radius * 0.4)
    
    var outerArc = d3
      .arc()
      .outerRadius(radius * 0.9)
      .innerRadius(radius * 0.9)
    
    function getMidAngle(d) {
      return d.startAngle + (d.endAngle - d.startAngle) / 2
    }
    
    5 구조가 자리를 잡으면 거의 화면에서 뭔가를 볼 수 있다.
    다음 내용을 원본outerArc 변수 성명에 연결합니다.
       .append('g')
       .attr('transform', `translate(${width / 2}, ${height / 2})`)
    
    지금 우리가 getMidAngle D3에 피드백을 할 때 마술이 발생한다.
    svg
      .selectAll('slices')
      .data(pie)
      .enter()
      .append('path')
      .attr('d', arc)
      .attr('fill', (d, i) => lakersColors(i % 4))
      .attr('stroke', black)
      .attr('stroke-width', 1)
    
    다음으로, 우리는 모든 절편에서 최종적으로 라벨을 가리키는 선을 그려야 한다.이름이 좋은 svg 함수는 pie 좌표를 가진 수조를 되돌려줍니다. 이 수조는 centroid 절편 (이 예에서 [x,y] 의 중심점에 있습니다.마지막으로, 우리는 세 개의 좌표 진열로 구성된 수조를 되돌릴 것이다. 이 좌표 진열은 현재 화면에 표시된 각 선의 원점, 구부러진 점과 끝점에 대응한다.pie 회선의 끝부분을 가리키는 방향을 정하는 데 도움이 된다.
    svg
      .selectAll('lines')
      .data(pie)
      .enter()
      .append('polyline')
      .attr('stroke', black)
      .attr('stroke-width', 1)
      .style('fill', 'none')
      .attr('points', d => {
        var posA = arc.centroid(d)
        var posB = outerArc.centroid(d)
        var posC = outerArc.centroid(d)
        var midAngle = getMidAngle(d)
        posC[0] = radius * 0.95 * (midAngle < Math.PI ? 1 : -1)
        return [posA, posB, posC]
      })
    
    지금 우리의 생산 라인은 라벨을 붙일 준비가 되었다.라벨이 도표의 어느 쪽에 나타나느냐에 따라 뒤집기darc의 순서를 통해 대칭성을 높이면 라벨이 더 좋아 보인다.midAngle 함수는 원본 namepoints 이라는 키로 이동합니다.pie 객체의 최상위 키는 data 기능에 사용되는 각도 측정 값을 포함합니다.
    svg
      .selectAll('labels')
      .data(pie)
      .enter()
      .append('text')
      .text(d => {
        var midAngle = getMidAngle(d)
        return midAngle < Math.PI
          ? `${d.data.name} - ${d.data.points}`
          : `${d.data.points} - ${d.data.name}`
      })
      .attr('class', 'label')
      .attr('transform', d => {
        var pos = outerArc.centroid(d)
        var midAngle = getMidAngle(d)
        pos[0] = radius * 0.99 * (midAngle < Math.PI ? 1 : -1)
        return `translate(${pos})`
      })
      .style('text-anchor', d => {
        var midAngle = getMidAngle(d)
        return midAngle < Math.PI ? 'start' : 'end'
      })
    
    6 - 어떤 스타일로 우리의 라벨을 수식하기 위해서, 우리는 data 스타일의 구성 요소에 몇 줄의 코드를 추가하기만 하면 된다.D3를 사용하여 Reactpie 갈고리에 getMidAngle 속성을 추가한 다음 스타일화된 구성 요소를 사용하여 이 클래스를 정의합니다. 통합 라이브러리의 체크 상자를 선택한 것 같습니다.
    .label {
      font-size: 12px;
      font-weight: 600;
    }
    
    7 - 우리는 보기에는 괜찮은데 왜 맛을 조금 늘리지 않고 사용자에게 상호작용하는 느낌을 주지 않는가.우리는 D3의 Visualization 함수를 사용하여 득점 총수를 신속하게 얻을 수 있다.
    var total = d3.sum(data, d => d.points)
    
    class 함수는 useEffect 노드에 간단하게 추가되어 우리의 총수를 표시합니다.sumshowTotalstyle 속성은 텍스트를 도넛 구멍의 중심에 두어야 한다.text 기능은 잠시 후에 발휘될 것이다.페이지가 불러올 때 텍스트를 표시할 수 있도록 text-anchor 함수를 호출하고 있습니다.
    function showTotal() {
      svg
        .append('text')
        .text(`Total: ${total}`)
        .attr('class', 'total')
        .style('text-anchor', 'middle')
    }
    
    function hideTotal() {
      svg.selectAll('.total').remove()
    }
    
    showTotal()
    
    우리는 단계 6의 middle 클래스 옆에 hideTotal 클래스를 추가해야 한다.
    .total {
      font-size: 20px;
      font-weight: 600;
    }
    
    9. 번호 평론 시스템은 이 점에서 좀 거칠지만, 만약 당신이 이 점을 해냈다면, 당신은 충분히 똑똑해서 따라갈 수 있습니다.다음은 이 함수showTotal입니다.이것들은 우리가 모든 부분에 응용할 탐지기다.
    function onMouseOver(d, i) {
      hideTotal()
      setPlayer(d.data)
      d3.select(this)
        .attr('fill', d3.rgb(lakersColors(i % 4)).brighter(0.5))
        .attr('stroke-width', 2)
        .attr('transform', 'scale(1.1)')
    }
    
    function onMouseOut(d, i) {
      setPlayer(null)
      showTotal()
      d3.select(this)
        .attr('fill', lakersColors(i % 4))
        .attr('stroke-width', 1)
        .attr('transform', 'scale(1)')
    }
    
    슬라이스가 멈추면 필획과 채우기가 강조되고, 살짝 확대하면 시원한 효과가 추가됩니다.구멍에 더 많은 정보가 있는 툴팁을 붙여넣을 수 있도록 전체 텍스트도 전환됩니다.우선 total 블록을 만들어야 합니다. 만약 그것이 없다면, React 응용 프로그램은 어떻게 될까요?
    const [player, setPlayer] = useState(null)
    
    예민한 관찰자들은 label의 인용에 주의를 기울이고 무슨 일이 일어났는지 알고 싶어 할지도 모른다.다음 탐지기는 hideTotal D3 체인의 끝에 연결되어야 합니다.
       .attr('class', 'slice')
       .on('mouseover', onMouseOver)
       .on('mouseout', onMouseOut)
    
    우리는 state 클래스에서 this 를 사용하기 때문에, slices 스타일 구성 요소의 다른 쌍선을 통해 그것을 제어할 수 있습니다.
    .slice {
      transition: transform 0.5s ease-in;
    }
    
    10 - 마우스가 각 슬라이스에 멈추면서 나타나는 transform 상태를 표시하기 위한 툴팁을 만들 수 있습니다.
    {
      player ? (
        <Tooltip>
          <div>
            <span className='label'>Name: </span>
            <span>{player.name}</span>
            <br />
            <span className='label'>Points: </span>
            <span>{player.points}</span>
            <br />
            <span className='label'>Percent: </span>
            <span>{Math.round((player.points / total) * 1000) / 10}%</span>
          </div>
        </Tooltip>
      ) : null
    }
    
    새로운 정보에 있어서 사용자는 현재 선수가 득점한 팀의 점수 백분율만 얻는다.그러나 집중된 위치와 동작을 결합시켜 좋은 효과와 좋은 상호작용 감각을 창조했다.만약 더 많은 정보를 표시할 수 있거나, 내가 더 똑똑하다면, 유사한 패턴을 더욱 효과적으로 사용할 수 있을 것이다.마지막으로 필요한 것은 slice 구성 요소입니다. 다른 스타일의 구성 요소와 함께 사용됩니다.
    export const Tooltip = styled.div`
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      width: ${radius * 0.7}px;
      height: ${radius * 0.7}px;
      display: grid;
      align-items: center;
      justify-items: center;
      border-radius: 50%;
      margin-top: 10px;
      font-size: 12px;
      background: #ffffff;
      .label {
        font-weight: 600;
      }
    `
    
    에이, 우리의 최종 코드는 아래와 같아야 한다.
    import React, { useRef, useEffect, useState } from 'react'
    import * as d3 from 'd3'
    import data from './data'
    import styled, { createGlobalStyle } from 'styled-components'
    
    /**
     * Constants
     */
    const width = 1000
    const height = 600
    const radius = Math.min(width, height) / 2
    const black = '#333333'
    const title = 'Los Angeles Lakers Scoring 2018-19'
    
    /**
     * D3 Helpers
     */
    
    // total points
    var total = d3.sum(data, d => d.points)
    
    // lakers colors
    var lakersColors = d3
      .scaleLinear()
      .domain([0, 1, 2, 3])
      .range(['#7E1DAF', '#C08BDA', '#FEEBBD', '#FDBB21'])
    
    // pie transformation
    var pie = d3
      .pie()
      .sort((a, b) => {
        return a.name.length - b.name.length
      })
      .value(d => d.points)(data)
    
    // inner arc used for pie chart
    var arc = d3
      .arc()
      .outerRadius(radius * 0.7)
      .innerRadius(radius * 0.4)
    
    // outer arc used for labels
    var outerArc = d3
      .arc()
      .outerRadius(radius * 0.9)
      .innerRadius(radius * 0.9)
    
    // midAngle helper function
    function getMidAngle(d) {
      return d.startAngle + (d.endAngle - d.startAngle) / 2
    }
    /**
     * Global Style Sheet
     */
    export const GlobalStyle = createGlobalStyle`
    @import url('https://fonts.googleapis.com/css?family=Raleway:400,600&display=swap');
    
    body {
      font-family: 'Raleway', Arial, Helvetica, sans-serif;
      color: ${black};
      padding: 0;
      margin: 0;
    }
    `
    
    /**
     * Styled Components
     */
    export const Container = styled.div`
      display: grid;
      grid-template-rows: 30px 1fr;
      align-items: center;
      user-select: none;
      .title {
        font-size: 25px;
        font-weight: 600;
        padding-left: 20px;
      }
    `
    
    export const Visualization = styled.div`
      justify-self: center;
      width: ${width}px;
      height: ${height}px;
      .slice {
        transition: transform 0.5s ease-in;
      }
      .label {
        font-size: 12px;
        font-weight: 600;
      }
      .total {
        font-size: 20px;
        font-weight: 600;
      }
    `
    
    export const Tooltip = styled.div`
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      width: ${radius * 0.7}px;
      height: ${radius * 0.7}px;
      display: grid;
      align-items: center;
      justify-items: center;
      border-radius: 50%;
      margin-top: 10px;
      font-size: 12px;
      background: #ffffff;
      .label {
        font-weight: 600;
      }
    `
    
    export default () => {
      const [player, setPlayer] = useState(null)
    
      const visualization = useRef(null)
    
      useEffect(() => {
        var svg = d3
          .select(visualization.current)
          .append('svg')
          .attr('width', width)
          .attr('height', height)
          .append('g')
          .attr('transform', `translate(${width / 2}, ${height / 2})`)
    
        svg
          .selectAll('slices')
          .data(pie)
          .enter()
          .append('path')
          .attr('d', arc)
          .attr('fill', (d, i) => lakersColors(i % 4))
          .attr('stroke', black)
          .attr('stroke-width', 1)
          .attr('class', 'slice')
          .on('mouseover', onMouseOver)
          .on('mouseout', onMouseOut)
    
        svg
          .selectAll('lines')
          .data(pie)
          .enter()
          .append('polyline')
          .attr('stroke', black)
          .attr('stroke-width', 1)
          .style('fill', 'none')
          .attr('points', d => {
            var posA = arc.centroid(d)
            var posB = outerArc.centroid(d)
            var posC = outerArc.centroid(d)
            var midAngle = getMidAngle(d)
            posC[0] = radius * 0.95 * (midAngle < Math.PI ? 1 : -1)
            return [posA, posB, posC]
          })
    
        svg
          .selectAll('labels')
          .data(pie)
          .enter()
          .append('text')
          .text(d => {
            var midAngle = getMidAngle(d)
            return midAngle < Math.PI
              ? `${d.data.name} - ${d.data.points}`
              : `${d.data.points} - ${d.data.name}`
          })
          .attr('class', 'label')
          .attr('transform', d => {
            var pos = outerArc.centroid(d)
            var midAngle = getMidAngle(d)
            pos[0] = radius * 0.99 * (midAngle < Math.PI ? 1 : -1)
            return `translate(${pos})`
          })
          .style('text-anchor', d => {
            var midAngle = getMidAngle(d)
            return midAngle < Math.PI ? 'start' : 'end'
          })
    
        function showTotal() {
          svg
            .append('text')
            .text(`Total: ${total}`)
            .attr('class', 'total')
            .style('text-anchor', 'middle')
        }
    
        function hideTotal() {
          svg.selectAll('.total').remove()
        }
    
        function onMouseOver(d, i) {
          hideTotal()
          setPlayer(d.data)
          d3.select(this)
            .attr('fill', d3.rgb(lakersColors(i % 4)).brighter(0.5))
            .attr('stroke-width', 2)
            .attr('transform', 'scale(1.1)')
        }
    
        function onMouseOut(d, i) {
          setPlayer(null)
          showTotal()
          d3.select(this)
            .attr('fill', lakersColors(i % 4))
            .attr('stroke-width', 1)
            .attr('transform', 'scale(1)')
        }
    
        showTotal()
      }, [])
    
      return (
        <>
          <GlobalStyle />
          <Container>
            <div className='title'>{title}</div>
            <Visualization ref={visualization} />
            {player ? (
              <Tooltip>
                <div>
                  <span className='label'>Name: </span>
                  <span>{player.name}</span>
                  <br />
                  <span className='label'>Points: </span>
                  <span>{player.points}</span>
                  <br />
                  <span className='label'>Percent: </span>
                  <span>{Math.round((player.points / total) * 1000) / 10}%</span>
                </div>
              </Tooltip>
            ) : null}
          </Container>
        </>
      )
    }
    
    NBA Player Salaries & Performance 2018-19 (Bubble Chart)
    Inspiration for example Donut Chart

    좋은 웹페이지 즐겨찾기