Angular 或 Three.js 不是从服务器中提取图像,而是从浏览器缓存中提取图像
Angular or Three.js not pulling image from server, but rather browser cache
请参阅本文末尾的更新 3 post 了解最新的 activity/plunker
我在 Node/Express 应用程序之上使用 AngularJS。在此应用程序中,我使用 three.js 来显示 3D 对象。包裹此对象的纹理发生变化,但文件名没有变化(例如,纹理图像的颜色发生变化但文件名保持不变 Texture_0.png)。
纹理文件存储在 Azure Blob 存储中,我启用了 CORS,所以它可以正常显示。如果我更新图像并在新会话中将其上传到 Azure Blob 存储,我第一次渲染 3D 对象时它会显示纹理及其变化。但是,每当我上传新更改并尝试重新渲染时,它都会显示浏览器缓存的图像,而不是服务器中存储和引用的新图像。需要刷新页面才能看到更改,但我需要在单击弹出模式的按钮而不是页面刷新后显示它。
如何让 AngularJS 在不刷新页面的情况下显示服务器版本的纹理图像而不是浏览器缓存?
我尝试添加一些随机数学来强制浏览器从服务器下载,但它不起作用。相关代码如下:
Three.JS 3D 对象代码:
$scope.generate3D = function () {
// 3D OBJECT - Variables
var texture0 = baseBlobURL + 'Texture_0.png?' + Math.random();
var boxDAE = baseBlobURL + 'Box.dae?' + Math.random();
var scene;
var camera;
var renderer;
var box;
var controls;
var newtexture;
// 3D OBJECT - Generate
newtexture = THREE.ImageUtils.loadTexture(texture0);
//Instantiate a Collada loader
var loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true;
loader.load(boxDAE, function (collada) {
box = collada.scene;
box.traverse(function (child) {
if (child instanceof THREE.SkinnedMesh) {
var animation = new THREE.Animation(child, child.geometry.animation);
animation.play();
}
});
box.scale.x = box.scale.y = box.scale.z = .2;
box.updateMatrix();
init();
animate();
});
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xdddddd);
renderer.setSize(500, 500);
// Load the box file
scene.add(box);
// Lighting
var light = new THREE.AmbientLight();
scene.add(light);
// Camera
camera.position.x = 40;
camera.position.y = 40;
camera.position.z = 40;
camera.lookAt(scene.position);
// Rotation Controls
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.rotateSpeed = 5.0;
controls.zoomSpeed = 5;
controls.noZoom = false;
controls.noPan = false;
var myEl = angular.element(document.querySelector('#webGL-container'));
myEl.append(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
}
Angular调用Modal中的3D对象和弹出函数:
$scope.boxPreview = function () {
$scope.generate3D();
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: 'lg'
}
作为脚本标记嵌入的模态(我需要在视图中使用它,因为图像 URL 是唯一的并且 运行 我的视图的代码需要这些变量以及 3D 对象代码):
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header">
<h3 class="modal-title">Box Preview</h3>
</div>
<div class="modal-body">
<div id="webGL-container" width="500px">
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" data-ng-click="ok()">Close</button>
</div>
</script>
更新 1:
在 Chrome 中查看网络选项卡时,我可以看到它正在下拉正确的图像,但没有在模式中显示它。我尝试禁用缓存,但这没有效果。就像 three.js 将图像保存在某处...但我只是不知道如何清除它。
更新二:
使用 Mr.doob listed in this post 的建议,在出现以下错误之前,我无需刷新页面就可以重新加载它:
RangeError: Maximum call stack size exceeded
当我将以下代码添加到我实例化加载程序的位置时:
THREE.ImageLoader.prototype._load = THREE.ImageLoader.prototype.load;
THREE.ImageLoader.prototype.load = function (url, onLoad, onProgress, onError) {
this._load(url + '?' + Math.random(), onLoad, onProgress, onError);
};
//Instantiate a Collada loader
var loader = new THREE.ColladaLoader();
事件顺序如下:
1) 在新会话中首次单击以显示 3D 框时,更改显示
2) 更改贴图并上传,然后在同一会话中点击显示3D框,显示更改。以前,它只有在刷新页面后才会显示。
3) 更改纹理并上传,然后单击以在同一会话中显示 3D 框。 RangeError:超出最大调用堆栈大小
需要说明的是,我在模式中打开它。并且看到模态生成这些错误的问题四处搜索,除了我在路由级别只保留一个控制器调用。
更新 3:
我整理了以下内容:
http://plnkr.co/edit/DGZA9LUBZWsLWrmXaaKD
它正在使用我的保管箱 public 文件夹来获取图像。我遇到同样的问题,如果我覆盖纹理文件,除非我刷新页面,否则当我 select 框预览时它不会更新。我需要这个来在点击时刷新盒子纹理。一些重要说明:
1) 我控制台记录了术语 "loaded" 以表明即使在模态框关闭后它仍会继续呈现。我不确定是否需要连续 render/animate,因为我只需要使用 OrbitControls 来旋转盒子。请让我知道是否有更好的方法或者这是否导致缓存 issue/we 需要销毁 scene/recreate 它以便框更新
2) 使用 Mr.Doob 推荐的代码,我可以更新服务器上的图像并让它重新正确渲染一次,然后它会给我一个错误:
Uncaught RangeError: Maximum call stack size exceeded
代码如下:
THREE.ImageLoader.prototype._load = THREE.ImageLoader.prototype.load;
THREE.ImageLoader.prototype.load = function (url, onLoad, onProgress, onError) {
this._load(url + '?' + Math.random(), onLoad, onProgress, onError);
};
我保留了这段代码,因为它在崩溃前至少达到了我想要的一次。
3) 因为我们使用的是 public 保管箱,所以我不确定那些帮助我的人是否能够用更改覆盖 Texture_0.png 文件(我在 Paint 中打开了它,只是涉足了一些上的东西并重新上传到保管箱),但您可以将 URL 更改为您自己的保管箱以进行测试,因为您可以上传并看到该框纹理不会刷新,除非您重新加载网页(plunker)。
如果您正在 three.js 的背后编辑图像文件并且不更改文件名,并且您想刷新 material 纹理,您可以使用这样的模式:
mesh.material.map.dispose(); // unrelated, but important!
mesh.material.map = THREE.ImageUtils.loadTexture( "filename.jpg" + "?" + Math.random() );
如果没有动画循环,则需要强制重新渲染,如下所示:
mesh.material.map.dispose(); // unrelated, but important!
mesh.material.map = THREE.ImageUtils.loadTexture( "filename.jpg" + "?" + Math.random(), undefined, render );
three.js r.72
@weslangley 的回答在技术上对 three.js 的大多数用户都是正确的,因为他们使用材料。在我非常独特的情况下,我有一个不同的 Collada 文件,我必须在其中编辑 ImageLoader.js 文件才能将其转换为 return 具有相同文件名(但图像内容不同)的图像文件从服务器飞。 我不建议触摸源代码,但在我的例子中,我在带有 ImageLoader.js 的加载函数下添加了以下行:
url = url + '?' + Math.random();
对我来说这很有效,但同样,对于 three.js 的 99.9% 的其他用户来说这可能不适用。
请参阅本文末尾的更新 3 post 了解最新的 activity/plunker
我在 Node/Express 应用程序之上使用 AngularJS。在此应用程序中,我使用 three.js 来显示 3D 对象。包裹此对象的纹理发生变化,但文件名没有变化(例如,纹理图像的颜色发生变化但文件名保持不变 Texture_0.png)。
纹理文件存储在 Azure Blob 存储中,我启用了 CORS,所以它可以正常显示。如果我更新图像并在新会话中将其上传到 Azure Blob 存储,我第一次渲染 3D 对象时它会显示纹理及其变化。但是,每当我上传新更改并尝试重新渲染时,它都会显示浏览器缓存的图像,而不是服务器中存储和引用的新图像。需要刷新页面才能看到更改,但我需要在单击弹出模式的按钮而不是页面刷新后显示它。
如何让 AngularJS 在不刷新页面的情况下显示服务器版本的纹理图像而不是浏览器缓存?
我尝试添加一些随机数学来强制浏览器从服务器下载,但它不起作用。相关代码如下:
Three.JS 3D 对象代码:
$scope.generate3D = function () {
// 3D OBJECT - Variables
var texture0 = baseBlobURL + 'Texture_0.png?' + Math.random();
var boxDAE = baseBlobURL + 'Box.dae?' + Math.random();
var scene;
var camera;
var renderer;
var box;
var controls;
var newtexture;
// 3D OBJECT - Generate
newtexture = THREE.ImageUtils.loadTexture(texture0);
//Instantiate a Collada loader
var loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true;
loader.load(boxDAE, function (collada) {
box = collada.scene;
box.traverse(function (child) {
if (child instanceof THREE.SkinnedMesh) {
var animation = new THREE.Animation(child, child.geometry.animation);
animation.play();
}
});
box.scale.x = box.scale.y = box.scale.z = .2;
box.updateMatrix();
init();
animate();
});
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xdddddd);
renderer.setSize(500, 500);
// Load the box file
scene.add(box);
// Lighting
var light = new THREE.AmbientLight();
scene.add(light);
// Camera
camera.position.x = 40;
camera.position.y = 40;
camera.position.z = 40;
camera.lookAt(scene.position);
// Rotation Controls
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.rotateSpeed = 5.0;
controls.zoomSpeed = 5;
controls.noZoom = false;
controls.noPan = false;
var myEl = angular.element(document.querySelector('#webGL-container'));
myEl.append(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
}
Angular调用Modal中的3D对象和弹出函数:
$scope.boxPreview = function () {
$scope.generate3D();
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: 'lg'
}
作为脚本标记嵌入的模态(我需要在视图中使用它,因为图像 URL 是唯一的并且 运行 我的视图的代码需要这些变量以及 3D 对象代码):
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header">
<h3 class="modal-title">Box Preview</h3>
</div>
<div class="modal-body">
<div id="webGL-container" width="500px">
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" data-ng-click="ok()">Close</button>
</div>
</script>
更新 1:
在 Chrome 中查看网络选项卡时,我可以看到它正在下拉正确的图像,但没有在模式中显示它。我尝试禁用缓存,但这没有效果。就像 three.js 将图像保存在某处...但我只是不知道如何清除它。
更新二:
使用 Mr.doob listed in this post 的建议,在出现以下错误之前,我无需刷新页面就可以重新加载它:
RangeError: Maximum call stack size exceeded
当我将以下代码添加到我实例化加载程序的位置时:
THREE.ImageLoader.prototype._load = THREE.ImageLoader.prototype.load;
THREE.ImageLoader.prototype.load = function (url, onLoad, onProgress, onError) {
this._load(url + '?' + Math.random(), onLoad, onProgress, onError);
};
//Instantiate a Collada loader
var loader = new THREE.ColladaLoader();
事件顺序如下:
1) 在新会话中首次单击以显示 3D 框时,更改显示
2) 更改贴图并上传,然后在同一会话中点击显示3D框,显示更改。以前,它只有在刷新页面后才会显示。
3) 更改纹理并上传,然后单击以在同一会话中显示 3D 框。 RangeError:超出最大调用堆栈大小
需要说明的是,我在模式中打开它。并且看到模态生成这些错误的问题四处搜索,除了我在路由级别只保留一个控制器调用。
更新 3:
我整理了以下内容:
http://plnkr.co/edit/DGZA9LUBZWsLWrmXaaKD
它正在使用我的保管箱 public 文件夹来获取图像。我遇到同样的问题,如果我覆盖纹理文件,除非我刷新页面,否则当我 select 框预览时它不会更新。我需要这个来在点击时刷新盒子纹理。一些重要说明:
1) 我控制台记录了术语 "loaded" 以表明即使在模态框关闭后它仍会继续呈现。我不确定是否需要连续 render/animate,因为我只需要使用 OrbitControls 来旋转盒子。请让我知道是否有更好的方法或者这是否导致缓存 issue/we 需要销毁 scene/recreate 它以便框更新
2) 使用 Mr.Doob 推荐的代码,我可以更新服务器上的图像并让它重新正确渲染一次,然后它会给我一个错误:
Uncaught RangeError: Maximum call stack size exceeded
代码如下:
THREE.ImageLoader.prototype._load = THREE.ImageLoader.prototype.load;
THREE.ImageLoader.prototype.load = function (url, onLoad, onProgress, onError) {
this._load(url + '?' + Math.random(), onLoad, onProgress, onError);
};
我保留了这段代码,因为它在崩溃前至少达到了我想要的一次。
3) 因为我们使用的是 public 保管箱,所以我不确定那些帮助我的人是否能够用更改覆盖 Texture_0.png 文件(我在 Paint 中打开了它,只是涉足了一些上的东西并重新上传到保管箱),但您可以将 URL 更改为您自己的保管箱以进行测试,因为您可以上传并看到该框纹理不会刷新,除非您重新加载网页(plunker)。
如果您正在 three.js 的背后编辑图像文件并且不更改文件名,并且您想刷新 material 纹理,您可以使用这样的模式:
mesh.material.map.dispose(); // unrelated, but important!
mesh.material.map = THREE.ImageUtils.loadTexture( "filename.jpg" + "?" + Math.random() );
如果没有动画循环,则需要强制重新渲染,如下所示:
mesh.material.map.dispose(); // unrelated, but important!
mesh.material.map = THREE.ImageUtils.loadTexture( "filename.jpg" + "?" + Math.random(), undefined, render );
three.js r.72
@weslangley 的回答在技术上对 three.js 的大多数用户都是正确的,因为他们使用材料。在我非常独特的情况下,我有一个不同的 Collada 文件,我必须在其中编辑 ImageLoader.js 文件才能将其转换为 return 具有相同文件名(但图像内容不同)的图像文件从服务器飞。 我不建议触摸源代码,但在我的例子中,我在带有 ImageLoader.js 的加载函数下添加了以下行:
url = url + '?' + Math.random();
对我来说这很有效,但同样,对于 three.js 的 99.9% 的其他用户来说这可能不适用。