Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a containerRef prop for those who need a reference to the rendered chart #781

Closed
boygirl opened this issue Oct 4, 2017 · 8 comments
Closed

Comments

@boygirl
Copy link
Contributor

boygirl commented Oct 4, 2017

Supports exporting to pdf etc.

Implementation:

A prop on VictoryContainer (and all the containers that extend it) called containerRef takes a function. The ref will be applied to the outermost element that Victory renders.

Usage:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.logContainerRef = this.logContainerRef.bind(this);
  }

  componentDidMount() {
    this.logContainerRef();
  }

  logContainerRef() {
    console.log(this.containerRef);
  };

  render() {
    return (
      <VictoryBar
         containerComponent={
            <VictoryContainer containerRef={(ref) => { this.containerRef = ref; }}/>
         }
     />
   );
 }
}
@boygirl
Copy link
Contributor Author

boygirl commented Oct 4, 2017

@esutton

I expect this change to be released in main victory this week, but not in victory-native until some time next week.

@esutton
Copy link

esutton commented Oct 4, 2017

@boygirl Thank you so much!

@boygirl
Copy link
Contributor Author

boygirl commented Oct 6, 2017

released in victory@0.23.1. Look for this in the next victory native release as well. ~ next week

@boygirl boygirl closed this as completed Oct 6, 2017
@esutton
Copy link

esutton commented Oct 6, 2017

Thank you.

I have not tried it yet but I assume this should work with the new containerRef exposed:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.logContainerRef = this.logContainerRef.bind(this);
  }

  componentDidMount() {
    this.logContainerRef();
  }

  logContainerRef() {
    console.log(this.containerRef);
  }

  render() {
    return (
      <VictoryBar
        containerComponent={
          <VictoryContainer containerRef={(ref) => {
            this.containerRef = ref;

            setTimeout(() => {
              this.containerRef.svgRef.toDataURL((base64) => {
                console.log(base64);
              });
            }, 500);
          }}
          />
        }
      />
    );
  }
}


@arieldf
Copy link

arieldf commented Oct 6, 2017

@boygirl @esutton I have tried your exact example above and I get the following error: Uncaught TypeError: Cannot read property 'toDataURL' of undefined at setTimeout

I have tried for quite some time to understand the issue, but without any luck. Do you know what could be the issue?

@ghost
Copy link

ghost commented Jun 28, 2019

@arieldf @boygirl I too get this same issue. Anyone have any thoughts?

@nithil
Copy link

nithil commented Aug 28, 2019

Any update on this? TypeError: Cannot read property 'toDataURL' of undefined

@alfredvaa
Copy link

Old issue but this was almost all I found on the issue of trying to export as an image. I did not get the suggestion above to work, but here is another solution that is working:

import { useLayoutEffect, useRef } from "react";

interface Props {
  svgRef: any;
}

export const DownloadChart = ({ svgRef }: Props) => {
  const canvasRef = useRef<any>();
  const linkRef = useRef<any>();

  useLayoutEffect(() => {
    const ctx = canvasRef.current.getContext("2d");
    const xml = new XMLSerializer().serializeToString(
      svgRef.current.firstChild
    );

    const dimensions = svgRef.current.getBoundingClientRect();
    canvasRef.current.height = dimensions.height;
    canvasRef.current.width = dimensions.width;

    var svg64 = btoa(xml);
    var b64Start = "data:image/svg+xml;base64,";

    var image64 = b64Start + svg64;
    const image = new Image();

    image.src = image64;
    image.onload = () => {
      ctx.drawImage(image, 0, 0, dimensions.width, dimensions.height);
      linkRef.current.href = canvasRef.current.toDataURL();
      linkRef.current.download = "chart.png";
      linkRef.current.style.display = "block";
    };
  }, [svgRef]);

  return (
    <>
      <canvas ref={canvasRef} style={{ display: "none" }} />
      <a ref={linkRef} href="chart.png" style={{ display: "none" }}>
        Download
      </a>
    </>
  );
};

The usage of the component:

const svgRef = useRef<any>();
const [animationDone, setAnimationDone] = useState(false);

...

{animationDone && <DownloadChart svgRef={svgRef} />}

...
<VictoryChart
  animate={{
    easing: "quadInOut",
    onEnd: () => {
      setAnimationDone(true);
    }
  }}
  containerComponent={
    <VictoryContainer
      containerRef={(ref) => {
        svgRef.current = ref;
      }}
    />
  }
... 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants