文將演示如何使用three。js和TweenMax(GSAP)為元素網格構建有趣的排斥效果。效果重現BestServedBold的Dribbble拍攝,在文章的最後還給出了演示全程。
注意:我們假設您已經擁有一些基本的JavaScript和three。js知識。如果您不熟悉它,我強烈建議您檢視官方和。
效果圖如下:
原創的想法
最初的想法是基於BestServedBold的Dribbble拍攝全息相互作用:
影片載入中。。。
核心概念
我們的想法是建立一個隨機元素網格,對滑鼠移動做出反應。
網格的每個元素將根據從當前滑鼠位置到元素中心的距離更新其Y位置,旋轉和縮放值。
滑鼠離元素越近,它就越大。
我們還為此定義了一個半徑,僅影響該半徑內的一個元素或任意數量的元素。半徑越大,移動滑鼠時元素的反應就越多。
開始
首先,我們必須為演示設定HTML頁面。這是一個簡單的樣板檔案,因為所有程式碼都將在canvas元素中執行:
createGrid() { // create a basic 3D object to be used as a container for our grid elements so we can move all of them together this。groupMesh = new THREE。Object3D(); const meshParams = { color: ‘#ff00ff’, metalness: 。58, emissive: ‘#000000’, roughness: 。18, }; // we create our material outside the loop to keep it more performant const material = new (meshParams); for (let row = 0; row < this。grid。rows; row++) { this。meshes[row] = []; for (let col = 0; col < this。grid。cols; col++) { const geometry = this。getRandomGeometry(); const mesh = this。getMesh(geometry。geom, material); mesh。position。set(col + (col * this。gutter。size), 0, row + (row * this。gutter。size)); mesh。rotation。x = geometry。rotationX; mesh。rotation。y = geometry。rotationY; mesh。rotation。z = geometry。rotationZ; // store the initial rotation values of each element so we can animate back mesh。initialRotation = { x: mesh。rotation。x, y: mesh。rotation。y, z: mesh。rotation。z, }; this。groupMesh。add(mesh); // store the element inside our array so we can get back when need to animate this。meshes[row][col] = mesh; } } //center on the X and Z our group mesh containing all the grid elements const centerX = ((this。grid。cols - 1) + ((this。grid。cols - 1) * this。gutter。size)) * 。5; const centerZ = ((this。grid。rows - 1) + ((this。grid。rows - 1) * this。gutter。size)) * 。5; this。groupMesh。position。set(-centerX, 0, -centerZ); this。scene。add(this。groupMesh); }環境光接下來,我們將新增一個環境光,以提供一些不錯的色彩效果:
addAmbientLight() { const light = new (‘#2900af’, 1); this。scene。add(light); }聚光燈我們還將SpotLight新增到場景中以獲得逼真的觸感:
RectArea光為了照亮一些均勻的光,我們使用RectArea光:
addRectLight() { const light = new (‘#0077ff’, 1, 2000, 2000); light。position。set(5, 50, 50); light。lookAt(0, 0, 0); this。scene。add(light); }點光源對於最終的燈光效果,我們建立了一個PointLight函式來新增我們想要的光線:
addPointLight(color, position) { const light = new (color, 1, 1000, 1); light。position。set(position。x, position。y, position。z); this。scene。add(light); }陰影層(Shadow Floor)現在,我們需要新增一個形狀,作為滑鼠游標能夠懸停的對映物件: addFloor() { const geometry = new (100, 100); const material = new ({ opacity: 。3 }); this。floor = new (geometry, material); this。floor。position。y = 0; this。floor。receiveShadow = true; this。floor。rotateX(- Math。PI / 2); this。scene。add(this。floor); }繪製/動畫元素這是處理所有動畫的功能; 它將在requestAnimationFrame回撥內的每一幀上呼叫: draw() { // maps our mouse coordinates from the camera perspective this。raycaster。setFromCamera(this。mouse3D, this。camera); // checks if our mouse coordinates intersect with our floor shape const intersects = this。raycaster。intersectObjects([this。floor]); if (intersects。length) { // get the x and z positions of the intersection const { x, z } = intersects[0]。point; for (let row = 0; row < this。grid。rows; row++) { for (let col = 0; col < this。grid。cols; col++) { // extract out mesh base on the grid location const mesh = this。meshes[row][col]; // calculate the distance from the intersection down to the grid element const mouseDistance = distance(x, z, mesh。position。x + this。groupMesh。position。x, mesh。position。z + this。groupMesh。position。z); // based on the distance we map the value to our min max Y position // it works similar to a radius range const maxPositionY = 10; const minPositionY = 0; const startDistance = 6; const endDistance = 0; const y = map(mouseDistance, startDistance, endDistance, minPositionY, maxPositionY); // based on the y position we animate the mesh。position。y // we don´t go below position y of 1 TweenMax。to(mesh。position, 。4, { y: y < 1 ? 1 : y }); // create a scale factor based on the mesh。position。y const scaleFactor = mesh。position。y / 2。5; // to keep our scale to a minimum size of 1 we check if the scaleFactor is below 1 const scale = scaleFactor < 1 ? 1 : scaleFactor; // animates the mesh scale properties TweenMax。to(mesh。scale, 。4, { ease: Back。easeOut。config(1。7), x: scale, y: scale, z: scale, }); // rotate our element TweenMax。to(mesh。rotation, 。7, { ease: Back。easeOut。config(1。7), x: map(mesh。position。y, -1, 1, radians(45), mesh。initialRotation。x), z: map(mesh。position。y, -1, 1, radians(-90), mesh。initialRotation。z), y: map(mesh。position。y, -1, 1, radians(90), mesh。initialRotation。y), }); } } } }這就是它!這裡有更多的可能性,即新增更多物件等。看看以下網格變化:
或相機旋轉變數:
最後,提供本文的Demo的訪問網址:http://www。ikinsoft。com/demo/interactive/index。html