如何在不将其安装在DOM中的情况下拍摄React组件的屏幕截图?

时间:2019-01-29 22:05:47

标签: reactjs dom base64 screenshot react-dom-server

我的使用情况是:我有一个网站编辑和提供自己的网页的用户列表。在此列表中的每个页面由一个缩略图来表示。每次用户使用编辑器对页面进行更改时,都必须更新相应站点的缩略图以反映更改。我做的方式是通过安装在页面中ThumbnailSandbox组件,从终极版店里传递的道具,然后使用DOM至PNG创建截图,并在列表中使用它。但我想这样做没有安装页面上的部件,因为我认为这将是一个更清洁的解决方案,并受着其他交互发生的机会较少。因此,我创建了一个CodeSanbox来说明我要实现的目标。

我的逻辑是这样的:

import React from "react";
import ReactDOMServer from "react-dom/server";
import html2canvas from "html2canvas";
import MyComp from "./component.jsx";

export const createScrenshot = () => {
  const el = (
    <div>
      test component <MyComp />
    </div>
  );

  const markup = ReactDOMServer.renderToString(el);
  let doc = new DOMParser().parseFromString(markup, "text/html");
  let target = doc.body.getElementsByClassName("my-comp")[0];

  console.log(markup, target);

  html2canvas(target, {
    useCORS: true,
    allowTaint: true,
    scale: 1,
    width: 500,
    height: 500,
    x: 0,
    y: 0,
    logging: true,
    windowWidth: 500,
    windowHeight: 500
  })
    .then(function(canvas) {
      console.log(">> ", canvas);
    })
    .catch(error => {
      console.log(error);
    });
};

因此,我将组件传递给ReactDOM,然后使用第一步中的字符串创建一个DOM节点,并将该节点传递给html2canvas。但是这时我得到了错误Uncaught TypeError: Cannot read property 'pageXOffset' of null。由于传递给html2canvas元素的ownerDocument为null,并且它不具有的属性:devicePixelRation,innerWidth,innerHeight,pageYOffset和pageXOffset。据我了解,那是因为node元素不是DOM的一部分。

现在,我的问题是:

1)是否有来解决使用这个问题html2canvas?

的方式

2)是否有任何其他的方式,采取的阵营部件,在浏览器中,屏幕截图,而不在DOM安装部件?

提前谢谢!

2 个答案:

答案 0 :(得分:0)

对于点1:

为什么不安装组件,然后在处理后删除引用中的组件? (也可以在ComponentDidMount中完成,但引用会在DidMount之前出现)。这是执行下载(创建标签并单击然后将其删除)的最标准解决方案。

这是使用ref回调的未经测试的示例代码

export class CreateScrenshot extends React.Component {
    constructor() {
        super() {
            this._reactRef = this._reactRef.bind(this);
            this.state = {
                removeNode: false
            };
        }
    }

    _reactRef(node) {
        if(node) {
            // your html2Canvas handling and in the returned promise remove the node by
            this.setState({removeNode: true});
        }
    }

    render() {
        let childComponent = null;
        if(!this.state.removeNode) {
            {/*pass the ref of the child node to the parent component using the ref callback*/}
            childComponent =  (
                <div>
                    test component <MyComp refCallBack={this._reactRef}/>
                </div>
            );
        }
        return childComponent;
    }
}

但是,限制是这将是异步的并且可能导致闪烁。 因此,如果可能,请尝试使用同步库,以便可以在下一个渲染中删除该节点。

对于点2:https://reactjs.org/docs/react-component.html#componentdidmount

来自react的componentDidMount()doc部分: “但是,对于模态和工具提示之类的情况,当您需要在渲染取决于其大小或位置的对象之前测量DOM节点时,这是必要的。”

这很清楚,您只能在安装节点后才能获得它们的测量值。

答案 1 :(得分:0)

将react元素设置为z-index,底部为-9999px