使用three.js實現(xiàn)炫酷的酸性風(fēng)格3D頁面效果
本文內(nèi)容主要介紹,通過使用React+three.js技術(shù)棧,加載3D模型、添加3D文字、增加動畫、點擊交互等,配合樣式設(shè)計,實現(xiàn)充滿設(shè)計感的 🤢`酸性風(fēng)格頁面。

背景
近期學(xué)習(xí)了 WebGL 和 Three.js 的一些基礎(chǔ)知識,于是想結(jié)合最近流行的酸性設(shè)計風(fēng)格,裝飾一下個人主頁,同時總結(jié)一些學(xué)到的知識。本文內(nèi)容主要介紹,通過使用 React + three.js 技術(shù)棧,加載 3D模型、添加 3D文字、增加動畫、點擊交互等,配合樣式設(shè)計,實現(xiàn)充滿設(shè)計感的 🤢 酸性風(fēng)格頁面。
基礎(chǔ)知識
Three.js
Three.js 是一款基于原生 WebGL封裝運行在瀏覽器中的 3D引擎,可以用它創(chuàng)建各種三維場景,包括了攝影機、光影、材質(zhì)等各種對象。是一款使用非常廣泛的三維引擎??梢栽?three.js官方中文文檔 進一步深入學(xué)習(xí)。
酸性設(shè)計
酸性設(shè)計 一詞翻譯自 Acid Graphics,起源于 上世紀90年代 的酸浩室音樂、電子舞曲以及嬉皮士文化。在設(shè)計領(lǐng)域,這種酸性美學(xué)承載一種 自由的主張,怪誕的圖形,大膽鮮明的配色,特殊的材料質(zhì)感,搭配多種字體,組成了獨特的酸性設(shè)計風(fēng)格。

總之,鮮艷高飽和度 的色彩組合;黑灰色打底高飽和 熒光色 點綴畫面的 五彩斑斕的黑;充滿未來感、炫酷、充滿科技感的液態(tài)金屬、玻璃、鋁箔塑料等材質(zhì);隨機 的元素、圖形的布局;不斷 重復(fù)、裁切、組合 幾何圖形 等都是酸性設(shè)計風(fēng)格。酸性風(fēng)格在音樂專輯封面、視覺海報、書籍電影封面、網(wǎng)頁設(shè)計中也逐漸開始流行。
實現(xiàn)效果

在線預(yù)覽:https://tricell.fun
實現(xiàn)
3D模型
場景初始化
🌏 創(chuàng)建場景
scene = new THREE.Scene();
📷 初始化相機
透視相機 PerspectiveCamera 的 4個 參數(shù)分別是指:視場、長寬比、近面、遠面。
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 1000); // 設(shè)置相機位置 camera.position.set(600, 20, -200); // 相機聚焦到屏幕中央 camera.lookAt(new THREE.Vector3(0, 0, 0));
💡 初始化光源
添加 半球光源 HemisphereLight:創(chuàng)建室外效果更加自然的光源
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);
添加 環(huán)境光 AmbientLight:
var ambiColor = '#0C0C0C'; var ambientLight = new THREE.AmbientLight(ambiColor); scene.add(ambientLight);
添加輔助工具(可選)
📦 添加輔助網(wǎng)格
GridHelper 可用于添加網(wǎng)格輔助線,也可用于裝飾,通過 GridHelper(size, divisions, colorCenterLine, colorGrid) 實現(xiàn)。
size:網(wǎng)格寬度,默認值為10。divisions:等分數(shù),默認值為10。colorCenterLine:中心線顏色,默認值為0x444444。colorGrid: 網(wǎng)格線顏色,默認值為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 可以對三維場景進行縮放、平移、旋轉(zhuǎn)操作,本質(zhì)上改變的并不是場景,而是相機的參數(shù)。開發(fā)時 OrbitControls.js 需要單獨引入。
controls = new THREE.OrbitControls(camera, renderer.domElement); controls.target.set(0, 0, 0); controls.update();
📦 添加性能查看插件
stats 是一個 Three.js 開發(fā)的輔助庫,主要用于檢測動畫運行時的幀數(shù)。stats.js 也需要單獨引入。
stats = new Stats(); container.appendChild(stats.dom);
加載模型
本文示例用到的 扔鐵餅的人 雕像 3D 模型來源于 threedscans.com,可 免費😄 下載使用,本文末尾提供了多個免費模型下載網(wǎng)站,有 200多頁 免費模型,大家感興趣的話可以挑選自己喜歡的模型下載使用。當然,有建模能力的同學(xué),也可以使用 blender、3dmax 等專業(yè)建模軟件生成自己喜歡的模型。
加載 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);
});
添加網(wǎng)格、加載完成模型之后的效果如下圖所示。

添加轉(zhuǎn)盤動畫
通過 requestAnimationFrame 刷新頁面的方法添加轉(zhuǎn)盤動畫效果。window.requestAnimationFrame() 告訴瀏覽器希望執(zhí)行一個動畫,并且要求瀏覽器在下次重繪之前調(diào)用指定的回調(diào)函數(shù)更新動畫。該方法需要傳入一個回調(diào)函數(shù)作為參數(shù),該回調(diào)函數(shù)會在瀏覽器下一次重繪之前執(zhí)行。
function animate () {
requestAnimationFrame(animate);
// 隨著頁面重繪不斷改變場景的rotation.y來實現(xiàn)旋轉(zhuǎn)
scene.rotation.y -= 0.015;
renderer.render(scene, camera);
}
添加點擊交互
在 Three.js 場景中我們要點擊某個模型獲取它的信息、或者做一些其他操作,要用到 Raycaster(光線投射),原理就是在你鼠標點擊的位置發(fā)射一束射線,被射線中的物體都被記錄下來。基本語法是 Raycaster(origin, direction, near, far),其中:
origin:射線的起點向量。direction:射線的方向向量。near:所有返回的結(jié)果應(yīng)該比near遠。值不能為負,默認值為0。far:所有返回的結(jié)果應(yīng)該比far近。不能小于near,默認值為無窮大。
代碼實現(xiàn)的基本步驟是:獲取鼠標在屏幕的坐標 → 屏幕坐標轉(zhuǎn)標準設(shè)備坐標 → 標準設(shè)備坐標轉(zhuǎn)世界坐標 → 拿到鼠標在場景的世界坐標 → 根據(jù)世界坐標和相機生成射線投射方向單位向量 → 根據(jù)射線投射方向單位向量創(chuàng)建射線投射器對象。
//聲明raycaster和mouse變量
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
onMouseClick = event => {
// 將鼠標點擊位置的屏幕坐標轉(zhuǎn)成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直線和所有模型相交的數(shù)組集合
let intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
alert('HELLO WORLD')
// 可以通過遍歷實現(xiàn)點擊不同mesh觸發(fā)不同交互,如:
let selectedObj = intersects[0].object;
if (selectedObj.name === 'car') {
alert('汽車🚗')
}
}
}
window.addEventListener('click', onMouseClick, false);
添加3D文字
使用 TextGeometry(text : String, parameters : Object) 添加 3D文字,以下是可設(shè)置屬性的說明:
size:字號大小,一般為大寫字母的高度。height:文字的厚度。weight:值為normal或bold,表示是否加粗。font:字體,默認是helvetiker,需對應(yīng)引用的字體文件。style:值為normal或italics,表示是否斜體bevelThickness:倒角厚度。bevelSize:倒角寬度。curveSegments:弧線分段數(shù),使得文字的曲線更加光滑。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);
});

優(yōu)化
現(xiàn)在模型加載已經(jīng)基本完成了,但是 3D 模型的體積一般比較大,部署之后我發(fā)現(xiàn)網(wǎng)頁加載非常慢,影響用戶體驗,減小模型體積是十分必要的,在網(wǎng)上找了很久壓縮工具,發(fā)現(xiàn)在不需要安裝大型 3D建模軟件 的情況下,使用 obj2gltf 可以將體積較大的 OBJ 格式模型轉(zhuǎn)化為 gltf 模型,有效優(yōu)化模型體積,提升網(wǎng)頁加載速度。
安裝
npm install obj2gltf --save
將obj模型復(fù)制到以下目錄中
node_modules\obj2gltf\bin
執(zhí)行轉(zhuǎn)碼指令
node obj2gltf.js -i demo.obj -o demo.gltf
如圖出現(xiàn)類似上述內(nèi)容,轉(zhuǎn)碼完成,對比轉(zhuǎn)化前后的文件體積,本例中 kas.obj 初始文件大小為 9.7M 轉(zhuǎn)化后的文件 kas.gltf 只有 4.6M,體積縮小一半,此時將轉(zhuǎn)化后的模型加載到頁面上,肉眼幾乎看不出模型效果的變化,同時頁面加載速度得到明顯提升。

obj2gltf 也可以作為庫使用,通過 node服務(wù) 實時轉(zhuǎn)化模型,感興趣的同學(xué)可以通過文章末尾鏈接深入學(xué)習(xí)。
也可以是使用 3D 建模軟件如 blender 等手動通過減少模型 面數(shù) 和 縮小體積 等途徑對模型壓縮優(yōu)化,這種優(yōu)化效果更明顯。
完整代碼
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));
// 半球光源:創(chuàng)建室外效果更加自然的光源
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);
// 環(huán)境光
var ambiColor = '#0C0C0C';
var ambientLight = new THREE.AmbientLight(ambiColor);
scene.add(ambientLight);
// 網(wǎng)格
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直線和所有模型相交的數(shù)組集合
var intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
alert('HELLO WORLD')
}
}
window.addEventListener('click', onMouseClick, false);
}
}
其他設(shè)計元素
本文主要介紹 3D元素 的加載,由于文章篇幅以及時間有限(博主太懶😂)其他元素的實現(xiàn)不做詳細講解(可能后續(xù)有時間會總結(jié)整理 maybe)感興趣的同學(xué)可以擴展閱讀以下其他大神優(yōu)秀的文章。
流體背景
靜態(tài) 液態(tài)背景圖可以通過 SVG filter 實現(xiàn),可以閱讀《Creating Patterns With SVG Filters》,實現(xiàn) 動態(tài) 流體背景,可以使用Three.js 結(jié)合原生GLSL實現(xiàn),可參考《CodePen Shader Template》示例來實現(xiàn)。

金屬、霓虹、故障效果等酸性效果字體可以閱讀我的另一篇文章《僅用CSS幾步實現(xiàn)賽博朋克2077風(fēng)格視覺效果》,也可以使用設(shè)計生成,由于時間關(guān)系,本文項目中的金屬效果文字以及本文banner頭圖中的文字都是使用在線藝術(shù)字體生成網(wǎng)站生成的,感興趣的同學(xué)可以自行嘗試設(shè)計。
未來進一步優(yōu)化
#todo酸性風(fēng)格液態(tài)背景實現(xiàn)。#todo3D模型液態(tài)金屬效果。
three.js 優(yōu)秀案例推薦
最后給大家推薦幾個非常驚艷的 three.js 項目來一起體驗和學(xué)習(xí),無論是頁面交互、視覺設(shè)計還是性能優(yōu)化都做到了極致,可以從中學(xué)到很多。

github首頁:3D地球 實時顯示全球熱門倉庫。
kodeclubs:低面數(shù) 3D城市 第三人稱小游戲。
球鞋展示:720度 球鞋動態(tài)展示。
沙雕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
藝術(shù)字體在線生成:https://cooltext.com
什么是酸性設(shè)計:https://www.shejipi.com/361258.html
作者:dragonir 本文地址:https://www.cnblogs.com/dragonir/p/15350537.html
到此這篇關(guān)于使用three.js實現(xiàn)炫酷的酸性風(fēng)格3D頁面的文章就介紹到這了,更多相關(guān)three.js酸性風(fēng)格3D頁面內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS實現(xiàn)table表格內(nèi)針對某列內(nèi)容進行即時搜索篩選功能
這篇文章主要介紹了JS實現(xiàn)table表格內(nèi)針對某列內(nèi)容進行即時搜索篩選功能,涉及javascript針對HTML元素的遍歷、屬性動態(tài)修改相關(guān)操作技巧,需要的朋友可以參考下2018-05-05
前端開發(fā)基礎(chǔ)javaScript的六大作用
這篇文章主要介紹了前端開發(fā)基礎(chǔ)javaScript的六大作用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
JS如何遍歷帶有子集的數(shù)組集合(嵌套數(shù)組)
這篇文章主要介紹了JS如何遍歷帶有子集的數(shù)組集合(嵌套數(shù)組)問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06
jquery實現(xiàn)下拉菜單的二級聯(lián)動利用json對象從DB取值顯示聯(lián)動
這篇文章主要介紹了jquery實現(xiàn)下拉菜單的二級聯(lián)動利用json對象從DB取值顯示聯(lián)動,需要的朋友可以參考下2014-03-03
五步輕松實現(xiàn)JavaScript HTML時鐘效果
這篇文章主要為大家詳細介紹了五步輕松實現(xiàn)JavaScript HTML時鐘效果的代碼,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-11-11

