通过D3_SAVE_SVG下载SVG后,React应用程序中断了

发布于 2025-02-09 20:21:20 字数 2598 浏览 1 评论 0原文

我正在原型型简单react应用程序,其中用户能够与svg使用d3生成的svg并下载从其交互中产生的SVG (尽管在原型制定阶段,React并不是特别有用,但是一旦原型扩展,最终将需要它)。

我偶然发现了d3-save-svg库( https:// Github.com/edeno/d3-save-svg )确实很方便,我正在原型中测试它,以查看我是否可以依靠它来期待它。

从用户可以:

  1. svg进行交互的意义上,该原型正在“良好”,并
  2. 下载svg是由于其交互而产生的。

但是,依赖于d3-save-svg下载svg,似乎打破了与svg的进一步互动,这是无意的。用户仍然能够重载svg,但不能再与svg进行交互。

谁能:

  • 帮助我了解正在发生的事情和/或
  • 提出一个修复程序,该修复程序允许用户通过第一个下载,与svg进一步交互并重新下载其进一步交互的结果?

这是原型代码( https://codesandbox.io/sandbox.io/goofy-hellman-quccpppyd < /a>):

import { useRef, useEffect } from "react";
import * as d3 from "d3";
import d3_save_svg from "d3-save-svg";

export default function App() {
  const ref = useRef();

  useEffect(() => {
    const svg = d3
      .select(ref.current)
      .append("svg")
      .attr("width", 500)
      .attr("height", 500);

    svg
      .append("circle")
      .attr("cx", 400)
      .attr("cy", 300)
      .attr("r", 100)
      .attr("fill", "blue");

    svg
      .append("text")
      .text("Move")
      .attr("x", 100)
      .attr("y", 100)
      .attr("cursor", "pointer")
      .on("click", function () {
        svg.selectAll("circle").attr("cx", function () {
          return d3.select(this).attr("cx") - 10;
        });
      });

    svg
      .append("path")
      .attr(
        "d",
        "M896 672q119 0 203.5 84.5t84.5 203.5-84.5 203.5-203.5 84.5-203.5-84.5-84.5-203.5 84.5-203.5 203.5-84.5zm704-416q106 0 181 75t75 181v896q0 106-75 181t-181 75h-1408q-106 0-181-75t-75-181v-896q0-106 75-181t181-75h224l51-136q19-49 69.5-84.5t103.5-35.5h512q53 0 103.5 35.5t69.5 84.5l51 136h224zm-704 1152q185 0 316.5-131.5t131.5-316.5-131.5-316.5-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5z"
      )
      .attr("transform", "translate(200 100) scale(0.02)")
      .attr("class", "snap-button")
      .attr("fill", "#1976d2")
      .attr("cursor", "pointer")
      /* .html(iconHtml) */
      .on("click", function () {
        var config = {
          filename: "customFileName"
        };

        d3_save_svg.save(d3.select("svg").node(), config);
      });
  });

  return <div ref={ref} />;
}

I am prototyping a simple React App where the user is able to interact with an SVG generated with d3 and download the SVG that results from their interaction (though React isn't especially useful at the prototyping stage, it will be required eventually once the prototype is expanded upon).

I stumbled upon the d3-save-svg library (https://github.com/edeno/d3-save-svg) which is really handy and I am testing it in my prototype to see if I can rely on it looking forward.

The prototype is working "well" in the sense that the user is able to:

  1. Interact with the SVG, and
  2. Download the SVG resulting from their interaction.

However downloading the SVG, which relies on d3-save-svg, appears to break further interactivity with the SVG, which is unintended. The user is still able to redownload the SVG but cannot interact with the SVG anymore.

Can anyone:

  • Help me understand what's going on, and/or
  • Propose a fix that would allow the user to, pass the first download, interact further with the SVG and redownload the result of their further interactions?

Here is the prototype code (https://codesandbox.io/s/goofy-hellman-qcppyd):

import { useRef, useEffect } from "react";
import * as d3 from "d3";
import d3_save_svg from "d3-save-svg";

export default function App() {
  const ref = useRef();

  useEffect(() => {
    const svg = d3
      .select(ref.current)
      .append("svg")
      .attr("width", 500)
      .attr("height", 500);

    svg
      .append("circle")
      .attr("cx", 400)
      .attr("cy", 300)
      .attr("r", 100)
      .attr("fill", "blue");

    svg
      .append("text")
      .text("Move")
      .attr("x", 100)
      .attr("y", 100)
      .attr("cursor", "pointer")
      .on("click", function () {
        svg.selectAll("circle").attr("cx", function () {
          return d3.select(this).attr("cx") - 10;
        });
      });

    svg
      .append("path")
      .attr(
        "d",
        "M896 672q119 0 203.5 84.5t84.5 203.5-84.5 203.5-203.5 84.5-203.5-84.5-84.5-203.5 84.5-203.5 203.5-84.5zm704-416q106 0 181 75t75 181v896q0 106-75 181t-181 75h-1408q-106 0-181-75t-75-181v-896q0-106 75-181t181-75h224l51-136q19-49 69.5-84.5t103.5-35.5h512q53 0 103.5 35.5t69.5 84.5l51 136h224zm-704 1152q185 0 316.5-131.5t131.5-316.5-131.5-316.5-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5z"
      )
      .attr("transform", "translate(200 100) scale(0.02)")
      .attr("class", "snap-button")
      .attr("fill", "#1976d2")
      .attr("cursor", "pointer")
      /* .html(iconHtml) */
      .on("click", function () {
        var config = {
          filename: "customFileName"
        };

        d3_save_svg.save(d3.select("svg").node(), config);
      });
  });

  return <div ref={ref} />;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

鸢与 2025-02-16 20:21:20

事实证明,d3_save_svg在导出SVG之前修改了DOM,并在其中添加了许多内容,包括样式。似乎,由于这发生在React的渲染周期之外,因此React将其裁判丢失,并失去了SVG所需的隐式状态。

一种解决方案是将导出基于svg的克隆版本,然后在导出后立即删除,从而保证用户永远不会看到复制品(如果需要的话,可以有其他其他确保用户永远不会看到副本的方法,例如在Viewbox之外找到副本)。

import { useRef, useEffect } from "react";
import * as d3 from "d3";
import d3_save_svg from "d3-save-svg";

export default function App() {
  const ref = useRef();

  useEffect(() => {
    const svg = d3
      .select(ref.current)
      .append("svg")
      .attr("width", 500)
      .attr("height", 500);

    svg
      .append("circle")
      .attr("cx", 400)
      .attr("cy", 300)
      .attr("r", 100)
      .attr("fill", "blue");

    svg
      .append("text")
      .text("Move")
      .attr("x", 100)
      .attr("y", 100)
      .attr("cursor", "pointer")
      .on("click", function () {
        svg.selectAll("circle").attr("cx", function () {
          return d3.select(this).attr("cx") - 10;
        });
      });

    svg
      .append("path")
      .attr(
        "d",
        "M896 672q119 0 203.5 84.5t84.5 203.5-84.5 203.5-203.5 84.5-203.5-84.5-84.5-203.5 84.5-203.5 203.5-84.5zm704-416q106 0 181 75t75 181v896q0 106-75 181t-181 75h-1408q-106 0-181-75t-75-181v-896q0-106 75-181t181-75h224l51-136q19-49 69.5-84.5t103.5-35.5h512q53 0 103.5 35.5t69.5 84.5l51 136h224zm-704 1152q185 0 316.5-131.5t131.5-316.5-131.5-316.5-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5z"
      )
      .attr("transform", "translate(200 100) scale(0.02)")
      .attr("class", "snap-button")
      .attr("fill", "#1976d2")
      .attr("cursor", "pointer")
      /* .html(iconHtml) */
      .on("click", function () {
        var config = {
          filename: "customFileName"
        };

         let svgExport = d3.select(".svg").clone(true)
         d3_save_svg.save(svgExport.node(), config);
         svgExport.remove()
      });
  });

  return <div ref={ref} />;
}

Turns out d3_save_svg modifies the DOM before it exports the svg, adding a number of things to it including styles. It seems that, since this happens outside of react's rendering cycle, React looses its refs and looses track of the SVG's desired implicit state.

A solution is to base the export on a clone version of the SVG which you can then remove right after the export, thereby guaranteeing the copy is never actually visible to the user (if desired, there can be other ways to do ensure the user never sees the copy, such as locating the copy outside of the viewbox).

import { useRef, useEffect } from "react";
import * as d3 from "d3";
import d3_save_svg from "d3-save-svg";

export default function App() {
  const ref = useRef();

  useEffect(() => {
    const svg = d3
      .select(ref.current)
      .append("svg")
      .attr("width", 500)
      .attr("height", 500);

    svg
      .append("circle")
      .attr("cx", 400)
      .attr("cy", 300)
      .attr("r", 100)
      .attr("fill", "blue");

    svg
      .append("text")
      .text("Move")
      .attr("x", 100)
      .attr("y", 100)
      .attr("cursor", "pointer")
      .on("click", function () {
        svg.selectAll("circle").attr("cx", function () {
          return d3.select(this).attr("cx") - 10;
        });
      });

    svg
      .append("path")
      .attr(
        "d",
        "M896 672q119 0 203.5 84.5t84.5 203.5-84.5 203.5-203.5 84.5-203.5-84.5-84.5-203.5 84.5-203.5 203.5-84.5zm704-416q106 0 181 75t75 181v896q0 106-75 181t-181 75h-1408q-106 0-181-75t-75-181v-896q0-106 75-181t181-75h224l51-136q19-49 69.5-84.5t103.5-35.5h512q53 0 103.5 35.5t69.5 84.5l51 136h224zm-704 1152q185 0 316.5-131.5t131.5-316.5-131.5-316.5-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5z"
      )
      .attr("transform", "translate(200 100) scale(0.02)")
      .attr("class", "snap-button")
      .attr("fill", "#1976d2")
      .attr("cursor", "pointer")
      /* .html(iconHtml) */
      .on("click", function () {
        var config = {
          filename: "customFileName"
        };

         let svgExport = d3.select(".svg").clone(true)
         d3_save_svg.save(svgExport.node(), config);
         svgExport.remove()
      });
  });

  return <div ref={ref} />;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文