Visx - это библиотека, которая позволяет нам легко добавлять графику в наше приложение React.

В этой статье мы рассмотрим, как использовать его для добавления кривых с сегментами, имеющими собственные стили, в наше приложение React.

Установить необходимые пакеты

Пришлось установить несколько модулей.

Для начала запускаем:

npm i @visx/curve @visx/gradient @visx/responsive @visx/scale @visx/shape

для установки пакетов.

Создайте многостильную линию

Мы можем создать диаграмму, добавив элементы, предоставленные модулями.

Чтобы добавить строку, мы пишем:

import React, { useMemo } from "react";
import { scaleLinear } from "@visx/scale";
import { curveCardinal } from "@visx/curve";
import { LinePath, SplitLinePath } from "@visx/shape";
import { LinearGradient } from "@visx/gradient";
const getX = (d) => d.x;
const getY = (d) => d.y;
const background = "#045275";
const backgroundLight = "#089099";
const foreground = "#b7e6a5";
function generateSinPoints({
  width,
  height,
  numberOfWaves = 10,
  pointsPerWave = 10
}) {
  const waveLength = width / numberOfWaves;
  const distanceBetweenPoints = waveLength / pointsPerWave;
  const sinPoints = [];
  for (let waveIndex = 0; waveIndex <= numberOfWaves; waveIndex += 1) {
    const waveDistFromStart = waveIndex * waveLength;
    for (let pointIndex = 0; pointIndex <= pointsPerWave; pointIndex += 1) {
      const waveXFraction = pointIndex / pointsPerWave;
      const waveX = pointIndex * distanceBetweenPoints;
      const globalX = waveDistFromStart + waveX;
      const globalXFraction = (width - globalX) / width;
      const waveHeight =
        Math.min(globalXFraction, 1 - globalXFraction) * height;
      sinPoints.push({
        x: globalX,
        y: waveHeight * Math.sin(waveXFraction * (2 * Math.PI))
      });
    }
  }
  return sinPoints;
}
function Example({
  width,
  height,
  numberOfWaves = 10,
  pointsPerWave = 100,
  numberOfSegments = 8
}) {
  const data = useMemo(
    () => generateSinPoints({ width, height, numberOfWaves, pointsPerWave }),
    [width, height, numberOfWaves, pointsPerWave]
  );
  const dividedData = useMemo(() => {
    const segmentLength = Math.floor(data.length / numberOfSegments);
    return new Array(numberOfSegments)
      .fill(null)
      .map((_, i) => data.slice(i * segmentLength, (i + 1) * segmentLength));
  }, [numberOfSegments, data]);
  const getScaledX = useMemo(() => {
    const xScale = scaleLinear({ range: [0, width], domain: [0, width] });
    return (d) => xScale(getX(d)) ?? 0;
  }, [width]);
  const getScaledY = useMemo(() => {
    const yScale = scaleLinear({ range: [0, height], domain: [height, 0] });
    return (d) => yScale(getY(d)) ?? 0;
  }, [height]);
  return width < 10 ? null : (
    <div>
      <svg width={width} height={height}>
        <LinearGradient
          id="visx-shape-splitlinepath-gradient"
          from={background}
          to={backgroundLight}
          fromOpacity={0.8}
          toOpacity={0.8}
        />
        <rect
          x={0}
          y={0}
          width={width}
          height={height}
          fill="url(#visx-shape-splitlinepath-gradient)"
          rx={14}
        />
        <g transform={`rotate(${0})translate(${-0}, ${-height * 0.5})`}>
          <LinePath
            data={data}
            x={getScaledX}
            y={getScaledY}
            strokeWidth={8}
            stroke="#fff"
            strokeOpacity={0.15}
            curve={curveCardinal}
          />
          <SplitLinePath
            segments={dividedData}
            x={getScaledX}
            y={getScaledY}
            curve={curveCardinal}
            styles={[
              { stroke: foreground, strokeWidth: 3 },
              { stroke: "#fff", strokeWidth: 2, strokeDasharray: "9,5" },
              { stroke: background, strokeWidth: 2 }
            ]}
          >
            {({ segment, styles, index }) =>
              index === numberOfSegments - 1 || index === 2 ? (
                segment.map(({ x, y }, i) =>
                  i % 8 === 0 ? (
                    <circle
                      key={i}
                      cx={x}
                      cy={y}
                      r={10 * (i / segment.length)}
                      stroke={styles?.stroke}
                      fill="transparent"
                      strokeWidth={1}
                    />
                  ) : null
                )
              ) : (
                <LinePath
                  data={segment}
                  x={(d) => d.x || 0}
                  y={(d) => d.y || 0}
                  {...styles}
                />
              )
            }
          </SplitLinePath>
        </g>
      </svg>
    </div>
  );
}
export default function App() {
  return (
    <div className="App">
      <Example width={500} height={300} />
    </div>
  );
}

Мы добавляем функции getX и getY, чтобы получить данные для осей x и y.

background - один из цветов градиента фона.

backgroundLight имеет более светлый цвет фонового градиента.

foreground имеет один из цветов линии.

Мы генерируем точки для линии с помощью функции generateSinPoints.

globalX имеет координаты x синусоиды.

И мы используем метод Math.sin для создания значения оси y.

В компоненте Example мы визуализируем линию в различных стилях.

Делим линию на сегменты переменной dividedData.

Это делается путем создания массива значений и разделения значений, созданных из generateSinPoints, на slice.

Затем мы создаем шкалы для графика с помощью переменных getScaledX и getScaledY.

Мы создаем их с помощью функции scaleLinear, так как все в линейном масштабе.

Затем мы визуализируем левый отрезок линии с помощью компонента LinePath.

И мы используем компонент SplitLinePath для рендеринга оставшихся сегментов.

Мы получаем стили из опоры styles и визуализируем их стили с помощью компонента LineSegment, который мы возвращаем в опоре рендеринга SplitLinePath.

Заключение

Мы можем создать линию из нескольких сегментов с их собственными стилями в нашем приложении React с библиотекой Visx.

Понравилась эта статья? Если да, то получите больше похожего контента, подписавшись на наш канал YouTube!

Больше контента на plainenglish.io