如何在three.js中将SVG文件加载到SVGRenderer中

7

我正在尝试在three.js(http://threejs.org/examples/#svg_sandbox)中使用SVGRenderer。示例向您展示如何动态创建SVG元素(一个圆)。我想导入已经存在于我的计算机上的SVG文件。我该怎么做?

createElementNS命令似乎不支持导入SVG文件?

实际上,我希望将我的image.svg显示在three.js场景中。


我想和SVG元素进行交互,例如:让它们动起来或者改变它们的颜色等。 - therealtypon
我在文档中找不到svgRenderer,但是有一个svgLoader可以解析外部SVG文件。 - Kaiido
SVGLoader 返回一个 SVGDocument。我试图将其传递给 SVGRenderer,但它报错了。我需要以某种方式将该 SVGDocument 转换为元素吗? - therealtypon
然后尝试SVGDocument.documentElement。 - Kaiido
我刚试过了,传入 SVGLoader 构造函数的 load 回调中的对象实际上是一个 svg 元素。因此,您可以直接将它传递到 SVGObject 构造函数中:function svg_loading_done_callback(svgObject){ var object = new THREE.SVGObject( svgObject); doSomethingWith(object)} - Kaiido
显示剩余4条评论
2个回答

13
您可以使用THREE.SVGLoader()库来实现此目的:

var svgManager = new THREE.LegacySVGLoader();
var url = 'https://upload.wikimedia.org/wikipedia/commons/b/b0/NewTux.svg';

function svg_loading_done_callback(doc) {
  init(new THREE.SVGObject(doc));
  animate();
};

svgManager.load(url,
  svg_loading_done_callback,
  function() {
    console.log("Loading SVG...");
  },
  function() {
    console.log("Error loading SVG!");
  });

var camera, scene, renderer;

function init(svgObject) {
  camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 100000);
  camera.position.z = 1500;

  svgObject.position.x = Math.random() * innerWidth;
  svgObject.position.y = 200;
  svgObject.position.z = Math.random() * 10000 - 5000;
  svgObject.scale.x = svgObject.scale.y = svgObject.scale.z = 0.01;

  scene = new THREE.Scene();
  scene.add(svgObject);

  var ambient = new THREE.AmbientLight(0x80ffff);
  scene.add(ambient);
  var directional = new THREE.DirectionalLight(0xffff00);
  directional.position.set(-1, 0.5, 0);
  scene.add(directional);
  renderer = new THREE.SVGRenderer();
  renderer.setClearColor(0xf0f0f0);
  renderer.setSize(window.innerWidth, window.innerHeight - 5);
  document.body.appendChild(renderer.domElement);

  window.addEventListener('resize', onWindowResize, false);
}

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate() {
  requestAnimationFrame(animate);
  render();
}

function render() {
  var time = Date.now() * 0.0002;
  camera.position.x = Math.sin(time) * 200;
  camera.position.z = Math.cos(time) * 200;
  camera.lookAt(scene.position);
  renderer.render(scene, camera);
}
* {
  margin: 0
}
<script src="http://threejs.org/build/three.min.js"></script>
<script src="http://threejs.org/examples/js/renderers/SVGRenderer.js"></script>
<script src="http://threejs.org/examples/js/renderers/Projector.js"></script>
<script src="http://threejs.org/examples/js/loaders/SVGLoader.js"></script>
<script>
  /**
   * @name LegacySVGLoader
   * @author mrdoob / http://mrdoob.com/
   * @author zz85 / http://joshuakoo.com/
   * @see https://github.com/mrdoob/three.js/issues/14387
   */

  THREE.LegacySVGLoader = function(manager) {

    this.manager = (manager !== undefined) ? manager : THREE.DefaultLoadingManager;

  };

  THREE.LegacySVGLoader.prototype = {

    constructor: THREE.LegacySVGLoader,

    load: function(url, onLoad, onProgress, onError) {

      var scope = this;

      var parser = new DOMParser();

      var loader = new THREE.FileLoader(scope.manager);
      loader.load(url, function(svgString) {

        var doc = parser.parseFromString(svgString, 'image/svg+xml'); // application/xml

        onLoad(doc.documentElement);

      }, onProgress, onError);

    }

  };
</script>


可以了...哎。原来是我的SVGLoader.js的问题。我从three.js的github上下载了它,一旦我用你提供的直接从网站上复制的代码替换了它,它就好了。非常感谢,你太棒了。 - therealtypon
1
我有一个问题,希望你能帮我解答:如何通过three.js访问此svg对象的子级?例如,如果我想更改NewTux的喙的颜色,比如#path136,我该如何选择该子元素?我似乎无法使用标准的Jquery方法。 - therealtypon
所以我尝试了这个: var pathobj = svgObject.node.getElementById("path136"); pathobj.setAttribute('opacity', 0); 看起来好像可以工作。但是这是否由SVGRenderer通过three.js处理呢? 你认为这样做是正确的方式吗? - therealtypon
1
加载的SVG文档未附加到DOM中。要访问它,您必须保留其ID的引用或为其命名,然后使用scene.getObjectById(savedId).nodescene.getObjectByName(setName).node。似乎SVGRenderer在每次绘制时更新svg图像,因此您实际上可以在流程中修改svg的子元素,并直接更新它。 - Kaiido
SVGLoader已经更改,现在提供的是路径数组,而不是像示例中显示的doc。SVGObject不再可用。jsfiddle也无法工作。如果SVG中嵌入了图像URL,则SVGLoader不起作用,也不会报错。有任何建议吗? - lifetimeLearner007
显示剩余7条评论

2

这是来自最新的three.js库的当前答案:

https://threejs.org/examples/?q=svg#webgl_loader_svg

但是该代码将SVG加载到几何体网格中,因此它不是DOM元素,就像任何其他3D对象一样进行缩放。 它就像3D场景中的广告牌。 如果有人需要这种行为,那么没问题。

如果您想将SVG加载为DOM元素,使其悬浮在3D场景上方,并能够添加鼠标单击和悬停事件……我要求提供示例,这里就是它:

https://github.com/mrdoob/three.js/issues/15528


谢谢您的回复!您是否知道SVG嵌入式样式(特别是动画)是否可以使用SVGLoader和SVGRenderer工作?这不是本地SVG动画(不确定是否支持)。或者我应该单独提问吗? - Igor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接