React with VisX에서 선형 차트 만들기

VisX은 AirBnB의 저급 도표 라이브러리로 d3을 사용하여 수학과 계산을 한다.이것은 가파른 학습 곡선을 가지고 있지만, 좋은 면에서react에서 성능과 맞춤형 그래프를 만들 수 있습니다.
이 도표를 그리기 위해 저는 IEA data과 협력하여 각국의 연구개발 에너지 기술 지출을 연구할 것입니다.이 데이터들은 매우 전면적이어서 1974년까지 거슬러 올라갈 수 있다.나는 덴마크의 총 에너지 지출과 재생 가능한 에너지 지출을 필터해서 비교할 수 있도록 데이터를 미리 처리했다.
이것은 생성된 데이터 구조입니다.
export const data = 
[{"country":"DENMARK","currency":"RDDUSD","type":"RENEWABLE","year":1975,"amount":0.804},
{"country":"DENMARK","currency":"RDDUSD","type":"RENEWABLE","year":1976,"amount":1.350},
{"country":"DENMARK","currency":"RDDUSD","type":"RENEWABLE","year":1977,"amount":7.928},
{"country":"DENMARK","currency":"RDDUSD","type":"RENEWABLE","year":1978,"amount":15.357}]
우리가 건설할 것은:
  • 애플리케이션 레이아웃 및 맥박 사용자 인터페이스
  • VisX 포함
  • 선형 차트
  • 상호 작용 툴팁 및 십자선

  • 1. NextJS 응용 프로그램 만들기


    npx create-next-app@latest visx-demo
    cd visx-demo
    

    2. 라이브러리 설치


    Chakra UI은 간단한 스타일 설정 기능을 갖춘 훌륭한 UI 구성 요소 라이브러리입니다.
    npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4
    
    VisX은 모듈화된 라이브러리로 우리가 필요로 하는 구성 요소만 사용하고 가방의 크기를 비교적 낮게 유지할 수 있습니다.
    npm install @visx/axis @visx/event @visx/glyph @visx/gradient @visx/grid @visx/group @visx/responsive @visx/scale @visx/shape @visx/tooltip
    

    3. 맥박 UI가 있는 건축 구조


    응용 프로그램이 펄스 사용자 인터페이스와 협동하여 작업을 하기 위해서, 우리는 펄스 스타일을 포함하도록 pages/_app.js 구성 요소를 수정해야 한다.
    import { ChakraProvider, CSSReset } from "@chakra-ui/react";
    import Head from "next/head";
    
    const GlobalStyle = ({ children }) => {
      return (
        <>
          <Head>
            <meta content="width=device-width, initial-scale=1" 
             name="viewport" />
          </Head>
          <CSSReset />
          {children}
        </>
      );
    };
    
    function MyApp({ Component, pageProps }) {
      return (
        <ChakraProvider>
          <GlobalStyle />
        <Component {...pageProps} />
        </ChakraProvider>
      ) 
    }
    
    export default MyApp
    
    components/Header.js의 간단한 제목을 살펴보겠습니다.
    import { Box, Flex,  Heading } from "@chakra-ui/react";
    
    const Header = () => {
        return (
            <Box
                pos="fixed"
                as="header"
                top="0"
                bg='#242730'
                left="0"
                right="0"
                borderBottomWidth="1px"
                width="full"
                height="4rem"
            >
                <Box width="full" mx="auto" px={6} pr={[1, 6]} height="100%" >
                    <Flex size="100%" p={[0, 3]} pl={[0, 4]} align="center" justify="space-between">
                        <Box as="a" d="block" href="/" aria-label="VisX Area Chart">
                            <Heading color="gray.100" as="h4" size="md">VizX Area Chart</Heading>
                        </Box>
                    </Flex>
                </Box>
            </Box>
        );
    }
    
    export default Header;
    
    현재 우리는 pages/index.js 파일의 헤더 파일을 가져오고 chakraui 구성 요소로 간단한 레이아웃을 구축할 수 있습니다.
    import Header from "../components/Header"
    import { Container, Heading, Box, Text, Link } from '@chakra-ui/react'
    import ParentSize from '@visx/responsive/lib/components/ParentSize';
    import LineChart from '../components/LineChart';
    import { data } from '../data/stats_for_Denmark'
    
    export default function Home() {
      return (
        <>
          <Header />
          <Box height='100vh' bg="#242730">
            <Container maxW='4xl' height='85vh' mt="4rem" >
              <Heading ml='40px' as='i' size='md' color={'gray.100'}>Denmark R&D Spend on Renewable Energy vs Total</Heading>
             // Chart will go here
              <Link ml='40px' fontSize='sm' color={'gray.100'} href='https://www.iea.org/data-and-statistics/data-product/energy-technology-rd-and-d-budget-database-2' isExternal>
                Data by IEA, 2021
              </Link></Container>
          </Box>
        </>
      )
    }
    

    4. Viz 응답 설정


    도표가 응답성을 가지도록 하기 위해서, 우리는 부모 용기의 너비와 높이를 이해해야 한다.VisX 라이브러리는 <ParentSize /> 구성 요소를 @visx/responsive/lib/components/ParentSize에서 쉽게 가져올 수 있도록 설계되었습니다.우리는 <ParentSize />으로 도표 부품을 포장하고 너비와 높이를 도구로 사용할 것이다.
     <ParentSize>
    {({ width, height }) => 
        <LineChart data={data} width={width} height={height} />}</ParentSize>
    
    이제 <Group />을 이해하고 margins을 가시화하는 두 개의svg 직사각형을 구축합시다.VisX <Group /> components는 모든 하위 요소인 <g />을 포함하는 컨테이너로서 코드를 단순화하고 여백을 전달할 수 있습니다.components/LineChart.js:
    import { Group } from "@visx/group";
    
    function LineChart({ data, width, height }) {
        // define margins from where to start drawing the chart
        const margin = { top: 40, right: 40, bottom: 50, left: 40 };
        // defining inner measurements
        const innerWidth = width - margin.left - margin.right;
        const innerHeight = height - margin.top - margin.bottom;
        return (
            <svg width={width} height={height} >
                  <rect x={0} y={0} width={width} height={height} fill={'#718096'} rx={14} />
                <Group left={margin.left} top={margin.top}>
                <rect x={0} y={0} width={innerWidth} height={innerHeight} fill={'#A0AEC0'} />
                </Group>
            </svg>
        )
    }
    
    export default LineChart
    
    경계 및 여백이 있는 배치:

    브라우저는 왼쪽 상단(0,0)부터 coordinate system<svg /> 요소를 그립니다. 여백은 초기 좌표(즉 원점)를 시계 방향으로 정의합니다.

    5. 건축축선


    작은 일부터 시작해서 축선을 만들자.축을 구성하려면 먼저 배율을 정의해야 합니다.엔진 덮개 아래 VisX는 d3을 사용하기 때문에 우리는 d3 눈금 documentation을 볼 수 있습니다.D3은 수학적 도움말을 사용하여 컨테이너 내의 픽셀 위치로 숫자를 변환합니다.
    눈금의 범위를 얻기 위해, 나는 d3.extent() 함수를 사용했는데, 이것은 수조에서 최소값과 최대값을 되돌려준다.
    모든 함수로 축의 기호를 포맷할 수 있습니다. 이 예에서 날짜 라벨을 문자열로 변환해서 기본 숫자 포맷에서 쉼표를 삭제합니다.const formatDate = (year) => year.toString()components/Chart.js:
     // Defining selector functions
     const getRD = (d) => d.amount;
     const getDate = (d) => d.year;
    
    // Defining scales
    
    // horizontal, x scale
    const timeScale = scaleLinear({
        range: [0, innerWidth],
        domain: extent(data, getDate),
        nice: true
        })
    
    // vertical, y scale
    const rdScale = scaleLinear({
         range: [innerHeight, 0],
         domain: extent(data, getRD),
         nice: true,
    });
    
    <svg/> 컨테이너 내의 축을 정의합니다.
    <AxisLeft
        tickTextFill={'#EDF2F7'}
        stroke={'#EDF2F7'}
        tickStroke={'#EDF2F7'}
        scale={rdScale}
        tickLabelProps={() => ({
            fill: '#EDF2F7',
            fontSize: 11,
            textAnchor: 'end',
          })} 
    />
    <text x="-125" y="20" transform="rotate(-90)" fontSize={12} fill='#EDF2F7'>
         R&D Spend, RDDUSD
    </text>
    <AxisBottom
        scale={timeScale}
        stroke={'#EDF2F7'}
        tickFormat={formatDate}
        tickStroke={'#EDF2F7'}
        tickTextFill={'#EDF2F7'}
        top={innerHeight}
        tickLabelProps={() => ({
            fill: '#EDF2F7',
            fontSize: 11,
            textAnchor: 'middle',
        })} 
    />
    
    나는 또한 우리의 도표에 <GridRows />GridColumns />을 추가하고 싶다. 그들은 축과 같은 비율을 사용할 것이다.
    <GridRows 
        scale={rdScale} 
        width={innerWidth} 
        height={innerHeight - margin.top} 
        stroke='#EDF2F7' 
        strokeOpacity={0.2} 
    />
    <GridColumns 
        scale={timeScale} 
        width={innerWidth} 
        height={innerHeight} 
        stroke='#EDF2F7' 
        strokeOpacity={0.2} 
    />
    
    결과는 이렇다.나는 보통 마지막으로 내부와 외부 직사각형만 제거하고, 작업 중에 도표에 요소를 배치하도록 허용한다.

    6. 선형 차트 작성


    선형 다이어그램의 경우 <LinePath /> 구성 요소를 사용합니다.나는 덴마크의 연구개발 총투자와 재생에너지 투자를 비교하기 위해 두 라인을 세우고 싶다.이를 위해, 나는 원시 그룹의 데이터를 필터하고, 두 개의 출력을 포함하여 줄을 만들 시리즈를 정의할 것이다.
    내가 이 멋진 MetBrewer R 팔레트 세트에서 고른 라인의 배색 방안.
    //colours for lines
    const colors = ['#43b284', '#fab255']
    
    // data for lines
    const data1 = data.filter(function (el) {
        return el.type === "RENEWABLE"
    });
    
    const data2 = data.filter(function (el) {
        return el.type === "TOTAL"
    });
    
    const series = [data1, data2]
    
    <LinePath /><svg /> 컨테이너에 배치하기 위해 데이터를 매핑합니다.
    {series.map((sData, i) => (
        <LinePath
             key={i}
             stroke={colors[i]}
             strokeWidth={3}
             data={sData}
             x={(d) => timeScale(getDate(d)) ?? 0}
             y={(d) => rdScale(getRD(d)) ?? 0}
        />
    ))}
    
    이제 각 데이터 시리즈의 라인을 볼 수 있습니다.

    7. 상호작용 증가


    이 도표에 십자선을 추가하고 싶습니다. 십자선은 데이터 포인트를 중심으로 하는 세선일 뿐이고, 연도와 연구개발 지출 가치가 있는 도구 알림일 뿐입니다.
    docs의 규정에 따라 도구 알림을 추가하려면 전체 부품을 상대적인 위치를 가진 용기에 포장해야 한다.
    <div position = 'relative'>
    /// Your whole component ///
    </div>
    
    VisX에는 많은 작업을 할 수 있는 편리한 갈고리가 있습니다.
    // tooltip parameters
    const { 
        tooltipData, 
        tooltipLeft = 0, 
        tooltipTop = 0, 
        showTooltip, 
        hideTooltip 
    } = useTooltip();
    
    그러나 포지셔닝 도구 알림은 복잡하지만 까다로운 문제이다.간단히 말해서, 우리는 마우스 정지에서 X와 Y 좌표를 가져와 데이터 값으로 변환해야 한다.
    이를 위해, 내가 본 대부분의 예는 데이터가 집중된 위치를 찾기 위해 d3.bisect()을 사용한다.나는 두 줄이 있기 때문에 d값이 필요하다. 나는 대분 함수에서 얻은 날짜값으로 그룹을 필터한다.
     // function get data from a year
     const getD = (year) => {
         const output = data.filter(function (el) {
             return el.year === year
         })
         return output
        }
    
    이제 함수를 정의하여 툴팁을 처리합니다.
    const handleTooltip = useCallback((event) => {
    const { x } = localPoint(event) || { x: 0 };
    const x0 = timeScale.invert(x - margin.left); // get Date from the scale
    
    const index = bisectDate(data, x0, 1); // get index of this date from the array
    const d0 = data[index - 1];
    const d1 = data[index];
    let d = d0;
    // is previous data point available?
    if (d1 && getDate(d1)) {
        d = x0.valueOf() - getDate(d0).valueOf() > 
             getDate(d1).valueOf() - x0.valueOf() ? d1 : d0;
         }
    showTooltip({
        tooltipData: getD(d.year),
        tooltipLeft: x,
        tooltipTop: rdScale(getRD(d))
    })
    })
    
    예를 들어 두 행의 툴팁 데이터는 다음과 같습니다.
    [
        {
            "country": "DENMARK",
            "currency": "RDDUSD",
            "type": "RENEWABLE",
            "year": 2006,
            "amount": 41.657
        },
        {
            "country": "DENMARK",
            "currency": "RDDUSD",
            "type": "TOTAL",
            "year": 2006,
            "amount": 112.857
        }
    ]
    
    이제 도구 알림을 정의할 것입니다. 도구 알림을
    {/* render a tooltip */}
    {tooltipData ? (
         <TooltipWithBounds 
             key={Math.random()}
             top={tooltipTop}
             left={tooltipLeft}
             style={tooltipStyles}
          > 
          <p>{`Total Spend: $${getRD(tooltipData[1])}`}</p>
          <p>{`Renewable Spend: $${getRD(tooltipData[0])}`}</p>
          <p>{`Year: ${getDate(tooltipData[1])}`}</p>
          </TooltipWithBounds>
          ) 
    : null}
    
    간단하게 십자선을 정의할 때 모든 <g/> 요소는 <svg />에 있어야 합니다.
    {tooltipData && (
        <g>
             <Line
                 from={{ x: tooltipLeft - margin.left, y: 0 }}
                 to={{ x: tooltipLeft - margin.left, y:innerHeight 
                 }}
                  stroke={'#EDF2F7'}
                  strokeWidth={2}
                  pointerEvents="none"
                  strokeDasharray="4,2"
               />
        </g>
    )}
    
    @visx/glyph을 사용하여 로딩 시 표시할 점을 정의합니다.
    {tooltipData && tooltipData.map((d, i) => (
        <g>
            <GlyphCircle 
                left={tooltipLeft - margin.left}
                top={rdScale(d.amount) + 2}
                size={110}
                fill={colors[i]}
                stroke={'white'}
                strokeWidth={2} />
         </g>
    ))}
    
    이제 HandletToolTip 함수를 호출하여 도구 설명, 십자선, 그림 문자에 포지셔닝 값을 전달해야 합니다.
    차트 내의 모든 점에 십자선을 표시하려면 첫 번째 점 채우기를 투명하게 변경하고 사용자 상호 작용에서 handleTooltip을 호출합니다.
    <rect 
        x={0} 
        y={0} 
        width={innerWidth} 
        height={innerHeight} 
        fill={'transparent'}
        onTouchStart={handleTooltip} 
        onTouchMove={handleTooltip}
        onMouseMove={handleTooltip}
        onMouseLeave={() => hideTooltip()}
    />
    
    나는 또한 이 <rect />을 나의 모든 원소 뒤에 놓았다. 왜냐하면 이것은 다른 원소에 겹쳐져 있기 때문에 맨 윗원소가 되어 모든 도표를 상호작용하게 할 것이다.
    이것이 바로 최종 결과의 모습이다.

    솔직히 말해서, 비록 나는 d3와 합작한 적이 있지만.js 이전에 VisX로 도표를 만들 때 좀 지루하고 어려웠어요.하지만 코드를 가지고 놀 때 간결한 API를 좋아하고 개발자에게 주는 힘과 유연성을 배웠다.
    만약 당신도 시험해 보고 싶다면,GitHub에 대한 링크가 있습니다:https://github.com/MariaZentsova/visx-linechart
    내가 사용한 예는 학습과 계발이다.
    How to Make Beautiful Graphs With vx and React-Motion by Dylan Mozlowski
    VisX Area Chart
    VisX lines with glyphs
    VisX Area Difference Chart

    좋은 웹페이지 즐겨찾기