3

SVG là tuyệt vời. Cách tốt nhất để xây dựng đồ họa có thể mở rộng trên web. SVG có thể làm mọi thứ, từ logo đơn giản đến trực quan hóa dữ liệu và thậm chí cả hoạt hình.

Phần tốt nhất là, bạn có thể thao tác SVG bằng cả CSS JavaScript. Đó là một hình ảnh là một phần của DOM của bạn.

Không có gì ngạc nhiên khi SVG là chuẩn mực khi nói đến trực quan hóa dữ liệu và đồ họa lập trình khác.

Chỉ có một vấn đề: SVG hút bố cục.

Hoàn toàn khủng khiếp. Không có hỗ trợ bố trí nào cả. Bạn có được định vị tuyệt đối và đó là nó. Chắc chắn, bạn có thể định vị tuyệt đối trong các yếu tố được định vị tuyệt đối, đó là loại định vị tương đối, nhưng ...

Địa ngục tuyệt đối

Giả sử bạn đang xây dựng một bảng điều khiển nhỏ, giống như các biểu đồ phân tán khác nhau đang xem bộ dữ liệu về các giống chó.

Bởi vì dữ liệu ở đó, và tất nhiên bạn có thể.

Bạn tạo một thành phần phân tán. Nó có một xvà một yvị trí và thông tin kích thước. Bên trong, nó đặt hai trục, chú thích và biểu dữ liệu.

Các <Scatterplot>vị trí hoàn toàn thông qua một translatechuyển đổi. Điều đó di chuyển một phần tử SVG bằng một vectơ từ (0, 0)đó, do đó kết xuất tại (x, y)tọa độ.

Bạn kết xuất mỗi biểu đồ phân tán như thế này:

<Scatterplot
        data={data}
        x={100}
        y={100}
        width={350}
        height={350}
        filter={d =>
            d.weight &&
            d.height &&
            d.weight[0] &&
            d.height[0]
        }
        xData={d => d.weight[0]}
        yData={d => d.height[0]}
        xLabel="Weight (lbs)"
        yLabel="Height (in)"
        title="Dog Breed Height & Weight"
        entry={props => (
            <Datapoint
                breed={props.d.breed}
                {...props}
            />
        )}
    />

Rất nhiều tranh luận, tôi biết. Dữ liệu, định vị, định cỡ, các chức năng để diễn giải dữ liệu, một vài nhãn và prop prop cho mỗi datapoint.

Sau khi được định vị bằng một phần tử nhóm <g>, biểu đồ phân tán có thể sử dụng định vị gần đúng cho các phần tử của nó.

render() {
    return (
        <g transform={`translate(${x}, ${y})`}>
            <Heading y={-25}>{title}</Heading>
            {data.map(d =>
                entry({
                    x: xScale(xData(d)),
                    y: yScale(yData(d)),
                    d: d
                })
            )}
            <Axis
                scale={xScale}
                x={0}
                y={height}
                type="Bottom"
                label={xLabel}
            />
            <Axis scale={yScale} x={0} y={0} type="Left" label={yLabel} />
        </g>
    );
}

Định vị container chính tại xy. Đặt Headingtại (0, -25), điểm dữ liệu ở bất cứ nơi nào họ đi, một trục tại (0, 0)và một trục khác ở phía dưới bên trái. Tất cả liên quan đến container mẹ.

Container mẹ về mặt kỹ thuật có liên quan đến toàn bộ container SVG ...

Tuyệt đối hoặc tương đối, không quan trọng. Bạn sẽ có một thời gian vui vẻ khi tính toán vị trí của từng yếu tố riêng lẻ. Cân D3 giúp, nhưng bạn vẫn phải suy nghĩ về nó.

Bản thân SVG cung cấp trợ giúp bằng không.

Bạn muốn thay đổi kích thước phân tán của bạn? Đây là những gì xảy ra: 

Bạn muốn thay đổi kích thước trình duyệt của bạn? Đây là những gì xảy ra: 

React-svg-flexbox để giải cứu

Bạn có thể khắc phục vấn đề bố trí với react-svg-flexbox. Đó là một thư viện nhỏ, không có nhiều ngôi sao, nhưng nó quá hoàn hảo.

Được xây dựng dựa trên bố cục css của Facebook , gần đây đã trở thành một phần của yoga , nó cho phép bạn sử dụng CSS flexbox để định vị các yếu tố SVG.

Flexbox có thể gây nhầm lẫn - tôi xem hướng dẫn bất cứ khi nào tôi sử dụng nó cho bất cứ điều gì - nhưng nó tốt hơn so với việc tự làm. Có bao nhiêu kỹ sư làm việc trên các công cụ bố trí trình duyệt trong hai thập kỷ qua?

Bạn sẽ không muốn tự mình lấy lại tất cả các bước đó.

Bọc bảng điều khiển của chúng tôi trong một <Flexbox>yếu tố và ...

import Flexbox from "react-svg-flexbox";

// ...

// render() etc.
<Svg>
    <Flexbox
        style={{
            flexDirection: "row",
            justifyContent: "flex-start"
        }}
    >
        <Scatterplot
            data={data}
            width={200}
            height={200}
            filter={d =>
                d.weight &&
                d.height &&
                d.weight[0] &&
                d.height[0]
            }
            xData={d => d.weight[0]}
            yData={d => d.height[0]}
            xLabel="Weight (lbs)"
            yLabel="Height (in)"
            title="Dog Breed Height & Weight"
            entry={props => (
                <Datapoint
                    breed={props.d.breed}
                    {...props}
                />
            )}
        />

Chúng tôi <Flexbox>loại bỏ react-svg-flexbox, sử dụng các kiểu flexbox để nói rằng chúng tôi muốn kết xuất thành một hàng bắt đầu từ đầu và phần còn lại tự xảy ra.

Lưu ý rằng Reac-svg-flexbox chuyển xyđạo cụ vào các thành phần của chúng tôi, vì vậy chúng tôi phải đưa ra định vị thủ công. Bảng điều khiển của chúng tôi hiện sử dụng hết dung lượng có thể: 

Một cái gì đó thú vị với định vị dọc của chúng tôi, nhưng đó là một sửa chữa dễ dàng. yTọa độ bù bởi một vài pixel. Nó không xảy ra trong các ví dụ chính thức, vì vậy nó phải là thứ chúng tôi đang làm bên trong những phân tán đó.

Các trục vẫn đang chọc bên ngoài widthgiới hạn của chúng tôi , nhưng điều này có vẻ tốt hơn rồi.

Ngay cả khi chúng ta làm cho một trong số chúng lớn hơn, mọi thứ vẫn hoạt động. Không cần thay đổi định vị!

Giao diện đáp ứng với Reac-svg-flexbox

Để giành chiến thắng lớn nhất, chúng tôi thêm flexWrap: wrapvào <Flexbox>thành phần của chúng tôi . Như thế này: 

 <Flexbox
    style={{
        flexDirection: "row",
        justifyContent: "flex-start",
        flexWrap: "wrap",
        width: 1024,
        height: 1024
    }}
>

Bạn phải chỉ định chiều rộng có sẵn trong các kiểu của mình, nếu không, nó không hoạt động. Điều đó có nghĩa là bạn nên lắng nghe window.onresizevà cập nhật chiều rộng cho phù hợp.

Dễ nhất để đính kèm một người nghe sự kiện trong componentDidMount. Như thế này:

class App extends Component {
    state = {
        width: 1024
    };

    svgRef = React.createRef();

    componentDidMount() {
        window.addEventListener("resize", this.updateSize);
    }

    componentWillUnmount() {
        window.removeEventListener("resize", this.updateSize);
    }

    updateSize = () => {
        this.setState({ width: this.svgRef.current.clientWidth });
    };

    render() {
        // ...

        <svg
            style={{ width: "100%", height: 1024 }}
            ref={this.svgRef}
        >
            <Flexbox
                style={{
                    flexDirection: "row",
                    justifyContent: "flex-start",
                    flexWrap: "wrap",
                    width: this.state.width,
                    height: 1024
                }}
            >
    }

Và bảng điều khiển của bạn trở nên nhanh nhạy! Yay!

Xem mã, chơi với các ví dụ

Bạn có thể thấy một tập hợp đầy đủ các ví dụ phản ứng-svg-flexbox trên Storybook của họ .

Mã cho ví dụ bảng điều khiển giống chó của tôi là trên GitHub ở đây .

Bạn có thể thử nó sống ở đây .

Vây

Sử dụng phản ứng-svg-flexbox. Cuộc sống của bạn sẽ được cải thiện. Điều tốt nhất từng xảy ra với tôi đối với mã hóa SVG.

Cảm ơn Cody Averett đã tìm thấy viên ngọc này!

|