使用three.js實現炫酷的酸性風格3D頁面效果
本文內容主要介紹,通過使用React+three.js技術棧,加載3D模型、添加3D文字、增加動畫、點擊交互等,配合樣式設計,實現充滿設計感的 🤢`酸性風格頁面。
背景
近期學習瞭 WebGL
和 Three.js
的一些基礎知識,於是想結合最近流行的酸性設計風格,裝飾一下個人主頁,同時總結一些學到的知識。本文內容主要介紹,通過使用 React + three.js
技術棧,加載 3D模型
、添加 3D文字
、增加動畫、點擊交互等,配合樣式設計,實現充滿設計感的 🤢
酸性風格頁面。
基礎知識
Three.js
Three.js
是一款基於原生 WebGL
封裝運行在瀏覽器中的 3D引擎
,可以用它創建各種三維場景,包括瞭攝影機、光影、材質等各種對象。是一款使用非常廣泛的三維引擎。可以在 three.js官方中文文檔 進一步深入學習。
酸性設計
酸性設計
一詞翻譯自 Acid Graphics
,起源於 上世紀90年代
的酸浩室音樂、電子舞曲以及嬉皮士文化。在設計領域,這種酸性美學承載一種 自由的主張
,怪誕的圖形,大膽鮮明的配色,特殊的材料質感,搭配多種字體,組成瞭獨特的酸性設計風格。
總之,鮮艷高飽和度
的色彩組合;黑灰色打底高飽和 熒光色
點綴畫面的 五彩斑斕的黑
;充滿未來感、炫酷、充滿科技感的液態金屬
、玻璃
、鋁箔塑料
等材質;隨機
的元素、圖形的佈局;不斷 重復
、裁切、組合 幾何圖形
等都是酸性設計風格。酸性風格在音樂專輯封面、視覺海報、書籍電影封面、網頁設計中也逐漸開始流行。
實現效果
在線預覽:https://tricell.fun
實現
3D模型
場景初始化
🌏
創建場景
scene = new THREE.Scene();
📷
初始化相機
透視相機 PerspectiveCamera
的 4個
參數分別是指:視場、長寬比、近面、遠面。
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 1000); // 設置相機位置 camera.position.set(600, 20, -200); // 相機聚焦到屏幕中央 camera.lookAt(new THREE.Vector3(0, 0, 0));
💡
初始化光源
添加 半球光源 HemisphereLight
:創建室外效果更加自然的光源
light = new THREE.HemisphereLight(0xffffff, 0x444444); light.position.set(0, 20, 0); scene.add(light); light = new THREE.DirectionalLight(0xffffff); light.position.set(0, 20, 10); light.castShadow = true; scene.add(light);
添加 環境光 AmbientLight
:
var ambiColor = '#0C0C0C'; var ambientLight = new THREE.AmbientLight(ambiColor); scene.add(ambientLight);
添加輔助工具(可選)
📦
添加輔助網格
GridHelper
可用於添加網格輔助線,也可用於裝飾,通過 GridHelper(size, divisions, colorCenterLine, colorGrid)
實現。
size
:網格寬度,默認值為10
。divisions
:等分數,默認值為10
。colorCenterLine
:中心線顏色,默認值為0x444444
。colorGrid
: 網格線顏色,默認值為0x888888
。
var grid = new THREE.GridHelper(1000, 100, 0x000000, 0x000000); grid.material.opacity = 0.1; grid.material.transparent = true; grid.position.set(0, -240, 0); scene.add(grid);
📦
添加相機控件
通過相機控件 OrbitControls
可以對三維場景進行縮放、平移、旋轉操作,本質上改變的並不是場景,而是相機的參數。開發時 OrbitControls.js
需要單獨引入。
controls = new THREE.OrbitControls(camera, renderer.domElement); controls.target.set(0, 0, 0); controls.update();
📦
添加性能查看插件
stats
是一個 Three.js
開發的輔助庫,主要用於檢測動畫運行時的幀數。stats.js
也需要單獨引入。
stats = new Stats(); container.appendChild(stats.dom);
加載模型
本文示例用到的 扔鐵餅的人
雕像 3D
模型來源於 threedscans.com
,可 免費😄
下載使用,本文末尾提供瞭多個免費模型下載網站,有 200多頁
免費模型,大傢感興趣的話可以挑選自己喜歡的模型下載使用。當然,有建模能力的同學,也可以使用 blender
、3dmax
等專業建模軟件生成自己喜歡的模型。
加載 obj
或 fbx
模型
需要單獨引入 FBXLoader.js
或 OBJLoader.js
,.fbx
和 .obj
格式的模型加載方法是一樣的。
// var loader = new THREE.FBXLoader(); var loader = new THREE.OBJLoader(); loader.load(model, function (object) { object.traverse(function (child) { if (child.isMesh) { child.castShadow = true; child.receiveShadow = true; } }); object.rotation.y = Math.PI / 2; object.position.set(0, -200, 0); object.scale.set(0.32, 0.32, 0.32); model = object; scene.add(object); });
加載 gltf 模型
需要單獨引入 GLTFLoader.js
,加載 .gltf
格式模型方法稍有不同,需要註意的是模型的遍歷對象和最終添加到場景中的是 object.scene
而不是 object
。
var loader = new THREE.GLTFLoader(); loader.load(model, function (object) { object.scene.traverse(function (child) { if (child.isMesh) { child.castShadow = true; child.receiveShadow = true; } }); object.scene.rotation.y = Math.PI / 2; object.scene.position.set(0, -240, 0); object.scene.scale.set(0.33, 0.33, 0.33); model = object.scene; scene.add(object.scene); });
添加網格、加載完成模型之後的效果如下圖所示。
添加轉盤動畫
通過 requestAnimationFrame
刷新頁面的方法添加轉盤動畫效果。window.requestAnimationFrame()
告訴瀏覽器希望執行一個動畫,並且要求瀏覽器在下次重繪之前調用指定的回調函數更新動畫。該方法需要傳入一個回調函數作為參數,該回調函數會在瀏覽器下一次重繪之前執行。
function animate () { requestAnimationFrame(animate); // 隨著頁面重繪不斷改變場景的rotation.y來實現旋轉 scene.rotation.y -= 0.015; renderer.render(scene, camera); }
添加點擊交互
在 Three.js
場景中我們要點擊某個模型獲取它的信息、或者做一些其他操作,要用到 Raycaster(光線投射)
,原理就是在你鼠標點擊的位置發射一束射線,被射線中的物體都被記錄下來。基本語法是 Raycaster(origin, direction, near, far)
,其中:
origin
:射線的起點向量。direction
:射線的方向向量。near
:所有返回的結果應該比near
遠。值不能為負,默認值為0
。far
:所有返回的結果應該比far
近。不能小於near
,默認值為無窮大
。
代碼實現的基本步驟是:獲取鼠標在屏幕的坐標 →
屏幕坐標轉標準設備坐標 →
標準設備坐標轉世界坐標 →
拿到鼠標在場景的世界坐標 →
根據世界坐標和相機生成射線投射方向單位向量 →
根據射線投射方向單位向量創建射線投射器對象。
//聲明raycaster和mouse變量 var raycaster = new THREE.Raycaster(); var mouse = new THREE.Vector2(); onMouseClick = event => { // 將鼠標點擊位置的屏幕坐標轉成threejs中的標準坐標,以屏幕中心為原點,值的范圍為-1到1. mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = - (event.clientY / window.innerHeight) * 2 + 1; // 通過鼠標點的位置和當前相機的矩陣計算出raycaster raycaster.setFromCamera(mouse, camera); // 獲取raycaster直線和所有模型相交的數組集合 let intersects = raycaster.intersectObjects(scene.children); if (intersects.length > 0) { alert('HELLO WORLD') // 可以通過遍歷實現點擊不同mesh觸發不同交互,如: let selectedObj = intersects[0].object; if (selectedObj.name === 'car') { alert('汽車🚗') } } } window.addEventListener('click', onMouseClick, false);
添加3D文字
使用 TextGeometry(text : String, parameters : Object)
添加 3D文字
,以下是可設置屬性的說明:
size
:字號大小,一般為大寫字母的高度。height
:文字的厚度。weight
:值為normal
或bold
,表示是否加粗。font
:字體,默認是helvetiker
,需對應引用的字體文件。style
:值為normal
或italics
,表示是否斜體bevelThickness
:倒角厚度。bevelSize
:倒角寬度。curveSegments
:弧線分段數,使得文字的曲線更加光滑。bevelEnabled
:佈爾值,是否使用倒角,意為在邊緣處斜切。
var loader = new THREE.FontLoader(); loader.load('gentilis_regular.typeface.json', function (font) { var textGeo = new THREE.TextGeometry('HELLO WORLD', { font: font, size: .8, height: .8, curveSegments: .05, bevelThickness: .05, bevelSize: .05, bevelEnabled: true }); var textMaterial = new THREE.MeshPhongMaterial({ color: 0x03c03c }); var mesh = new THREE.Mesh(textGeo, textMaterial); mesh.position.set(0, 3.8, 0); scene.add(mesh); });
優化
現在模型加載已經基本完成瞭,但是 3D
模型的體積一般比較大,部署之後我發現網頁加載非常慢,影響用戶體驗,減小模型體積是十分必要的,在網上找瞭很久壓縮工具,發現在不需要安裝大型 3D建模軟件
的情況下,使用 obj2gltf
可以將體積較大的 OBJ
格式模型轉化為 gltf
模型,有效優化模型體積,提升網頁加載速度。
安裝
npm install obj2gltf –save
將obj模型復制到以下目錄中
node_modules\obj2gltf\bin
執行轉碼指令
node obj2gltf.js -i demo.obj -o demo.gltf
如圖出現類似上述內容,轉碼完成,對比轉化前後的文件體積,本例中 kas.obj
初始文件大小為 9.7M
轉化後的文件 kas.gltf
隻有 4.6M
,體積縮小一半,此時將轉化後的模型加載到頁面上,肉眼幾乎看不出模型效果的變化,同時頁面加載速度得到明顯提升。
obj2gltf
也可以作為庫使用,通過 node服務
實時轉化模型,感興趣的同學可以通過文章末尾鏈接深入學習。
也可以是使用 3D
建模軟件如 blender
等手動通過減少模型 面數
和 縮小體積
等途徑對模型壓縮優化,這種優化效果更明顯。
完整代碼
var model = require('@/assets/models/kas.gltf'); var container, stats, controls; var camera, scene, renderer, light, model; class Kas extends React.Component { render () { return ( <div id="kas"></div> ) } componentDidMount () { this.initThree(); } initThree () { init(); animate(); function init () { container = document.getElementById('kas'); scene = new THREE.Scene(); scene.fog = new THREE.Fog(0xa0a0a0, 200, 1000); // 透視相機:視場、長寬比、近面、遠面 camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(600, 20, -200); camera.lookAt(new THREE.Vector3(0, 0, 0)); // 半球光源:創建室外效果更加自然的光源 light = new THREE.HemisphereLight(0xffffff, 0x444444); light.position.set(0, 20, 0); scene.add(light); light = new THREE.DirectionalLight(0xffffff); light.position.set(0, 20, 10); light.castShadow = true; scene.add(light); // 環境光 var ambiColor = '#0C0C0C'; var ambientLight = new THREE.AmbientLight(ambiColor); scene.add(ambientLight); // 網格 var grid = new THREE.GridHelper(1000, 100, 0x000000, 0x000000); grid.material.opacity = 0.1; grid.material.transparent = true; grid.position.set(0, -240, 0); scene.add(grid); // 加載gltf模型 var loader = new THREE.GLTFLoader(); loader.load(model, function (object) { object.scene.traverse(function (child) { if (child.isMesh) { child.castShadow = true; child.receiveShadow = true; } }); object.scene.rotation.y = Math.PI / 2; object.scene.position.set(0, -240, 0); object.scene.scale.set(0.33, 0.33, 0.33); model = object.scene; scene.add(object.scene); }); renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setClearAlpha(0); renderer.shadowMap.enabled = true; container.appendChild(renderer.domElement); window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }, false); stats = new Stats(); container.appendChild(stats.dom); } function animate () { var clock = new THREE.Clock() requestAnimationFrame(animate); var delta = clock.getDelta(); scene.rotation.y -= 0.015; renderer.render(scene, camera); stats.update(); } // 增加點擊事件 //聲明raycaster和mouse變量 var raycaster = new THREE.Raycaster(); var mouse = new THREE.Vector2(); function onMouseClick(event) { // 通過鼠標點擊位置計算出raycaster所需要點的位置,以屏幕中心為原點,值的范圍為-1到1. mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = - (event.clientY / window.innerHeight) * 2 + 1; // 通過鼠標點的位置和當前相機的矩陣計算出raycaster raycaster.setFromCamera(mouse, camera); // 獲取raycaster直線和所有模型相交的數組集合 var intersects = raycaster.intersectObjects(scene.children); if (intersects.length > 0) { alert('HELLO WORLD') } } window.addEventListener('click', onMouseClick, false); } }
其他設計元素
本文主要介紹 3D元素
的加載,由於文章篇幅以及時間有限(博主太懶😂
)其他元素的實現不做詳細講解(可能後續有時間會總結整理 maybe
)感興趣的同學可以擴展閱讀以下其他大神優秀的文章。
流體背景
靜態
液態背景圖可以通過 SVG filter
實現,可以閱讀《Creating Patterns With SVG Filters》,實現 動態
流體背景,可以使用Three.js 結合原生GLSL實現,可參考《CodePen Shader Template》示例來實現。
金屬、霓虹、故障效果等酸性效果字體可以閱讀我的另一篇文章《僅用CSS幾步實現賽博朋克2077風格視覺效果》,也可以使用設計生成,由於時間關系,本文項目中的金屬效果文字以及本文banner頭圖中的文字都是使用在線藝術字體生成網站生成的,感興趣的同學可以自行嘗試設計。
未來進一步優化
#todo
酸性風格液態背景實現。#todo
3D模型
液態金屬效果。
three.js
優秀案例推薦
最後給大傢推薦幾個非常驚艷的 three.js
項目來一起體驗和學習,無論是頁面交互、視覺設計還是性能優化都做到瞭極致,可以從中學到很多。
github首頁:3D地球
實時顯示全球熱門倉庫。
kodeclubs:低面數 3D城市
第三人稱小遊戲。
球鞋展示:720度
球鞋動態展示。
沙雕dance:沙雕動物舞者。
Zenly軟件:Zenly App
中文主頁。
參考資料
three.js: https://threejs.org
obj2gltf: https://github.com/CesiumGS/obj2gltf
200多頁免費3d模型 https://www.turbosquid.com
免費3D雕像: https://threedscans.com
免費3D模型:https://free3d.com
藝術字體在線生成:https://cooltext.com
什麼是酸性設計:https://www.shejipi.com/361258.html
作者:dragonir 本文地址:https://www.cnblogs.com/dragonir/p/15350537.html
到此這篇關於使用three.js實現炫酷的酸性風格3D頁面的文章就介紹到這瞭,更多相關three.js酸性風格3D頁面內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Three.js實現臉書元宇宙3D動態Logo效果
- three.js實現3d全景看房示例
- vue3項目中使用three.js的操作步驟
- Three.js物理引擎Cannon.js創建簡單應用程序
- 使用Three.js 實現虎年春節3D創意頁面