天天看點

threejs路徑路徑使用路徑建立形狀使用路徑作為物體運動軌迹将svg形狀轉成路徑

路徑

引用百度百科的解釋:

路徑通常指存在于多種計算機圖形設計軟體中的以貝塞爾曲線為理論基礎的區域繪制方式。
           

路徑在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(,,,-,,);
       path.bezierCurveTo(,-,,,,-);
       path.closePath();
           

接着在render循環中動态的從路徑中取點,設定成相機的位置:

var progress = ;
function render(){
  requestAnimationFrame(render);
        progress += ;
        let point = path.getPointAt(progress);
        if(point){
          camera.position.set(point.x,point.y,);
        }else{
          progress = ;
        }
        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(,);
path.lineTo(,);
           

可見,兩者的形式是相近的,從形式上來看,簡單的轉換難度也不會很大。下面是一個轉換的例子:

function createShapes(){
   let dataString = getSvgData();  //擷取svg資料
   let shape = transformToShapePath(dataString); //轉換成ShapePath表示
   let geometry = new THREE.ShapeGeometry(shape);
   let material = new THREE.LineBasicMaterial({color:});
   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 = ;
        while(currIndex<dataArr.length){
            let command = dataArr[currIndex][];
            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()[];
}


/**************下面是各種指令的轉換器****************/
        //moveTo指令轉換器
        function moveParse(currIndex,dataArr,path){
           let paramsLength = ; //需要的參數個數
           let data = [dataArr[currIndex].substring(),dataArr[currIndex+]];
           toThreejsCoor(data);
           path.moveTo(data[],data[]);
           return paramsLength;
        }

        //Curve指令轉換器
        function curveParse(currIndex,dataArr,path){
          let paramsLength = ; //需要的參數個數
          let data = [ 
               dataArr[currIndex].substring(),
               dataArr[currIndex+],
               dataArr[currIndex+],
               dataArr[currIndex+],
               dataArr[currIndex+],
               dataArr[currIndex+]
            ];
          toThreejsCoor(data);
          path.bezierCurveTo(data[],data[],data[],data[],data[],data[]);
          return paramsLength;
        }

        //螢幕坐标轉threejs坐标
        function toThreejsCoor(data){
          for(let i =  ; i < data.length; i += ){
             data[i] = data[i] - window.innerWidth/;
             data[i+] = window.innerHeight/ - data[i+];
          }
        }
           

上面的代碼做了下面這幾件事:

  • 從svg中提取圖形的定義資料,比如<path>标簽的d屬性
  • 将所提取資料中的坐标轉換成threejs坐标
  • 分析所提取的資料裡的操作,用ShapePath執行這些操作
  • 調用ShapePath類的toShapes()方法,獲得一個Path對象數組
  • 用這個Path數組中的對象,建立threejs圖形。

這也是将svg轉成Path的幾個基本步奏。經過轉換之後,螢幕上會顯示出如下圖形:

threejs路徑路徑使用路徑建立形狀使用路徑作為物體運動軌迹将svg形狀轉成路徑

完整效果,請戳【完整示例】

繼續閱讀