A-Frame + pdf.js:无法更新 canvas 纹理两次,仅在 VR 模式下
A-Frame + pdf.js: unable to update canvas texture twice, in VR mode only
我正在使用 A-Frame 和 pdf.js 创建应用程序以在 VR 中查看 PDF 文件。该应用程序在桌面上按预期工作,包括前进到 PDF 文档的下一页并重新呈现为 canvas.
当 运行 在 VR 中的浏览器中(使用 Quest 2 测试)时,第一次尝试按预期呈现。但是,当将第二个文档页面渲染到 canvas 时,新图像无法出现 ,除非 退出 VR 模式,此时新纹理会按预期出现。
我已经尝试将关联的 material map.needsUpdate
重复设置(在 A-Frame 组件滴答循环中)无效。在试验时,我还注意到,如果您尝试第二次渲染一个新的 PDF 页面,然后第三次渲染另一个页面(在 VR 模式下前进两页),当退出 VR 模式时,只会出现第二次的纹理;第三次渲染的数据似乎丢失了 - 我想知道这是否与主要问题有关。
下面是一个最小示例的代码,http://stemkoski.net/test/quest-pdf-min.html 提供了实时版本。在桌面模式下查看示例时,您可以通过打开浏览器 JavaScript 控制台并输入 testBook.nextPage();
前进到下一页,并看到图像按预期变化。您可以使用 Quest 耳机在 VR 模式下进行测试;按 A 按钮应前进到下一页。
问题是:如何在 A-Frame 的 VR 模式下多次将 PDF 文档的页面呈现为相同 canvas?
<!DOCTYPE html>
<html>
<head>
<title>A-Frame + PDF.js</title>
<script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.6.347/pdf.js"></script>
</head>
<body>
<script>
AFRAME.registerComponent("interactive-pdf", {
schema:
{
fileName: {type: 'string', default:"assets/test10.pdf"},
pageWidth: {type: 'float', default: 1200},
pageHeight: {type: 'float', default: 1500},
},
init: function ()
{
this.displayPlane = document.getElementById("plane1");
this.displayMaterial = this.displayPlane.getObject3D('mesh').material;
this.canvas = this.displayPlane.getAttribute("material").src
this.canvas.setAttribute("width", this.data.pageWidth);
this.canvas.setAttribute("height", this.data.pageHeight);
this.context = this.canvas.getContext('2d');
this.textArea = document.querySelector("#textArea");
this.pdf = null;
this.currentPage = 1;
pdfjsLib.GlobalWorkerOptions.workerSrc = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.6.347/pdf.worker.js";
let self = this;
pdfjsLib.getDocument(this.data.fileName).promise.then( function(pdf) {
self.pdf = pdf;
self.render(1);
});
},
render(pageNumber = 1)
{
if (!this.pdf)
return;
let self = this;
let text = "rendering page " + pageNumber + " of " + this.pdf.numPages + " on canvas";
this.textArea.setAttribute("text", "value", text);
this.pdf.getPage(pageNumber).then( function(page) {
const pageViewport = page.getViewport({ scale: 2 });
const context = self.canvas.getContext("2d");
const renderContext = {
canvasContext: context,
viewport: pageViewport
};
const renderTask = page.render(renderContext);
renderTask.promise.then( function() {
self.displayMaterial.map.needsUpdate = true;
});
});
},
nextPage: function()
{
this.currentPage++;
this.render( this.currentPage );
},
prevPage: function()
{
this.currentPage--;
this.render( this.currentPage );
},
tick: function()
{
// this.displayMaterial.map.needsUpdate = true;
}
});
AFRAME.registerComponent('page-turner', {
init: function ()
{
let bookComponent = document.getElementById("interactive-pdf").components["interactive-pdf"];
this.el.addEventListener('abuttondown', function(event) { bookComponent.nextPage(); } );
}
});
</script>
<a-scene>
<a-assets>
<!-- use to display pdf pages -->
<canvas id="canvas1"></canvas>
</a-assets>
<a-camera></a-camera>
<a-sky color = "#000337"></a-sky>
<a-plane id="plane1" material="shader: flat; src: #canvas1;" position="0 1 -2"></a-plane>
<a-entity id="interactive-pdf" position="0 1 -2"
interactive-pdf="fileName: assets/test10.pdf; pageWidth: 1224; pageHeight: 1584;">
</a-entity>
<a-entity id="left-controller" oculus-touch-controls="hand: left"></a-entity>
<a-entity id="right-controller" oculus-touch-controls="hand: right" page-turner></a-entity>
<!-- text area to print debug text -->
<a-entity id="textArea" position="0 2 -2"
geometry="primitive: plane; width: 3; height: auto"
material="color: #444444;"
text="anchor: center; color: #8888FF; value: debug text here">
</a-entity>
</a-scene>
<script>
var testBook = document.getElementById("interactive-pdf").components["interactive-pdf"];
</script>
</body>
</html>
找到答案here:
requestAnimationFrame doesn't fire when WebXR is engaged
它非常相关,因为 pdf.js
在将图像渲染到 canvas 上时默认使用 requestAnimationFrame
。
虽然可以切换 - pages render function has an option intent
, which is later on used to determine whether to use requestAnimationFrame。
您使用的来源略有不同(较旧?),但如果您在 ProxyPDFPage
的 render
函数中搜索创建 InternalRenderTask
- 都在那里:
const internalRenderTask = new InternalRenderTask({
callback: complete,
params: {
canvasContext,
viewport,
transform,
imageLayer,
background
},
objs: this.objs,
commonObjs: this.commonObjs,
operatorList: intentState.operatorList,
pageIndex: this._pageIndex,
canvasFactory: canvasFactoryInstance,
webGLContext,
useRequestAnimationFrame: renderingIntent !== "print",
pdfBug: this._pdfBug
});
您可以在 renderContext
对象中将 intent
设置为 print:
// 'interactive-pdf'.render()
const renderContext = {
canvasContext: context,
viewport: pageViewport,
intent: "print"
};
const renderTask = page.render(renderContext);
它似乎工作正常:
我不确定,如果将意图设置为 print
不会造成不良影响(它也会影响其他设置),所以在源中禁用 useRequestAnimationFrame
就可以了嗯。
查看 the version with the 'print' intent, or the one with the modified pdfjs source code。
我正在使用 A-Frame 和 pdf.js 创建应用程序以在 VR 中查看 PDF 文件。该应用程序在桌面上按预期工作,包括前进到 PDF 文档的下一页并重新呈现为 canvas.
当 运行 在 VR 中的浏览器中(使用 Quest 2 测试)时,第一次尝试按预期呈现。但是,当将第二个文档页面渲染到 canvas 时,新图像无法出现 ,除非 退出 VR 模式,此时新纹理会按预期出现。
我已经尝试将关联的 material map.needsUpdate
重复设置(在 A-Frame 组件滴答循环中)无效。在试验时,我还注意到,如果您尝试第二次渲染一个新的 PDF 页面,然后第三次渲染另一个页面(在 VR 模式下前进两页),当退出 VR 模式时,只会出现第二次的纹理;第三次渲染的数据似乎丢失了 - 我想知道这是否与主要问题有关。
下面是一个最小示例的代码,http://stemkoski.net/test/quest-pdf-min.html 提供了实时版本。在桌面模式下查看示例时,您可以通过打开浏览器 JavaScript 控制台并输入 testBook.nextPage();
前进到下一页,并看到图像按预期变化。您可以使用 Quest 耳机在 VR 模式下进行测试;按 A 按钮应前进到下一页。
问题是:如何在 A-Frame 的 VR 模式下多次将 PDF 文档的页面呈现为相同 canvas?
<!DOCTYPE html>
<html>
<head>
<title>A-Frame + PDF.js</title>
<script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.6.347/pdf.js"></script>
</head>
<body>
<script>
AFRAME.registerComponent("interactive-pdf", {
schema:
{
fileName: {type: 'string', default:"assets/test10.pdf"},
pageWidth: {type: 'float', default: 1200},
pageHeight: {type: 'float', default: 1500},
},
init: function ()
{
this.displayPlane = document.getElementById("plane1");
this.displayMaterial = this.displayPlane.getObject3D('mesh').material;
this.canvas = this.displayPlane.getAttribute("material").src
this.canvas.setAttribute("width", this.data.pageWidth);
this.canvas.setAttribute("height", this.data.pageHeight);
this.context = this.canvas.getContext('2d');
this.textArea = document.querySelector("#textArea");
this.pdf = null;
this.currentPage = 1;
pdfjsLib.GlobalWorkerOptions.workerSrc = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.6.347/pdf.worker.js";
let self = this;
pdfjsLib.getDocument(this.data.fileName).promise.then( function(pdf) {
self.pdf = pdf;
self.render(1);
});
},
render(pageNumber = 1)
{
if (!this.pdf)
return;
let self = this;
let text = "rendering page " + pageNumber + " of " + this.pdf.numPages + " on canvas";
this.textArea.setAttribute("text", "value", text);
this.pdf.getPage(pageNumber).then( function(page) {
const pageViewport = page.getViewport({ scale: 2 });
const context = self.canvas.getContext("2d");
const renderContext = {
canvasContext: context,
viewport: pageViewport
};
const renderTask = page.render(renderContext);
renderTask.promise.then( function() {
self.displayMaterial.map.needsUpdate = true;
});
});
},
nextPage: function()
{
this.currentPage++;
this.render( this.currentPage );
},
prevPage: function()
{
this.currentPage--;
this.render( this.currentPage );
},
tick: function()
{
// this.displayMaterial.map.needsUpdate = true;
}
});
AFRAME.registerComponent('page-turner', {
init: function ()
{
let bookComponent = document.getElementById("interactive-pdf").components["interactive-pdf"];
this.el.addEventListener('abuttondown', function(event) { bookComponent.nextPage(); } );
}
});
</script>
<a-scene>
<a-assets>
<!-- use to display pdf pages -->
<canvas id="canvas1"></canvas>
</a-assets>
<a-camera></a-camera>
<a-sky color = "#000337"></a-sky>
<a-plane id="plane1" material="shader: flat; src: #canvas1;" position="0 1 -2"></a-plane>
<a-entity id="interactive-pdf" position="0 1 -2"
interactive-pdf="fileName: assets/test10.pdf; pageWidth: 1224; pageHeight: 1584;">
</a-entity>
<a-entity id="left-controller" oculus-touch-controls="hand: left"></a-entity>
<a-entity id="right-controller" oculus-touch-controls="hand: right" page-turner></a-entity>
<!-- text area to print debug text -->
<a-entity id="textArea" position="0 2 -2"
geometry="primitive: plane; width: 3; height: auto"
material="color: #444444;"
text="anchor: center; color: #8888FF; value: debug text here">
</a-entity>
</a-scene>
<script>
var testBook = document.getElementById("interactive-pdf").components["interactive-pdf"];
</script>
</body>
</html>
找到答案here:
requestAnimationFrame doesn't fire when WebXR is engaged
它非常相关,因为 pdf.js
在将图像渲染到 canvas 上时默认使用 requestAnimationFrame
。
虽然可以切换 - pages render function has an option intent
, which is later on used to determine whether to use requestAnimationFrame。
您使用的来源略有不同(较旧?),但如果您在 ProxyPDFPage
的 render
函数中搜索创建 InternalRenderTask
- 都在那里:
const internalRenderTask = new InternalRenderTask({
callback: complete,
params: {
canvasContext,
viewport,
transform,
imageLayer,
background
},
objs: this.objs,
commonObjs: this.commonObjs,
operatorList: intentState.operatorList,
pageIndex: this._pageIndex,
canvasFactory: canvasFactoryInstance,
webGLContext,
useRequestAnimationFrame: renderingIntent !== "print",
pdfBug: this._pdfBug
});
您可以在 renderContext
对象中将 intent
设置为 print:
// 'interactive-pdf'.render()
const renderContext = {
canvasContext: context,
viewport: pageViewport,
intent: "print"
};
const renderTask = page.render(renderContext);
它似乎工作正常:
我不确定,如果将意图设置为 print
不会造成不良影响(它也会影响其他设置),所以在源中禁用 useRequestAnimationFrame
就可以了嗯。
查看 the version with the 'print' intent, or the one with the modified pdfjs source code。