Пожалуйста, ознакомьтесь с обновлением 3 в конце этого сообщения для самой последней активности/плунжера
Я использую AngularJS поверх приложения Node/Express. В этом приложении я использую three.js для отображения 3D-объекта. Текстура, которая обертывает этот объект, изменяется, но имя файла не изменяется (например, цвет изображения текстуры изменяется, но имя файла остается Texture_0.png).
Файл текстуры хранится в хранилище Azure Blob, и у меня есть CORS, поэтому он отображается правильно. Если я обновляю изображение и загружаю его в хранилище Azure Blob в новом сеансе, при первом рендеринге моего 3D-объекта он отображает текстуру с ее изменениями. Тем не менее, в любое время, когда я загружаю новые изменения и пытаюсь повторно отобразить его, отображается изображение в кэше браузера вместо нового изображения, которое хранится и ссылается на сервере. Для просмотра изменений требуется обновление страницы, но мне нужно, чтобы это отображалось, когда я нажимаю кнопку, которая появляется модально, а не обновление страницы.
Как заставить AngularJS отображать серверную версию текстурного изображения вместо кеша браузера без обновления моей страницы?
Я попытался добавить некоторую случайную математику, чтобы заставить браузер загружаться с сервера, но он не работает. Соответствующий код выглядит следующим образом:
Код объекта 3D.JS:
$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);
}
}
Угловая функция для вызова 3D-объекта и поп-музыки в модальном режиме:
$scope.boxPreview = function () {
$scope.generate3D();
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: 'lg'
}
Модально встроено как тег сценария (мне нужно это в представлении, поскольку URL-адреса изображений уникальны, а код для запуска моего представления нуждается в этих переменных, а также в трехмерном объектном коде):
<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, я вижу, что она сбрасывает правильное изображение, но просто не показывает его в Modal. Я попытался отключить кеш, но это не повлияло. Это похоже на three.js где-то держит изображение... но я просто не знаю, как его очистить.
Обновление 2:
Используя рекомендации Mr.doob, перечисленные в этом сообщении, я могу заставить его перезагрузить без обновления страницы один раз, прежде чем я получу следующую ошибку:
RangeError: превышен максимальный размер стека вызовов
Когда я добавляю следующий код, где я создаю экземпляр моего загрузчика:
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
Для получения изображений используется моя общая папка dropbox. Я получаю ту же проблему, где, если я перезаписываю файл текстуры, он не обновляется, когда я выбираю предварительный просмотр окна, если я не обновляю страницу. Мне нужно это, чтобы обновить текстуру окна при щелчке. Несколько критических замечаний:
1) Я консоль регистрировал термин "загруженный", чтобы показать, что это продолжает отображаться даже после закрытия модала. Я не уверен, что мне нужно постоянно визуализировать/анимацию, так как мне просто нужно вращать коробку с помощью OrbitControls. Пожалуйста, дайте мне знать, если есть лучший способ, или если это вызывает проблему кеширования/нам нужно уничтожить сцену/воссоздать ее для обновления окна
2) Используя код, рекомендованный Mr.Doob, я могу обновить изображение на сервере и повторно обработать его один раз, прежде чем он даст мне ошибку:
Uncaught RangeError: превышен максимальный размер стека вызовов
Вот код:
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) Поскольку мы используем общедоступный Dropbox, я не уверен, что те, кто помогает мне, смогут перезаписать файл Texture_0.png с изменениями (я открыл его в краске и просто пропустил некоторые вещи на нем и повторно загрузил в Dropbox), но вы можете изменить URL-адрес на свой собственный Dropbox для тестирования, так как у вас будет возможность загрузить и увидеть, что текстура окна не обновляется, если вы не перезагрузите веб-страницу (plunker).
Если вы редактируете файл изображения позади трех.js и не изменяете имя файла, и хотите обновить текстуру материала, вы можете использовать шаблон, подобный этому:
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, чтобы он мог вернуть файл изображения с тем же именем файла (но с другим изображением) на лету с сервера. Я не рекомендую касаться исходного кода, но в моем случае я добавил следующую строку под функцией загрузки с помощью ImageLoader.js:
url = url + '?' + Math.random();
Для меня это сработало, но опять же, для 99,9% других пользователей из трех. Js это, скорее всего, не применимо.