路径
引用百度百科的解释:
路径通常指存在于多种计算机图形设计软件中的以贝塞尔曲线为理论基础的区域绘制方式。
路径在Canvas、SVG上都有相关定义,一般用来创建形状。在threejs中,也可以用来创建形状,除此之外,还可以用作物体运动的轨迹。下面就说说这两种用法。
在threejs中,Path类代表路径,Path继承自CurvePath(曲线路径),CurvePath继承自Curve(曲线)。CurvePath和Curve都是抽象类,在threejs文档中指出,CurvePath用来连接多条曲线(也就是Curve),其实质上就是一个Curve数组。其实,直接用Path类就可以了,对于CurvePath和Curve,无需关心。
另外还有一个ShapePath类,这个类可以将形状转成一系列路径来表示,比如将svg绘制的图形转成threejs的路径。
使用路径创建形状
关于使用路径创建形状,可以参考【创建平面几何形状一文】,其中的Shape类,就是Path类的子类,这里不多说。
使用路径作为物体运动轨迹
如果我们想要物体沿着某一条曲线运动,有两种办法:
1、算出曲线的公式,动态的计算出物体当前时刻的位置
2、创建一条代表该曲线的路径,然后动态的在该曲线上取点,将其位置赋给物体
一般,路径类会提供多种创建路径的方法,所以,第2种方式通常比较好用。
下面就使用threejs提供的Path类创建一条移动轨迹,然后把相机在这条轨迹上移动。开始的开始,先创建一条路径,如下:
path = new THREE.Path();path.bezierCurveTo(10,100,20,-30,30,19);path.bezierCurveTo(40,-5,50,150,60,-39);path.closePath();
接着在render循环中动态的从路径中取点,设置成相机的位置:
var progress = 0;
function render(){requestAnimationFrame(render);progress += 0.003;let point = path.getPointAt(progress);if(point){camera.position.set(point.x,point.y,300);}else{progress = 0;}renderer.render(scene,camera);
}
上面的progress是比例,范围从0~1.完整示例请看【完整示例】
将svg形状转成路径
svg是一种矢量图形格式,同时,javascript也有创建和修改这种图形格式的接口。各种表示矢量图形的技术,如Canvas、SVG,本质上是相同或相通的,只不过是实现和接口不一样,所以,是有转化的可能的。
在svg中,下面的代码创建了一条从(0,0)到(100,100)的直线路径:
<path d="M0 0 L 100 100"/>
而在threejs中,使用ShapePath创建一条直线路径使用:
let path = new ShapePath();
path.moveTo(0,0);
path.lineTo(100,100);
可见,两者的形式是相近的,从形式上来看,简单的转换难度也不会很大。下面是一个转换的例子:
function createShapes(){let dataString = getSvgData(); //获取svg数据let shape = transformToShapePath(dataString); //转换成ShapePath表示let geometry = new THREE.ShapeGeometry(shape);let material = new THREE.LineBasicMaterial({color:0xff0000});let mesh = new THREE.Line(geometry,material);scene.add(mesh);camera.lookAt(mesh.position);
}//获取svg数据,数据来自w3cschool
function getSvgData(){let svgData = "M153 334 C153 334 151 334 151 334 C151 339 153 344 156 344 C164 344 171 339 171 334 C171 322 164 314 156 314 C142 314 131 322 131 334 C131 350 142 364 156 364 C175 364 191 350 191 334 C191 311 175 294 156 294 C131 294 111 311 111 334 C111 361 131 384 156 384 C186 384 211 361 211 334 C211 300 186 274 156 274";return svgData;
}//转换成ShapePath表示
function transformToShapePath(dataString){let path = new THREE.ShapePath();let dataArr = dataString.split(" ");var currIndex = 0;while(currIndex<dataArr.length){let command = dataArr[currIndex][0];switch(command){case 'M' : {let inc = moveParse(currIndex,dataArr,path);currIndex += inc;break;}case 'C' : {let inc = curveParse(currIndex,dataArr,path);currIndex += inc;break;}default:{//出错处理currIndex++;}}}return path.toShapes()[0];
}/**************下面是各种命令的转换器****************///moveTo命令转换器function moveParse(currIndex,dataArr,path){let paramsLength = 2; //需要的参数个数let data = [dataArr[currIndex].substring(1),dataArr[currIndex+1]];toThreejsCoor(data);path.moveTo(data[0],data[1]);return paramsLength;}//Curve命令转换器function curveParse(currIndex,dataArr,path){let paramsLength = 6; //需要的参数个数let data = [ dataArr[currIndex].substring(1),dataArr[currIndex+1],dataArr[currIndex+2],dataArr[currIndex+3],dataArr[currIndex+4],dataArr[currIndex+5]];toThreejsCoor(data);path.bezierCurveTo(data[0],data[1],data[2],data[3],data[4],data[5]);return paramsLength;}//屏幕坐标转threejs坐标function toThreejsCoor(data){for(let i = 0 ; i < data.length; i += 2){data[i] = data[i] - window.innerWidth/2;data[i+1] = window.innerHeight/2 - data[i+1];}}
上面的代码做了下面这几件事:
- 从svg中提取图形的定义数据,比如<path>标签的d属性
- 将所提取数据中的坐标转换成threejs坐标
- 分析所提取的数据里的操作,用ShapePath执行这些操作
- 调用ShapePath类的toShapes()方法,获得一个Path对象数组
- 用这个Path数组中的对象,创建threejs图形。
这也是将svg转成Path的几个基本步奏。经过转换之后,屏幕上会显示出如下图形:
完整效果,请戳【完整示例】