一、前言
本文實現的是在一段圓弧上進行一段飛線進行移動,具體實現效果如下
二、思路
先繪製一段軌道圓弧,然後在繪製一段飛線圓弧,該段飛線圓弧保證開始的角度和軌道圓弧一致,然後改變飛線圓弧的姿態,讓其繞著z軸旋轉,當然不能一直不停的旋轉,這樣會脫離軌道圓弧;那麼什麼臨界點是什麼時候?因為軌道圓弧的生成從開始角度繪製到結束結束角度,那麼結束角度減去開始角度就是軌道圓度的角度大小,軌道圓弧的角度減去飛線圓弧的弧度就是剩餘能在軌道圓弧上旋轉的角度。上圖可能更直觀些:
三、程式碼實現
理清楚思路後,進行程式碼實現,只展示關鍵程式碼部分,場景、相機、光原始碼省略。
import * as THREE from "three"; const sumGroup = new THREE.Group(); const R = 150; const curve = new THREE.ArcCurve( 0, 40, R, Math.PI / 6, Math.PI - Math.PI / 6, false ); const points = curve.getPoints(100); const geometry = new THREE.BufferGeometry(); geometry.setFromPoints(points); const material = new THREE.LineBasicMaterial({ color: 0x00ffff, }); const curline = new THREE.Line(geometry, material); // 自定義一些屬性 curline.startAngle = Math.PI / 6; curline.endAngle = Math.PI - Math.PI / 6; sumGroup.add(curline); // 以相同的圓心畫一小段圓心 進行旋轉 const flyAngle = Math.PI / 10; const extraAngle = curline.endAngle - curline.startAngle - flyAngle; // 旋轉結束角度-開始角度-飛線角度=剩餘可旋轉的角度 // 重新建立一個同旋轉起點的小圓弧進行繞z軸旋轉 const flyCurve = new THREE.ArcCurve( 0, 0, R, curline.startAngle, curline.startAngle + flyAngle, false ); const flyPoints = flyCurve.getSpacedPoints(100); const flyGeometry = new THREE.BufferGeometry().setFromPoints(flyPoints); const flyMaterial = new THREE.PointsMaterial({ vertexColors: true, size: 2.0, }); // 建立縮小縮小系數陣列 let sizeArr = []; for (let i = 0; i < flyPoints.length; i++) { sizeArr.push(i / flyPoints.length); } flyGeometry.attributes.percent = new THREE.BufferAttribute( new Float32Array(sizeArr), 1 ); // 進行顏色插值計算 const color1 = new THREE.Color(0x006666); // 青色 const color2 = new THREE.Color(0xffff00); // 黃色 let color = null; const colorArr = []; const nums = flyPoints.length - 2; for (let i = 0; i < flyPoints.length; i++) { if (i < nums) { color = color1.clone().lerp(color2, i / nums); } else { (color = color2.clone()).lerp(color1, i - nums / flyPoints.length - nums); } colorArr.push(color.r, color.g, color.b); } flyGeometry.attributes.color = new THREE.BufferAttribute( new Float32Array(colorArr), 3 ); // 注意只有點材料纔會有預設的size屬性 頂點著色器纔會有gl_PointSize = size 語句 flyMaterial.onBeforeCompile = (shader) => { shader.vertexShader = shader.vertexShader.replace( "void main() {", ["attribute float percent;", "void main() {"].join("\n") ); shader.vertexShader = shader.vertexShader.replace( "gl_PointSize = size", ["gl_PointSize = size * percent;"].join("\n") ); }; const flyLine = new THREE.Points(flyGeometry, flyMaterial); flyLine.position.y += 40; sumGroup.add(flyLine); function animationLoop() { flyLine.rotation.z += 0.02; // 當旋轉的角度大於在大圓弧上可旋轉的剩餘角度時,說明已經不再此圓弧上旋轉,需重新開始旋轉 if (flyLine.rotation.z > extraAngle) flyLine.rotation.z = 0.0; requestAnimationFrame(animationLoop); } animationLoop(); export default sumGroup;
四、疑問
上方的程式碼實現中,飛線圓弧我使用的是以原點為中心的圓弧繞其旋轉,因為軌道圓弧的圓心在0,40;所以旋轉完後需要進行位置移動y軸40;但是我一開始的思路是飛線圓弧以同圓心同開始旋轉角度繪製,改變姿態後會不在圓弧軌道上,沒弄清原因,有會的可請教!