Как сделать снимок экрана с компонента React, не монтируя его в DOM?
Мой пример использования: у меня есть редактор веб-сайта и список доступных веб-страниц для пользователей. Каждая страница в этом списке представлена миниатюрой. Каждый раз, когда пользователь вносит изменения в страницу с помощью редактора, эскиз соответствующего сайта должен обновляться, чтобы отразить это изменение. Я делаю это, монтируя компонент ThumbnailSandbox на странице, передавая реквизиты из хранилища Redux, а затем используя dom-to-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
, Поскольку ownerDocument элемента, передаваемого в html2canvas, имеет значение null и не имеет свойств: devicePixelRation, innerWidth, innerHeight, pageYOffset и pageXOffset. Как я понимаю, это потому, что элемент узла не является частью DOM.
Теперь мои вопросы:
1) Есть ли способ решить эту проблему с помощью html2canvas?
2) Есть ли другой способ сделать скриншот компонента React в браузере, не монтируя компонент в DOM?
Заранее спасибо!!
1 ответ
Для пункта 1:
Почему бы вам не смонтировать компонент, а затем после обработки удалить компонент в ссылке? (может быть сделано и в ComponentDidMount, но ref должен предшествовать DidMount) Это наиболее стандартное решение для загрузки (создайте тег, нажмите, а затем удалите его)
Это пример непроверенного кода с использованием ref call back
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
Из раздела doc реагирования componentDidMount(): "Однако это может быть необходимо для случаев, таких как модалы и всплывающие подсказки, когда вам нужно измерить узел DOM перед рендерингом чего-либо, зависящего от его размера или положения".
Это дает понять, что вы можете получить измерения узлов только после того, как они были смонтированы.