真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Three.js開(kāi)發(fā)實(shí)現(xiàn)3D地圖的實(shí)踐過(guò)程總結(jié)

前言

成都創(chuàng)新互聯(lián)網(wǎng)站建設(shè)提供從項(xiàng)目策劃、軟件開(kāi)發(fā),軟件安全維護(hù)、網(wǎng)站優(yōu)化(SEO)、網(wǎng)站分析、效果評(píng)估等整套的建站服務(wù),主營(yíng)業(yè)務(wù)為成都網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)、外貿(mào)網(wǎng)站建設(shè)app軟件開(kāi)發(fā)公司以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠(chéng)的服務(wù)。成都創(chuàng)新互聯(lián)深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!

本文主要介紹Three.js的開(kāi)發(fā)基礎(chǔ)和基本原理,以及如何實(shí)現(xiàn)3D全景圖。想在web端實(shí)現(xiàn)3D全景圖的效果,除了全景圖片、WebGL外,還需要處理很多細(xì)節(jié)。據(jù)我所知,目前國(guó)外3D全景圖比較好的是KrPano,國(guó)內(nèi)很多3D全景服務(wù)是在使用krpano的工具。

前段時(shí)間連續(xù)上了一個(gè)月班,加班加點(diǎn)完成了一個(gè)3D攻堅(jiān)項(xiàng)目。也算是由傳統(tǒng)web轉(zhuǎn)型到webgl圖形學(xué)開(kāi)發(fā)中,坑不少,做了一下總結(jié)分享。

Three.js

基于簡(jiǎn)化WebGL開(kāi)發(fā)復(fù)雜度和降低入門難度的目的,mrdoob)在WebGL標(biāo)準(zhǔn)基礎(chǔ)上封裝了一個(gè)輕量級(jí)的JS 3D庫(kù)—— Three.js。

在我看來(lái),Three.js具有以下特點(diǎn):

  • 完備 具備3D開(kāi)發(fā)所需完整功能,基本上使用WebGL能實(shí)現(xiàn)的效果,用Three.js都能更簡(jiǎn)單地實(shí)現(xiàn)
  • 易用 架構(gòu)設(shè)計(jì)比較清晰和合理,易于理解,擴(kuò)展性較好,且開(kāi)發(fā)效率高于WebGL
  • 開(kāi)源 項(xiàng)目開(kāi)源,且有一批活躍的貢獻(xiàn)者, 持續(xù)維護(hù)升級(jí)中

Three.js使WebGL更加好用,可以實(shí)現(xiàn)很棒的3D效果,比如:

  • 游戲 hellorun
  • 數(shù)據(jù)可視化 armsglobe

1、法向量問(wèn)題

法線是垂直于我們想要照亮的物體表面的向量。法線代表表面的方向因此他們?yōu)楣庠春臀矬w的交互建模中具有決定性作用。每一個(gè)頂點(diǎn)都有一個(gè)關(guān)聯(lián)的法向量。

Three.js開(kāi)發(fā)實(shí)現(xiàn)3D地圖的實(shí)踐過(guò)程總結(jié)

如果一個(gè)頂點(diǎn)被多個(gè)三角形共享,共享頂點(diǎn)的法向量等于共享頂點(diǎn)在不同的三角形中的法向量的和。N=N1+N2;

Three.js開(kāi)發(fā)實(shí)現(xiàn)3D地圖的實(shí)踐過(guò)程總結(jié)

所以如果不做任何處理,直接將3維物體的點(diǎn)傳遞給BufferGeometry,那么由于法向量被合成,經(jīng)過(guò)片元著色器插值后,就會(huì)得到這個(gè)黑不溜秋的效果

Three.js開(kāi)發(fā)實(shí)現(xiàn)3D地圖的實(shí)踐過(guò)程總結(jié)

我的處理方式使頂點(diǎn)的法向量保持唯一,那么就需要在共享頂點(diǎn)處,拷貝一份頂點(diǎn),并重新計(jì)算索引,是的每個(gè)被多個(gè)面共享的頂點(diǎn)都有多份,每一份有一個(gè)單獨(dú)的法向量,這樣就可以使得每個(gè)面都有一個(gè)相同的顏色

Three.js開(kāi)發(fā)實(shí)現(xiàn)3D地圖的實(shí)踐過(guò)程總結(jié)

2、光源與面塊顏色

開(kāi)發(fā)過(guò)程中設(shè)計(jì)給了一套配色,然而一旦有光源,面塊的最終顏色就會(huì)與光源混合,顏色自然與最終設(shè)計(jì)的顏色大相徑庭。下面是Lambert光照模型的混合算法。

Three.js開(kāi)發(fā)實(shí)現(xiàn)3D地圖的實(shí)踐過(guò)程總結(jié)

而且產(chǎn)品的要求是頂面保持設(shè)計(jì)的顏色,側(cè)面需要加入光源變化效果,當(dāng)對(duì)地圖做操作時(shí),側(cè)面顏色需要根據(jù)視角發(fā)生變化。那么我的處理方式是將頂面與側(cè)面分別繪制(創(chuàng)建兩個(gè)Mesh),頂面使用MeshLambertMaterial的emssive屬性設(shè)置自發(fā)光顏色與設(shè)計(jì)顏色保持一致,也就不會(huì)有光照效果,側(cè)面綜合使用Emssive與color來(lái)應(yīng)用光源效果。

Three.js開(kāi)發(fā)實(shí)現(xiàn)3D地圖的實(shí)踐過(guò)程總結(jié)

var material1 = new __WEBPACK_IMPORTED_MODULE_0_three__["MeshLambertMaterial"]({
 emissive: new __WEBPACK_IMPORTED_MODULE_0_three__["Color"](style.fillStyle[0], style.fillStyle[1], style.fillStyle[2]),
 side: __WEBPACK_IMPORTED_MODULE_0_three__["DoubleSide"],
 shading: __WEBPACK_IMPORTED_MODULE_0_three__["FlatShading"],
 vertexColors: __WEBPACK_IMPORTED_MODULE_0_three__["VertexColors"]
 });

 var material2 = new __WEBPACK_IMPORTED_MODULE_0_three__["MeshLambertMaterial"]({
 color: new __WEBPACK_IMPORTED_MODULE_0_three__["Color"](style.fillStyle[0] * 0.1, style.fillStyle[1] * 0.1, style.fillStyle[2] * 0.1),
 emissive: new __WEBPACK_IMPORTED_MODULE_0_three__["Color"](style.fillStyle[0] * 0.9, style.fillStyle[1] * 0.9, style.fillStyle[2] * 0.9),
 side: __WEBPACK_IMPORTED_MODULE_0_three__["DoubleSide"],
 shading: __WEBPACK_IMPORTED_MODULE_0_three__["FlatShading"],
 vertexColors: __WEBPACK_IMPORTED_MODULE_0_three__["VertexColors"]
 });

3、POI標(biāo)注

Three中創(chuàng)建始終朝向相機(jī)的POI可以使用Sprite類,同時(shí)可以將文字和圖片繪制在canvas上,將canvas作為紋理貼圖放到Sprite上。但這里的一個(gè)問(wèn)題是canvas圖像將會(huì)失真,原因是沒(méi)有合理的設(shè)置sprite的scale,導(dǎo)致圖片被拉伸或縮放失真。

Three.js開(kāi)發(fā)實(shí)現(xiàn)3D地圖的實(shí)踐過(guò)程總結(jié)

問(wèn)題的解決思路是要保證在3d世界中的縮放尺寸,經(jīng)過(guò)一系列變換投影到相機(jī)屏幕后仍然與canvas在屏幕上的大小保持一致。這需要我們計(jì)算出屏幕像素與3d世界中的長(zhǎng)度單位的比值,然后將sprite縮放到合適的3d長(zhǎng)度。

Three.js開(kāi)發(fā)實(shí)現(xiàn)3D地圖的實(shí)踐過(guò)程總結(jié)

Three.js開(kāi)發(fā)實(shí)現(xiàn)3D地圖的實(shí)踐過(guò)程總結(jié)

4、點(diǎn)擊拾取問(wèn)題  

webgl中3D物體繪制到屏幕將經(jīng)過(guò)以下幾個(gè)階段

Three.js開(kāi)發(fā)實(shí)現(xiàn)3D地圖的實(shí)踐過(guò)程總結(jié)

所以要在3D應(yīng)用做點(diǎn)擊拾取,首先要將屏幕坐標(biāo)系轉(zhuǎn)化成ndc坐標(biāo)系,這時(shí)候得到ndc的xy坐標(biāo),由于2d屏幕并沒(méi)有z值所以,屏幕點(diǎn)轉(zhuǎn)化成3d坐標(biāo)的z可以隨意取值,一般取0.5(z在-1到1之間

function fromSreenToNdc(x, y, container) {
 return {
 x: x / container.offsetWidth * 2 - 1,
 y: -y / container.offsetHeight * 2 + 1,
 z: 1
 };
}
function fromNdcToScreen(x, y, container) {
 return {
 x: (x + 1) / 2 * container.offsetWidth,
 y: (1 - y) / 2 * container.offsetHeight
 };
}

然后將ndc坐標(biāo)轉(zhuǎn)化成3D坐標(biāo):  ndc = P * MV * Vec4  Vec4 = MV-1 * P -1 * ndc  這個(gè)過(guò)程在Three中的Vector3類中已經(jīng)有實(shí)現(xiàn):

unproject: function () {

 var matrix = new Matrix4();

 return function unproject( camera ) {

 matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );
 return this.applyMatrix4( matrix );

 };

 }(),

將得到的3d點(diǎn)與相機(jī)位置結(jié)合起來(lái)做一條射線,分別與場(chǎng)景中的物體進(jìn)行碰撞檢測(cè)。首先與物體的外包球進(jìn)行相交性檢測(cè),與球不相交的排除,與球相交的保存進(jìn)入下一步處理。將所有外包球與射線相交的物體按照距離相機(jī)遠(yuǎn)近進(jìn)行排序,然后將射線與組成物體的三角形做相交性檢測(cè)。求出相交物體。當(dāng)然這個(gè)過(guò)程也由Three中的RayCaster做了封裝,使用起來(lái)很簡(jiǎn)單:

mouse.x = ndcPos.x;
 mouse.y = ndcPos.y;

 this.raycaster.setFromCamera(mouse, camera);

 var intersects = this.raycaster.intersectObjects(this._getIntersectMeshes(floor, zoom), true);

5、性能優(yōu)化

隨著場(chǎng)景中的物體越來(lái)越多,繪制過(guò)程越來(lái)越耗時(shí),導(dǎo)致手機(jī)端幾乎無(wú)法使用。

Three.js開(kāi)發(fā)實(shí)現(xiàn)3D地圖的實(shí)踐過(guò)程總結(jié)

在圖形學(xué)里面有個(gè)很重要的概念叫“one draw all”一次繪制,也就是說(shuō)調(diào)用繪圖api的次數(shù)越少,性能越高。比如canvas中的fillRect、fillText等,webgl中的drawElements、drawArrays;所以這里的解決方案是對(duì)相同樣式的物體,把它們的側(cè)面和頂面統(tǒng)一放到一個(gè)BufferGeometry中。這樣可以大大降低繪圖api的調(diào)用次數(shù),極大的提升渲染性能。

Three.js開(kāi)發(fā)實(shí)現(xiàn)3D地圖的實(shí)踐過(guò)程總結(jié)

這樣解決了渲染性能問(wèn)題,然而帶來(lái)了另一個(gè)問(wèn)題,現(xiàn)在是吧所有樣式相同的面放在一個(gè)BufferGeometry中(我們稱為樣式圖形),那么在面點(diǎn)擊時(shí)候就無(wú)法單獨(dú)判斷出到底是哪個(gè)物體(我們稱為物體圖形)被選中,也就無(wú)法對(duì)這個(gè)物體進(jìn)行高亮縮放處理。我的處理方式是,把所有的物體單獨(dú)生成物體圖形保存在內(nèi)存中,做面點(diǎn)擊的時(shí)候用這部分?jǐn)?shù)據(jù)來(lái)做相交性檢測(cè)。對(duì)于選中物體后的高亮縮放處理,首先把樣式面中相應(yīng)部分裁減掉,然后把選中的物體圖形加入到場(chǎng)景中,對(duì)它進(jìn)行縮放高亮處理。裁剪方法是,記錄每個(gè)物體在樣式圖形中的其實(shí)索引位置,在需要裁切時(shí)候?qū)⑦@部分索引制零。在需要恢復(fù)的地方在把這部分索引恢復(fù)成原狀。

6、面點(diǎn)擊移動(dòng)到屏幕中央

這部分也是遇到了不少坑,首先的想法是:

面中心點(diǎn)目前是在世界坐標(biāo)系內(nèi)的坐標(biāo),先用center.project(camera)得到歸一化設(shè)備坐標(biāo),在根據(jù)ndc得到屏幕坐標(biāo),而后根據(jù)面中心點(diǎn)屏幕坐標(biāo)與屏幕中心點(diǎn)坐標(biāo)做插值,得到偏移量,在根據(jù)OribitControls中的pan方法來(lái)更新相機(jī)位置。這種方式最終以失敗告終,因?yàn)橄鄼C(jī)可能做各種變換,所以屏幕坐標(biāo)的偏移與3d世界坐標(biāo)系中的位置關(guān)系并不是線性對(duì)應(yīng)的。

最終的想法是:

我們現(xiàn)在想將點(diǎn)擊面的中心點(diǎn)移到屏幕中心,屏幕中心的ndc坐標(biāo)永遠(yuǎn)都是(0,0)我們的觀察視線與近景面的焦點(diǎn)的ndc坐標(biāo)也是0,0;也就是說(shuō)我們要將面中心點(diǎn)作為我們的觀察點(diǎn)(屏幕的中心永遠(yuǎn)都是相機(jī)的觀察視線),這里我們可以直接將面中心所謂視線的觀察點(diǎn),利用lookAt方法求取相機(jī)矩陣,但如果這樣簡(jiǎn)單處理后的效果就會(huì)給人感覺(jué)相機(jī)的姿態(tài)變化了,也就是會(huì)感覺(jué)并不是平移過(guò)去的,所以我們要做的是保持相機(jī)當(dāng)前姿態(tài)將面中心作為相機(jī)觀察點(diǎn)。

回想平移時(shí)我們將屏幕移動(dòng)轉(zhuǎn)化為相機(jī)變化的過(guò)程是知道屏幕偏移求target,這里我們要做的就是知道target反推屏幕偏移的過(guò)程。首先根據(jù)當(dāng)前target與面中心求出相機(jī)的偏移向量,根據(jù)相機(jī)偏移向量求出在相機(jī)x軸和up軸的投影長(zhǎng)度,根據(jù)投影長(zhǎng)度就能返推出應(yīng)該在屏幕上的平移量?!?/p>

this.unprojectPan = function(deltaVector, moveDown) {
 // var getProjectLength()
 var element = scope.domElement === document ? scope.domElement.body : scope.domElement;

 var cxv = new Vector3(0, 0, 0).setFromMatrixColumn(scope.object.matrix, 0);// 相機(jī)x軸
 var cyv = new Vector3(0, 0, 0).setFromMatrixColumn(scope.object.matrix, 1);// 相機(jī)y軸
 // 相機(jī)軸都是單位向量
 var pxl = deltaVector.dot(cxv)/* / cxv.length()*/; // 向量在相機(jī)x軸的投影
 var pyl = deltaVector.dot(cyv)/* / cyv.length()*/; // 向量在相機(jī)y軸的投影

 // offset=dx * vector(cx) + dy * vector(cy.project(xoz).normalize)
 // offset由相機(jī)x軸方向向量+相機(jī)y軸向量在xoz平面的投影組成
 var dv = deltaVector.clone();
 dv.sub(cxv.multiplyScalar(pxl));
 pyl = dv.length();

 if ( scope.object instanceof PerspectiveCamera ) {
 // perspective

 var position = scope.object.position;
 var offset = new Vector3(0, 0, 0);
 offset.copy(position).sub(scope.target);
 var distance = offset.length();
 distance *= Math.tan(scope.object.fov / 2 * Math.PI / 180);

 // var xd = 2 * distance * deltaX / element.clientHeight;
 // var yd = 2 * distance * deltaY / element.clientHeight;
 // panLeft( xd, scope.object.matrix );
 // panUp( yd, scope.object.matrix );

 var deltaX = pxl * element.clientHeight / (2 * distance);
 var deltaY = pyl * element.clientHeight / (2 * distance) * (moveDown ? -1 : 1);

 return [deltaX, deltaY];
 } else if ( scope.object instanceof OrthographicCamera ) {

 // orthographic
 // panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix );
 // panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix );
 var deltaX = pxl * element.clientWidth * scope.object.zoom / (scope.object.right - scope.object.left);
 var deltaY = pyl * element.clientHeight * scope.object.zoom / (scope.object.top - scope.object.bottom);

 return [deltaX, deltaY];
 } else {

 // camera neither orthographic nor perspective
 console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );

 }
 }

7、2/3D切換

23D切換的主要內(nèi)容就是當(dāng)相機(jī)的視線軸與場(chǎng)景的平面垂直時(shí),使用平行投影,這樣用戶只能看到頂面給人的感覺(jué)就是2D視圖。所以要根據(jù)透視的視錐體計(jì)算出平行投影的世景體。

Three.js開(kāi)發(fā)實(shí)現(xiàn)3D地圖的實(shí)踐過(guò)程總結(jié)

因?yàn)橛脩魰?huì)在2D、3D場(chǎng)景下做很多操作,比如平移、縮放、旋轉(zhuǎn),要想無(wú)縫切換,這個(gè)關(guān)鍵在于將平行投影與視錐體相機(jī)的位置、lookAt方式保持一致;以及將他們放大縮小的關(guān)鍵點(diǎn):distance的比例與zoom來(lái)保持一致。

平行投影中,zoom越大代表六面體的首尾兩個(gè)面面積越小,放大越大。

Three.js開(kāi)發(fā)實(shí)現(xiàn)3D地圖的實(shí)踐過(guò)程總結(jié)

8、3D中地理級(jí)別  

地理級(jí)別實(shí)際是像素跟墨卡托坐標(biāo)系下米的對(duì)應(yīng)關(guān)系,這個(gè)有通用的標(biāo)準(zhǔn)以及計(jì)算公式:

r=6378137
resolution=2*PI*r/(2^zoom*256)

各個(gè)級(jí)別中像素與米的對(duì)應(yīng)關(guān)系如下:

resolution zoom 2048 blocksize 256 blocksize scale(dpi=160)
156543.0339 0 320600133.5 40075016.69 986097851.5
78271.51696 1 160300066.7 20037508.34 493048925.8
39135.75848 2 80150033.37 10018754.17 246524462.9
19567.87924 3 40075016.69 5009377.086 123262231.4
9783.939621 4 20037508.34 2504688.543 61631115.72
4891.96981 5 10018754.17 1252344.271 30815557.86
2445.984905 6 5009377.086 626172.1357 15407778.93
1222.992453 7 2504688.543 313086.0679 7703889.465
611.4962263 8 1252344.271 156543.0339 3851944.732
305.7481131 9 626172.1357 78271.51696 1925972.366
152.8740566 10 313086.0679 39135.75848 962986.1831
76.4370283 11 156543.0339 19567.87924 481493.0916
38.2185141 12 78271.51696 9783.939621 240746.5458
19.1092571 13 39135.75848 4891.96981 120373.2729
9.5546285 14 19567.87924 2445.984905 60186.63645
4.7773143 15 9783.939621 1222.992453 30093.31822
2.3886571 16 4891.96981 611.4962263 15046.65911
1.1943286 17 2445.984905 305.7481131 7523.329556
0.5971643 18 1222.992453 152.8740566 3761.664778
0.2985821 19 611.4962263 76.43702829 1880.832389
0.1492911 20 305.7481131 38.21851414 940.4161945
0.0746455 21
0.0373227 22

3D中的計(jì)算策略是,首先需要將3D世界中的坐標(biāo)與墨卡托單位的對(duì)應(yīng)關(guān)系搞清楚,如果已經(jīng)是以mi來(lái)做單位,那么就可以直接將相機(jī)的投影屏幕的高度與屏幕的像素?cái)?shù)目做比值,得出的結(jié)果跟上面的ranking做比較,選擇不用的級(jí)別數(shù)據(jù)以及比例尺。注意3D地圖中的比例尺并不是在所有屏幕上的所有位置與現(xiàn)實(shí)世界都滿足這個(gè)比例尺,只能說(shuō)是相機(jī)中心點(diǎn)在屏幕位置處的像素是滿足這個(gè)關(guān)系的,因?yàn)槠叫型队坝薪筮h(yuǎn)小的效果。

9、poi碰撞

由于標(biāo)注是永遠(yuǎn)朝著相機(jī)的,所以標(biāo)注的碰撞就是把標(biāo)注點(diǎn)轉(zhuǎn)換到屏幕坐標(biāo)系用寬高來(lái)計(jì)算矩形相交問(wèn)題。至于具體的碰撞算法,大家可以在網(wǎng)上找到,這里不展開(kāi)。下面是計(jì)算poi矩形的代碼

export function getPoiRect(poi, zoomLevel, wrapper) {
 let style = getStyle(poi.styleId, zoomLevel);
 if (!style) {
 console.warn("style is invalid!");
 return;
 }
 let labelStyle = getStyle(style.labelid, zoomLevel);
 if (!labelStyle) {
 console.warn("labelStyle is invalid!");
 return;
 }
 if (!poi.text) {
 return;
 }
 let charWidth = (TEXTPROP.charWidth || 11.2) * // 11.2是根據(jù)測(cè)試得到的估值
 (labelStyle.fontSize / (TEXTPROP.fontSize || 13)); // 13是得到11.2時(shí)的fontSize
 // 返回2d坐標(biāo)
 let x = 0;//poi.points[0].x;
 let y = 0;//-poi.points[0].z;
 let path = [];
 let icon = iconSet[poi.styleId];
 let iconWidh = (icon && icon.width) || 32;
 let iconHeight = (icon && icon.height) || 32;
 let multi = /\//g;
 let firstLinePos = [];
 let textAlign = null;
 let baseLine = null;
 let hOffset = (iconWidh / 2) * ICONSCALE;
 let vOffset = (iconHeight / 2) * ICONSCALE;
 switch(poi.direct) {
 case 2: { // 左
 firstLinePos.push(x - hOffset - 2);
 firstLinePos.push(y);
 textAlign = 'right';
 baseLine = 'middle';
 break;
 };
 case 3: { // 下
 firstLinePos.push(x);
 firstLinePos.push(y - vOffset - 2);
 textAlign = 'center';
 baseLine = 'top';
 break;
 };
 case 4: { // 上
 firstLinePos.push(x);
 firstLinePos.push(y + vOffset + 2);
 textAlign = 'center';
 baseLine = 'bottom';
 break;
 };
 case 1:{ // 右
 firstLinePos.push(x + hOffset + 2);
 firstLinePos.push(y);
 textAlign = 'left';
 baseLine = 'middle';
 break;
 };
 default: {
 firstLinePos.push(x);
 firstLinePos.push(y);
 textAlign = 'center';
 baseLine = 'middle';
 }
 }
 path = path.concat(firstLinePos);

 let minX = null, maxX = null;
 let minY = null, maxY = null;
 let parts = poi.text.split(multi);

 let textWidth = 0;
 if (wrapper) {
 // 漢字和數(shù)字的寬度是不同的,所以必須使用measureText來(lái)精確測(cè)量
 let textWidth2 = wrapper.context.measureText(parts[0]).width;
 let textWidth3 = wrapper.context.measureText(parts[1] || '').width;
 textWidth = Math.max(textWidth2, textWidth3);
 } else {
 textWidth = Math.max(parts[0].length, parts[1] ? parts[1].length : 0) * charWidth;
 }

 if (textAlign === 'left') {
 minX = x - hOffset;
 maxX = path[0] + textWidth; // 只用第一行文本
 } else if (textAlign === 'right') {
 minX = path[0] - textWidth;
 maxX = x + hOffset;
 } else { // center
 minX = x - Math.max(textWidth / 2, hOffset);
 maxX = x + Math.max(textWidth / 2, hOffset);
 }
 if (baseLine === 'top') {
 maxY = y + vOffset;
 minY = y - vOffset - labelStyle.fontSize * parts.length;
 } else if (baseLine === 'bottom') {
 maxY = y + vOffset + labelStyle.fontSize * parts.length;
 minY = y - vOffset;
 } else { // middle
 minY = Math.min(y - vOffset, path[1] - labelStyle.fontSize / 2);
 maxY = Math.max(y + vOffset, path[1] + labelStyle.fontSize * (parts.length + 0.5 - 1));
 }

 return {
 min: {
 x: minX,
 y: minY
 },
 max: {
 x: maxX,
 y: maxY
 }
 };
}

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)創(chuàng)新互聯(lián)的支持。


當(dāng)前題目:Three.js開(kāi)發(fā)實(shí)現(xiàn)3D地圖的實(shí)踐過(guò)程總結(jié)
分享鏈接:http://weahome.cn/article/gggesh.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部