【问题标题】:Canvas (Three.js) scene with Angular带有 Angular 的 Canvas (Three.js) 场景
【发布时间】:2015-09-12 05:14:26
【问题描述】:

我正在 Angular SPA 中构建一系列 Web 应用程序。这些应用程序中的每一个都位于一个 Angular 模板中,它们之间的导航会剥离和替换适当的 DOM 元素并从控制器运行脚本。

我的问题更笼统:在离开 Three.js 场景并返回时,显然 DOM 元素和脚本本身会重新运行,从而导致场景从“scratch”开始。有没有办法让场景在后台运行,以便用户可以导航回应用程序并让它就在他们离开的地方?

这甚至是可取的吗?我知道当用户导航到另一个选项卡时 requestAnimationFrame 方法会停止动画,但这在 Angular 上下文中如何工作?

例如,here's the WIP web page。尝试离开 StarFinder 选项卡然后返回,您就会明白我的意思了。

编辑Here's a JSFiddle 来说明我的意思(在这种情况下,它会在没有 Angular 的情况下修改 DOM)。

真的,有问题的是渲染器,它会在返回时重新启动:

function render() {
        requestAnimationFrame(render);
        cube.rotation.x += 0.01;
        cube.rotation.y += 0.01;
        renderer.render(scene, camera);
    }
render();

【问题讨论】:

  • 请在此处添加有意义的代码和问题描述。不要只链接到需要修复的站点 - 否则,一旦问题得到解决,这个问题将对未来的访问者失去任何价值。发布 Short, Self Contained, Correct Example (SSCCE) 来证明您的问题将帮助您获得更好的答案。有关详细信息,请参阅 Something on my web site doesn't work. Can I just paste a link to it? 谢谢!
  • @Cerbrus 链接的站点不是“需要修复”的站点,这个问题比基于特定代码的问题更笼统/理论上。你想让我怎么做?
  • 当您解决问题时,您问题中的链接将失去其价值,因此它对未来的访问者没有帮助。你能就这个问题发表一个 SSCCE 吗?
  • @Cerbrus Point 已采取,但由于问题的性质,任何示例都不会很简单。我将在 JSFiddle 中组合一个。
  • @Cerbrus 我已经用 JSFiddle 更新了原始问题。

标签: javascript jquery angularjs canvas three.js


【解决方案1】:

重新编译模板时角度破坏 DOM,THREE.js 使用绑定到画布的 webGL 渲染器。

如果您一次只有一个用于 THREE.js 的画布渲染,我建议使用外部画布并在创建时将其移动到指令中,并在移除时移回静态容器(使用 $destroy)

试试这个:

创建一个指令来监听显示/隐藏事件的发射并创建一个功能性 3d 画布

然后将该指令选择器放置在更改模板之外的 DOM 中

喜欢这个

app.directive('threejsCanvas', function directive() {
  return {
    link: function(scope){
      var scene = new THREE.Scene();
      
      // create a temp holder to save the canvas outside of canvas
      var tempHolder = document.createElement('div');
      tempHolder.style.display = 'none';
      window.document.body.appendChild(tempHolder);
      
      var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
      var renderer = new THREE.WebGLRenderer();
      document.body.appendChild( renderer.domElement );
      
      scope.$on('start:webgl', function(view, callback) {
        view.appendChild(renderer.domElement);
        
        // you may do somthing like ...
        // renderer.setSize( view.innerWidth, view.innerHeight );
        // and change camera ratio if it have to change
        
        // pass the arguments to the caller's callback so the script can interact with the canvas singleton
        callback({
          scene: scene,
          camera: camera,
          renderer: renderer
        });
      });  
      
      scope.$on('$destroy', function() {
        // move canvas away from template so it's not destroyed
        tempHolder.appendChild(renderer.domElement);
      });
    }
  };
});
<div class="container">
  <threejs-canvas></threejs-canvas>
  <div ng-view></div>
</div>

因此您只需从控制器发出您希望画布在其中呈现的元素并传递回调以获取 THREE.js 对象

【讨论】:

  • 我也喜欢这种方法。也许您可以将它与我的解决方案结合起来,并在它位于 tempHolder 时暂停渲染,以保持网站上的性能。
【解决方案2】:

如果您使用的是 ui-router,您可以在嵌套视图中加载站点的其余部分,然后只需在包含动画的父视图中隐藏 div,而无需实际卸载它。将requestAnimationFrame(render); 放在检查布尔标志的if 语句中。然后在您的路线更改时更新该标志。请参阅我的回答 here 以了解如何检测路由更改。如果您正在导航您的场景,那么还请调用render() 以再次启动动画。

另一个选项可能是允许它卸载,但将您的动画的任何大型资产和/或设置保存在 localStorage 中,这样您的启动时间会更短。

【讨论】:

  • 我想无缝重新进入动画,所以我会看看你的第一个建议。干杯。
猜你喜欢
  • 2019-02-16
  • 2013-05-05
  • 2015-05-09
  • 2018-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-02
相关资源
最近更新 更多