前言:SVG作为一种优秀的矢量图形格式在Web得到广泛应用,three.js作为知名的WebGL库自然也对其提供了支持。然而,官方文档中对此的说明十分单薄,网上与此相关的资源也不多。经过多次试验之后,在此分享我的一点理解,包括SVGLoader,SVGObject,SVGRenderer,svg和THREE对象的互相转化等内容。
1 使用SVGLoader载入svg图像
常规显示svg图像,只需要在DOM中直接添加<svg>
节点就行了,为何还要绕弯使用three.js的相关组件对其进行显示呢?本文中或许有答案。下面来看看如何实现吧。首先把依赖的文件都包含进来:
<script src="three.js/build/three.min.js"></script>
<script src="three.js/examples/js/renderers/SVGRenderer.js"></script>
<script src="three.js/examples/js/renderers/Projector.js"></script>
<script src="three.js/examples/js/loaders/SVGLoader.js"></script>
然后再正式干活。主要分2步:
- 调用SVGLoader读取svg图像到内存
- 使用SVGRenderer渲染svg对象
下面来分别对这两步进行说明
1.1 读取svg图像
参见官方文档。
首先,SVGLoader
的源码就几行,看不出背后的内容;其次,官方文档只是简单介绍了SVGLoader
的属性和方法,没有案例。至于官方关于svg的只有SVGRenderer
几个案例,其所渲染的图形数据却并不是svg图像,而是THREE
的图形对象——这样就没什么参考意义了。
SVGLoader
的构造函数只有一个参数manager
,并且有默认值为THREE.DefaultLoadingManager
。这个manager的主要作用不是处理数据,而是在loader处理数据的过程中显示信息。因此除非你有要定制的信息,不然使用默认值就好。
然后看SVGLoader
的方法,只有一个.load( url, onLoad, onProgress, onError )
。4个参数中,后边两个是载入中和出错时要调用的函数,不是很重要,一般就用无名函数在控制台输出信息,也可以省略。前两个参数比较重要:
url
:要载入的svg的url,字符串类型。必须,可以是本地路径(chrome无法读取本地文件的解决方案见1,或者换Firefox吧);onLoad
:载入完成时候调用的函数。此函数必须有一个参数,这个参数将会是一个SVG Document
,当然是可以直接嵌入DOM的。例如下面的代码可以直接将svg放入DOM中显示。然而这样操作的话,如果url是超链接不如直接在DOM下面添加,如果是本地文件不如直接用js读,多此一举没什么意义,我只是举个栗子~
//此函数作为onLoad被传入.load
function(svgObj){
//.load之后要载入的svg就被放入了svgObj中
document.body.appenChild(svgObj);
}
使用SVGLoader
载入svg文件完整的代码如下(此代码源自2)
var svgManager = new THREE.SVGLoader();
//var url = 'https://upload.wikimedia.org/wikipedia/commons/b/b0/NewTux.svg';
var url = 'tst.svg';
function svg_loading_done_callback(doc) {
init(new THREE.SVGObject(doc));
render();
};
svgManager.load(url,
svg_loading_done_callback,
function(){console.log("Loading SVG...");},
function(){console.log("Error loading SVG!");
});
.load
将svg图像读取到doc
变量。要注意的是在回调函数init()
的参数部分,SVG Document
类型的doc
变量被传入了THREE.SVGObject()
构造函数的参数中,从而将svg对象变为了THREE.SVGObject
。(控制台输出了这个对象的信息,发现它type
是Object3D
,但有一些问题,下文再说)
1.2 在SVGRenderer中显示svg
three.js提供了好几种renderer,比如WebGLRenderer
,SVGRenderer
,CanvasRenderer
,CSS3DRenderer
等。如果使用WebGLRenderer渲染svg显然不是一个好选择,因为要涉及到Shader
相关的内容,实现起来会很复杂;而用CanvasRenderer
去渲染svg显然是费力不讨好的事,丢掉了svg的特性。CSS3DRenderer
渲染svg图像在放大时可能会出现锯齿3,所以SVGRenderer
看起来是坠吼的选择。这里先用SVGRenderer
渲染svg图像(代码同样修改自2),其具体的特性和CanvasRenderer
后文再说。
var camera, scene, renderer;
function init(svgObject) {
camera = new THREE.PerspectiveCamera(75, window.innerWidth window.innerHeight, 1, 10000);
camera.position.z = 500;
scene = new THREE.Scene();
//set svgObject transform ---- invalid :(
//svgObject.position.x = 10;
//svgObject.rotation.z += 90/180*Math.PI;
console.log(svgObject);
scene.add(svgObject);
renderer = new THREE.SVGRenderer();
renderer.setClearColor(0xf0f0f0);
renderer.setSize(window.innerWidth, window.innerHeight);
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 render() {
renderer.render(scene, camera);
}
上一小节最后提到SVGObject有些问题:虽然它是Object3D类型,但是使用修改诸如.rotation.z
等属性的方法进行旋转等空间变换操作只是改变了相应属性的值,并没有真的应用到svg图像的空间变换上!这就麻烦了,即使作为子物体放到group里,修改group的变换也没用,这个是SVGRenderer解析SVGObject的固有机制导致的,咱们下文再讲。
既然不能通过Object3D的方法修改svg的空间变换,那导进来的意义就不大了啊……svg最蛋疼的就是原生的空间变换中心是左上角(0,0)点,想以自身中心为基准变换要么先平移后变换再平移,这样非常麻烦;要么用CSS进行变换,也不是十分方便。现在three的变换这条路也给断了,sigh(然而后边还有一些补救的方法,咱们以后再讲)
1.3 参考资料
1 http://blog.csdn.net/dandanzmc/article/details/31344267/
2 https://stackoverflow.com/questions/33140342/how-to-load-svg-file-into-svgrenderer-in-three-js
3 https://github.com/mrdoob/three.js/issues/4429#issuecomment-41985025