您的位置:首页 > Web前端 > JavaScript

深入理解three.js对svg的支持(二):SVGRenderer

2017-07-14 20:31 686 查看
前言:SVG作为一种优秀的矢量图形格式在Web得到广泛应用,three.js作为知名的WebGL库自然也对其提供了支持。然而,官方文档中对此的说明十分单薄,网上与此相关的资源也不多。经过多次试验之后,在此分享我的一点理解,包括SVGLoader,SVGObject,SVGRenderer,svg和THREE对象的互相转化等内容。

2 SVGRenderer

上文说到了
SVGRenderer
的机制,那么这一节就来看看源码。源码不长,只有500多行。

2.1 THREE对象转svg

SVGRenderer
代码的核心在
this.render
方法上。渲染的数据从投影得来:

_projector = new THREE.Projector(),

...

_renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements );
_elements = _renderData.elements;
_lights = _renderData.lights;


至于
Projector.js
就复杂多了,单单
projectScene
方法代码的行数就和整个
SVGRenderer.js
一样多,不再深究,有兴趣的自行研读。

_renderData
包含了元素和光照信息。撇开光照不谈,数组
_elements
是核心数据,对其进行遍历渲染。这里将元素分为了3种类型,是根据
Projector.js
制定,但不完全对应。3种类型分别是
THREE.RenderableSprite
THREE.RenderableLine
THREE.RenderableFace
,每个类型的判断中分别调用各自的render函数,如
renderLine
等,各自的render函数大同小异,核心流程和函数定义的伪代码如下:

//遍历元素将数据放入_svg
for e in elements:
if e是3种的某一种:
处理数据
调用自己的render方法

flushPath()//将最后一条路径数据存入_svg,因为addPath机制的原因,最后一条路径不会调用flushPath,因此_currentPath中的数据不会保存到_svg中,得手动来一下

//函数定义
3种render:
将数据按照svg path格式编码到path、处理style;
addPath(style,path);

addPath:
if style == _currentStyle:
path放入_currentPath;
else:
flushPath();
清空_currentPath和_currentStyle;

flushPath():
_currentPath和_currentStyle数据放入_svg;
清空_currentPath和_currentStyle;


注意到这里有一个_svg对象,它在一开始就被定义为DOM元素,所有的内容都在此对象下显示,其构建完成之后就是一个完整的svg图像。

SVGRenderer
的功能还是很强大的,可以将视口中的三维物体转为二维svg显示(统一使用
<path>
编码)。如官方的例子就是将一组动态的
THREE.Line
转为了svg的path在SVGRenderer中显示。

2.2 SVGObject

等等,前面的这些都是三维对象转svg的例子,那么
SVGObject
是怎样在
SVGRenderer
中显示的呢?

其实
SVGRenderer.js
的源码首先就给出了
SVGObject
的定义:

THREE.SVGObject = function ( node ) {

THREE.Object3D.call( this );

this.node = node;

};

THREE.SVGObject.prototype = Object.create( THREE.Object3D.prototype );
THREE.SVGObject.prototype.constructor = THREE.SVGObject;


可见
SVGObject
确实是继承自
Object3D
,但是注意构造函数,有个传入参数
node
,并且还动态扩展了一个属性
this.node
,这个就是SVG文件的根节点啊,有种不好的预感……



先不管,看它是如何渲染的呢?render的最后有这么一段代码:

scene.traverseVisible( function ( object ) {

if ( object instanceof THREE.SVGObject ) {

_vector3.setFromMatrixPosition( object.matrixWorld );
_vector3.applyMatrix4( _viewProjectionMatrix );

var x =   _vector3.x * _svgWidthHalf;
var y = - _vector3.y * _svgHeightHalf;

var node = object.node;
node.setAttribute( 'transform', 'translate(' + x + ',' + y + ')' );

_svg.appendChild( node );

}

} );


原来渲染流程的最后会通过遍历查找scene中的SVGObject对象,单独处理SVGObject!对,2.1小节部分的render代码你怎么改都和SVGObject的显示没关系……,读取其node——合着是把我们的svg图像放到了_svg里,再放到DOM里而已,纯粹是多套了一层啊——坑爹呢是!



怪不得不能用变换,因为数据都在node属性里,又没有提供任何操作node的方法,你变transform和这个扩展出来的node没有任何关系。想变的话。。。改源代码吧——但又回到svg蛋疼的变换上去了。(我想静静)

下篇中,将对svg于THREE对象的关系进行进一步的探索。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  webgl svg three-js