Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
果圖截圖:
開始
中途
最終
由于截圖無法顯示出動畫的震撼,請看視頻:
<script src="https://lf6-cdn-tos.bytescm.com/obj/cdn-static-resource/tt_player/tt.player.js?v=20160723"></script>
流量不夠多的朋友可以看這里,gif動圖走起來:
開始
開始2
中間
最終
太好看了有沒有!!
朋友或者喜歡的他/她生日的時候,將這個美麗的場景發(fā)送給ta,一定會收獲滿滿的感動哦。
html代碼:
<div style="text-align:center;clear:both;"> <script src="/gg_bd_ad_720x90.js" type="text/javascript"></script> <script src="/follow.js" type="text/javascript"></script> </div> <canvas></canvas> <canvas></canvas> <canvas></canvas> <script src="js/index.js"></script>
js代碼中涉及到許多作圖函數(shù),如果能認(rèn)真研究透該項目的代碼,相信你一定會有所收獲的。
需要完整代碼的朋友,可以私信或留言獲取哦~
天小編我逛程序員論壇,看到了一位程序員小伙子分享的他的表白過程,中專生,暗戀某女孩好久了,但是程序員你懂得 很多人都說程序員不懂浪漫,乘女孩生日,寫了個網(wǎng)頁表白,不知后續(xù)如何。
推薦下我自己的前端學(xué)習(xí)群:680847368,不管你是小白還是大牛,小編我都挺歡迎,不定期分享干貨,今天的這個JavaScript做的愛心表白網(wǎng)頁已上傳,包括我自己整理的一份2017最新的前端資料和零基礎(chǔ)入門教程,歡迎初學(xué)和進階中的小伙伴。
代碼太長,這里只能展示部分,需要完整版的可以進小編的前端學(xué)習(xí)群:680847368,不管你是小白還是大牛,小編我都挺歡迎,不定期分享干貨,今天的這個JavaScript做的愛心表白網(wǎng)頁已上傳,包括我自己整理的一份2017最新的前端資料和零基礎(chǔ)入門教程,歡迎初學(xué)和進階中的小伙伴。
為整天和 UI 打交道的前端工程師,是否想在他(她)生日的時候用代碼送上一份驚喜呢?
不妨用 Three.js 做個 3D 的蛋糕送給 ta,既浪漫又能展現(xiàn)你技術(shù)的魅力。
這篇文章我們就來學(xué)習(xí)下如何用 Three.js 畫一個蛋糕。
圖片
代碼地址:https://github.com/QuarkGluonPlasma/threejs-exercize
Three.js 相關(guān)基礎(chǔ)
Three.js 是通過場景 Scene 來管理所有的物體的,加到 Scene 的物體還可以分個組:
const scene = new THREE.Scene();
scene.add(xxx);
const group = new THREE.Group();
group.add(yyy);
group.add(zzz);
scene.add(group);
想要把 Scene 中的所有物體渲染出來,需要指定一個相機 camera,然后用 renderer 來渲染,如果有動畫效果,要用 requestAnimationFrame 來一幀幀不斷渲染。
const renderer = new THREE.WebGLRenderer();
function render() {
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
相機 camera 分為從一個點去看的透視相機 PerspectiveCamera,還有從一個面去投影的正交相機 OrthographicCamera。
圖片
圖片
透視相機的特點是近大遠(yuǎn)小,而正交的則不是,就是一個平行投影,大小不變。
三維世界還需要指定一個光源,不然是全黑的,光源種類很多,常用的有這些:
點光源:從一個點發(fā)射光線,就像燈泡一樣。
平行光:平行的光線
環(huán)境光:均勻照射每個地方
聚光燈:舞臺聚光燈的光源
三維場景中的物體有很多種,比如永遠(yuǎn)面向相機的平面是 Sprite(我們做“漫天花雨”效果用的那個),還有由三角形構(gòu)成的物體叫做 Mesh。
圖片
Mesh 比較常用,它是由一個個三角形構(gòu)成的幾何體,還可以在每個面上貼圖。所以,參數(shù)有兩個,幾何體 Geometry 和材質(zhì) Material。
比如圓柱體就是一個 Mesh,創(chuàng)建它的時候要指定圓柱幾何體 CylinderBufferGeometry 和每個面的材質(zhì) Material。
const 圓柱幾何體 = new THREE.CylinderBufferGeometry(上圓半徑, 下圓半徑, 高度, 側(cè)面分段數(shù)量);
const 側(cè)面材質(zhì) = new THREE.MeshBasicMaterial({map: 紋理圖片});
const 上面材質(zhì) = new THREE.MeshBasicMaterial({color: 'red'});
const 下面材質(zhì) = new THREE.MeshBasicMaterial({color: 'red'});
const 圓柱 = new THREE.Mesh(圓柱幾何體, [側(cè)面材質(zhì), 上面材質(zhì), 下面材質(zhì)]);
MeshBasicMaterial 是基礎(chǔ)的材質(zhì),可以通過 color 來指定顏色,也可以通過 map 來指定紋理圖片 texture。
各種 Mesh 中比較特殊是文字,它用的是 TextGeometry,文字需要從一個 xxx.typeface.json 中加載。
而這種 json 文件可以用字體文件 ttf 來轉(zhuǎn)換得到。用ttf 轉(zhuǎn) typeface.json 的這個網(wǎng)站來轉(zhuǎn):
圖片
之后就可以顯示文字了:
const fontLoader = new THREE.FontLoader();
fontLoader.load('./font/xxx.typeface.json', function (font) {
var textGeometry = new THREE.TextGeometry('文字', 參數(shù));
const textMaterial = [
new THREE.MeshBasicMaterial({color: '字體顏色'}),
new THREE.MeshBasicMaterial({color: '側(cè)面顏色'}),
];
const text = new THREE.Mesh(textGeometry, textMaterial);
});
這些就是我們會用到的 Three.js 基礎(chǔ),簡單做個小結(jié):
Three.js 是通過 Scene 來管理各種物體的,物體還可以分下組。
物體中常見的有 Mesh 和 Sprite 等,Sprite 是永遠(yuǎn)面向相機的一個平面,Mesh 是由三角形構(gòu)成的三維物體。Mesh 要指定幾何體Geometry 和材質(zhì) Material,常用的材質(zhì)可以是顏色或者紋理貼圖。其中文字 TextGeometry 比較特殊,需要一個 typeface.json 的文件,這個可以由 ttf 轉(zhuǎn)換得到。
場景中的物體準(zhǔn)備好之后,還需要設(shè)置下光源 Light 和相機 Camera,相機主要有從點去看的透視相機和從一個平面去投影的正交相機,之后就可以通過渲染器 Renderer 渲染出來了,結(jié)合 requestAnimationFrame 來一幀幀的渲染。
基礎(chǔ)學(xué)完之后,正式開始畫蛋糕了。
畫 3D 蛋糕
蛋糕其實就是由 4 個圓柱體加上文字構(gòu)成的,每個圓柱體都設(shè)置了不同的位置,圓柱體的側(cè)面和上下面都貼上不同的貼圖,就是一個蛋糕。
我們先準(zhǔn)備蛋糕的貼圖:
圖片
圖片
圖片
圖片
使用紋理加載器 TextureLoader 去加載他們:
const cakeTexture1 = new THREE.TextureLoader().load('img/cake1.png');
const cakeTexture2 = new THREE.TextureLoader().load('img/cake2.png');
const cakeTexture3 = new THREE.TextureLoader().load(`img/cake3.png`);
const cakeTexture4 = new THREE.TextureLoader().load('img/cake4.png');
然后構(gòu)成紋理貼圖的材質(zhì):
const cakeMaterail1 = new THREE.MeshBasicMaterial({map: cakeTexture1});
const cakeMaterail2 = new THREE.MeshBasicMaterial({map: cakeTexture2});
const cakeMaterail3 = new THREE.MeshBasicMaterial({map: cakeTexture3});
const cakeMaterail4 = new THREE.MeshBasicMaterial({map: cakeTexture4});
除了紋理貼圖的材質(zhì)外,再準(zhǔn)備個顏色構(gòu)成的材質(zhì):
const pinkMaterial = new THREE.MeshBasicMaterial({color: 'pink'});
然后創(chuàng)建 4 個圓柱體的物體(Mesh),使用不同的貼圖材質(zhì)和顏色材質(zhì):
const cakeGeometry1 = new THREE.CylinderBufferGeometry(100, 100, 70, 40);
const cakePart1 = new THREE.Mesh(cakeGeometry1, [cakeMaterail1, pinkMaterial, pinkMaterial]);
圓柱體的幾何體 CylinderBufferGeometry 的參數(shù)分別是 上面圓的半徑,下面圓的半徑,高度,側(cè)面的分割次數(shù)。
上面圓半徑保持一致,這樣才是圓柱體。側(cè)面分割次數(shù)設(shè)置為 40,這樣比較圓滑。
之后還設(shè)置下位移,然后就可以加到蛋糕分組里了。
我們用同樣的方式創(chuàng)建四個圓柱體,設(shè)置不同的大小和位置,貼不同的圖:
const cakeGeometry1 = new THREE.CylinderBufferGeometry(100, 100, 70, 40);
const cakePart1 = new THREE.Mesh(cakeGeometry1, [cakeMaterail1, pinkMaterial, pinkMaterial]);
cakePart1.translateY(45)
const cakeGeometry2 = new THREE.CylinderBufferGeometry(120, 120, 70, 40);
const cakePart2 = new THREE.Mesh(cakeGeometry2,[cakeMaterail3, pinkMaterial, pinkMaterial]);
cakePart2.translateY(-25)
const cakeGeometry3 = new THREE.CylinderBufferGeometry(140, 140, 60, 40);
const cakePart3 = new THREE.Mesh(cakeGeometry3, [cakeMaterail2, pinkMaterial, pinkMaterial]);
cakePart3.translateY(-90)
const cakeGeometry4 = new THREE.CylinderBufferGeometry(160, 160, 10, 40);
const cakePart4 = new THREE.Mesh(cakeGeometry4, [cakeMaterail4, cakeMaterail4, cakeMaterail4]);
cakePart4.translateY(-120)
cake.add(cakePart1)
cake.add(cakePart2)
cake.add(cakePart3)
cake.add(cakePart4)
如果對坐標(biāo)位置拿不準(zhǔn),可以在 Scene 中加上一個坐標(biāo)的輔助工具 AxisHelper。參數(shù)是坐標(biāo)軸長度。
const axisHelper = new THREE.AxisHelper(2500);
scene.add(axisHelper);
圖片
然后是文字的部分,這個要先通過字體文件 ttf 轉(zhuǎn)成 typeface.json 的文件,然后用 fontLoader 來加載,之后創(chuàng)建相應(yīng)的 Mesh:
fontLoader.load('./font/guang.typeface.json', function (font) {
var textGeometry = new THREE.TextGeometry('光光', {
font: font,
size: 30,
height: 5,
bevelEnabled: true,
bevelSize: 10,
});
const textMaterial = ['white', 'red'].map(color => new THREE.MeshBasicMaterial({color}));
const text = new THREE.Mesh(textGeometry, textMaterial);
text.translateY(90)
text.translateX(-45)
cake.add(text);
});
TextGeometry 需要設(shè)置的參數(shù)有字體大小 size,厚度 height,以及邊緣是否是曲面 bevelEnabled,和曲面的大小 bevelSize。
我們這里的效果是需要開啟曲面的。
圖片
4 個圓柱體畫完了,文字也畫完了,那蛋糕就算是畫完了,之后設(shè)置下光源、相機,就可以用 Renderer 渲染了。
光源使用環(huán)境光,因為要均勻的照射:
const light = new THREE.AmbientLight(0xCCCCCC);
scene.add(light);
相機使用正交相機,因為不需要近大遠(yuǎn)小的透視效果:
const width = window.innerWidth;
const height = window.innerHeight;
//窗口寬高比
const k = width / height;
//三維場景顯示范圍的高度
const s = 200;
const camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
camera.position.set(0, 100, 500)
camera.lookAt(scene.position);
正交相機的參數(shù)分別是左右上下遠(yuǎn)近的三維視野范圍,我們指定高度為 200,然后根據(jù)窗口的寬高比算出寬度。遠(yuǎn)近可以設(shè)置一個比較大的范圍。
之后就可以用 Renderer 來渲染了。把渲染出的 canvas 的 dom 掛載到 body 上。
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
//設(shè)置背景顏色
renderer.setClearColor(0xFFFFFF, 1);
document.body.appendChild(renderer.domElement);
function render() {
renderer.render(scene, camera);
cake.rotation.y += 0.005;
requestAnimationFrame(render)
}
render()
在每幀 render 之前,還做了個圍繞 y 軸的自動旋轉(zhuǎn)。
還要支持手動的旋轉(zhuǎn),這個直接使用 Three.js 的軌道控制器 OrbitControls 就行。
const controls = new THREE.OrbitControls(camera);
參數(shù)是相機,因為這種視野的改變就是通過改變相機位置和朝向來實現(xiàn)的。
創(chuàng)建了 Scene 中的蛋糕的每一部分,設(shè)置好了光源、相機,用渲染器做了一幀幀的渲染,并且添加了用鼠標(biāo)來改變視角的軌道控制器之后,就完成了 3D 蛋糕的制作。
我們來看下效果:
圖片
代碼地址:https://github.com/QuarkGluonPlasma/threejs-exercize
全部代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>生日蛋糕</title>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
<script src="./js/three.js"></script>
<script src="./js/OrbitControls.js"></script>
</head>
<body>
<script>
const width = window.innerWidth;
const height = window.innerHeight;
//窗口寬高比
const k = width / height;
//三維場景顯示范圍的寬度
const s = 200;
const camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
const fontLoader = new THREE.FontLoader();
const scene = new THREE.Scene();
const cake = new THREE.Group();
const renderer = new THREE.WebGLRenderer();
function create() {
renderer.setSize(width, height);
//設(shè)置背景顏色
renderer.setClearColor(0xFFFFFF, 1);
document.body.appendChild(renderer.domElement);
camera.position.set(0, 100, 500)
camera.lookAt(scene.position);
const light = new THREE.AmbientLight(0xCCCCCC);
scene.add(light);
const axisHelper = new THREE.AxisHelper(2500);
scene.add(axisHelper);
const cakeTexture1 = new THREE.TextureLoader().load('img/cake1.png');
const cakeTexture2 = new THREE.TextureLoader().load('img/cake2.png');
const cakeTexture3 = new THREE.TextureLoader().load(`img/cake3.png`);
const cakeTexture4 = new THREE.TextureLoader().load('img/cake4.png');
const cakeMaterail1 = new THREE.MeshBasicMaterial({map: cakeTexture1});
const cakeMaterail2 = new THREE.MeshBasicMaterial({map: cakeTexture2});
const cakeMaterail3 = new THREE.MeshBasicMaterial({map: cakeTexture3});
const cakeMaterail4 = new THREE.MeshBasicMaterial({map: cakeTexture4});
const pinkMaterial = new THREE.MeshBasicMaterial({color: 'pink'});
const cakeGeometry1 = new THREE.CylinderBufferGeometry(100, 100, 70, 40);
const cakePart1 = new THREE.Mesh(cakeGeometry1, [cakeMaterail1, pinkMaterial, pinkMaterial]);
cakePart1.translateY(45)
const cakeGeometry2 = new THREE.CylinderBufferGeometry(120, 120, 70, 40);
const cakePart2 = new THREE.Mesh(cakeGeometry2,[cakeMaterail3, pinkMaterial, pinkMaterial]);
cakePart2.translateY(-25)
const cakeGeometry3 = new THREE.CylinderBufferGeometry(140, 140, 60, 40);
const cakePart3 = new THREE.Mesh(cakeGeometry3, [cakeMaterail2, pinkMaterial, pinkMaterial]);
cakePart3.translateY(-90)
const cakeGeometry4 = new THREE.CylinderBufferGeometry(160, 160, 10, 40);
const cakePart4 = new THREE.Mesh(cakeGeometry4, [cakeMaterail4, cakeMaterail4, cakeMaterail4]);
cakePart4.translateY(-120)
cake.add(cakePart1)
cake.add(cakePart2)
cake.add(cakePart3)
cake.add(cakePart4)
fontLoader.load('./font/guang.typeface.json', function (font) {
var textGeometry = new THREE.TextGeometry('光光', {
font: font,
size: 30,
height: 5,
bevelEnabled: true,
bevelSize: 10,
});
const textMaterial = ['white', 'red'].map(color => new THREE.MeshBasicMaterial({color}));
const text = new THREE.Mesh(textGeometry, textMaterial);
text.translateY(90)
text.translateX(-45)
cake.add(text);
});
scene.add(cake);
}
function render() {
renderer.render(scene, camera);
cake.rotation.y += 0.005;
requestAnimationFrame(render)
}
create()
render()
const controls = new THREE.OrbitControls(camera);
</script>
</body>
</html>
總結(jié)
本文我們用 Three.js 來實現(xiàn)了 3D 蛋糕的效果。
首先我們學(xué)習(xí)了下 Three.js 的基礎(chǔ):通過 Scene 來管理物體,物體可以分組,物體包括 Mesh、Sprite 等,Mesh 是三角形構(gòu)成的 3D 物體,要分別指定幾何體 Geometry 和材質(zhì) Material。材質(zhì)可以是紋理(Texture)貼圖、也可以是顏色。其中文字的 Mesh 需要做 ttf 到 typeface.json 的轉(zhuǎn)換,加載這個 json 才能顯示文字。
物體創(chuàng)建完了之后,還要設(shè)置相機、燈光等,然后通過渲染器來一幀幀的渲染。
調(diào)試的時候還可以添加 AxisHelper 坐標(biāo)系輔助工具來輔助開發(fā)。
然后我們實現(xiàn)了 3D 蛋糕:
通過 4 個圓柱體 + 文字來畫的,圓柱體用了不同的紋理貼圖材質(zhì),設(shè)置了不同的位置,然后組成蛋糕的 group。
設(shè)置了環(huán)境光,使用了正交相機,還啟用了軌道控制器 OrbitControls,來實現(xiàn)鼠標(biāo)拖拽改變相機位置,進而改變視野角度的效果。
下個他(她)的生日,不妨試試用 Three.js 畫個蛋糕送給他(她),或許會有不一樣的收獲哦。
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。