您好,登錄后才能下訂單哦!
這篇“three.js響應(yīng)式設(shè)計(jì)的方法”文章的知識(shí)點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“three.js響應(yīng)式設(shè)計(jì)的方法”文章吧。
canvas 畫布的尺寸有兩種:
像素尺寸,即canvas畫布在高度和寬度上有多少個(gè)像素,默認(rèn)是300*150
css 尺寸,即css 里的width和height
在web前端,dom元素的響應(yīng)式布局一般是通過css 實(shí)現(xiàn)的。
而canvas 則并非如此,canvas 的響應(yīng)式布局需要考慮其像素尺寸。
接下來,咱們就通過讓canvas 畫布自適應(yīng)瀏覽器的窗口的尺寸,來說一下canvas 的響應(yīng)式布局。
1.將之前的RenderStructure.tsx 復(fù)制粘貼一份,改名ResponsiveDesign.tsx,用于寫響應(yīng)式布局。
2.將ResponsiveDesign.tsx 頁(yè)面添加到路由中。
src/app.tsx
import React from "react"; import { useRoutes } from "react-router-dom"; import "./App.css"; import MainLayout from "./view/MainLayout"; import Fundamentals from "./view/Fundamentals"; import ResponsiveDesign from "./view/ResponsiveDesign"; const App: React.FC = (): JSX.Element => { const routing = useRoutes([ { path: "/", element: <MainLayout />, }, { path: "Fundamentals", element: <Fundamentals />, }, { path: "ResponsiveDesign", element: <ResponsiveDesign />, }, ]); return <>{routing}</>; }; export default App;
3.在ResponsiveDesign.tsx中先取消renderer 的尺寸設(shè)置。
//renderer.setSize(innerWidth, innerHeight);
4.用css 設(shè)置canvas 畫布及其父元素的尺寸,使其充滿窗口。
src/view/ResponsiveDesign
const ResponsiveDesign: React.FC = (): JSX.Element => { …… return <div ref={divRef} className="canvasWrapper"></div>; };
src/view/fullScreen.css
html { height: 100%; } body { margin: 0; overflow: hidden; height: 100%; } #root,.canvasWrapper,canvas{ width: 100%; height: 100%; }
3.將fullScreen.css 導(dǎo)入ResponsiveDesign.tsx
import "./fullScreen.css";
效果如下:
由上圖可見,立方體的邊界出現(xiàn)了鋸齒,這就是位圖被css拉伸后失真導(dǎo)致的,默認(rèn)canvas 畫布的尺寸只有300*150。
因此,我們需要用canvas 畫布的像素尺寸自適應(yīng)窗口。
4.建立一個(gè)讓canvas 像素尺寸隨css 尺寸同步更新的方法。
resizeRendererToDisplaySize(renderer); // 將渲染尺寸設(shè)置為其顯示的尺寸,返回畫布像素尺寸是否等于其顯示(css)尺寸的布爾值 function resizeRendererToDisplaySize(renderer) { const { width, height, clientWidth, clientHeight } = renderer.domElement; const needResize = width !== clientWidth || height !== clientHeight; if (needResize) { renderer.setSize(clientWidth, clientHeight, false); } return needResize; }
renderer.setSize(w,h,bool) 是重置渲染尺寸的方法,在此方法里會(huì)根據(jù)w,h參數(shù)重置canvas 畫布的尺寸。
this.setSize = function ( width, height, updateStyle ) { if ( xr.isPresenting ) { console.warn( 'THREE.WebGLRenderer: Can't change size while VR device is presenting.' ); return; } _width = width; _height = height; _canvas.width = Math.floor( width * _pixelRatio ); _canvas.height = Math.floor( height * _pixelRatio ); if ( updateStyle !== false ) { _canvas.style.width = width + 'px'; _canvas.style.height = height + 'px'; } this.setViewport( 0, 0, width, height ); };
setSize() 方法中的bool 參數(shù)很重要,會(huì)用于判斷是否設(shè)置canvas 畫布的css 尺寸。
5.當(dāng)canvas 畫布的尺寸變化了,相機(jī)視口的寬高比也需要同步調(diào)整。這樣我們拖拽瀏覽器的邊界,縮放瀏覽器的時(shí)候,就可以看到canvas 畫布自適應(yīng)瀏覽器的尺寸了。
function animate() { requestAnimationFrame(animate); if (resizeRendererToDisplaySize(renderer)) { const { clientWidth, clientHeight } = renderer.domElement; camera.aspect = clientWidth / clientHeight; camera.updateProjectionMatrix(); } cubes.forEach((cube) => { cube.rotation.x += 0.01; cube.rotation.y += 0.01; }); renderer.render(scene, camera); }
camera.aspect 屬性是相機(jī)視口的寬高比
我們?cè)赪ebGL 里說透視投影矩陣的時(shí)候說過,當(dāng)相機(jī)視口的寬高比變了,相機(jī)的透視投影矩陣也會(huì)隨之改變,因此我們需要使用camera.updateProjectionMatrix() 方法更新透視投影矩陣。
至于我們?yōu)槭裁床话迅孪鄼C(jī)視口寬高比的方法一起放進(jìn)resizeRendererToDisplaySize()里,這是為了降低resizeRendererToDisplaySize() 方法和相機(jī)的耦合度。具體要不要這么做視項(xiàng)目需求而定。
接下來咱們可以舉個(gè)例子,說一下canvas 畫布響應(yīng)式布局的應(yīng)用場(chǎng)合。
在下面的例子里,我們會(huì)給三維插圖一個(gè)縮放功能,從而更好的觀察細(xì)節(jié)。
1.新建一個(gè)Illustration 頁(yè)。
src/view/Illustration.tsx
import React, { useRef, useEffect, useState } from "react"; import { BoxGeometry, DirectionalLight, Mesh, MeshPhongMaterial, PerspectiveCamera, Scene, WebGLRenderer } from "three"; import "./Illustration.css"; const { innerWidth, innerHeight } = window; const scene = new Scene(); const camera = new PerspectiveCamera(75, innerWidth / innerHeight, 0.1, 1000); const renderer = new WebGLRenderer(); // renderer.setSize(innerWidth, innerHeight, false); // 光源 const color = 0xffffff; const intensity = 1; const light = new DirectionalLight(color, intensity); light.position.set(-1, 2, 4); scene.add(light); const geometry = new BoxGeometry(); const material = new MeshPhongMaterial({ color: 0x44aa88 }); camera.position.z = 5; const cubes = [-2, 0, 2].map((num) => makeInstance(num)); scene.add(...cubes); // 將渲染尺寸設(shè)置為其顯示的尺寸,返回畫布像素尺寸是否等于其顯示(css)尺寸的布爾值 function resizeRendererToDisplaySize(renderer: WebGLRenderer) { const { width, height, clientWidth, clientHeight } = renderer.domElement; const needResize = width !== clientWidth || height !== clientHeight; if (needResize) { renderer.setSize(clientWidth, clientHeight, false); } return needResize; } function makeInstance(x: number) { const cube = new Mesh(geometry, material); cube.position.x = x; return cube; } function animate() { requestAnimationFrame(animate); if (resizeRendererToDisplaySize(renderer)) { const { clientWidth, clientHeight } = renderer.domElement; camera.aspect = clientWidth / clientHeight; camera.updateProjectionMatrix(); } cubes.forEach((cube) => { cube.rotation.x += 0.01; cube.rotation.y += 0.01; }); renderer.render(scene, camera); } const Illustration: React.FC = (): JSX.Element => { const divRef = useRef<HTMLDivElement>(null); let [btnState, setBtnState] = useState(["small", "放大"]); const toggle = () => { if (btnState[0] === "small") { setBtnState(["big", "縮小"]); } else { setBtnState(["small", "放大"]); } }; useEffect(() => { const { current } = divRef; if (current) { current.innerHTML = ""; current.append(renderer.domElement); } animate(); }, []); return ( <div className="cont"> <p> 立方體,也稱正方體,是由6個(gè)正方形面組成的正多面體,故又稱正六面體。它有12條邊和8個(gè)頂點(diǎn)。其中正方體是特殊的長(zhǎng)方體。立方體是一種特殊的正四棱柱、長(zhǎng)方體、三角偏方面體、菱形多面體、平行六面體,就如同正方形是特殊的矩形、菱形、平行四邊形一様。立方體具有正八面體對(duì)稱性,即考克斯特BC3對(duì)稱性,施萊夫利符號(hào) ,考克斯特-迪肯符號(hào),與正八面體對(duì)偶。 </p> <div className="inllustration"> <div ref={divRef} className={`canvasWrapper ${btnState[0]}`}></div> <button className="btn" onClick={toggle}> {btnState[1]} </button> </div> <p> 立方體有11種不同的展開圖,即是說,我們可以有11種不同的方法切開空心立方體的7條棱而將其展平為平面圖形,見圖1。 [2] 立方體的11種不同展開圖。 如果我們要將立方體涂色而使相鄰的面不帶有相同的顏色,則我們至少需要3種顏色(類似于四色問題)。 立方體是唯一能夠獨(dú)立密鋪三維歐幾里得空間的柏拉圖正多面體,因此立方體堆砌也是四維唯一的正堆砌(三維空間中的堆砌拓?fù)渖系葍r(jià)于四維多胞體)。它又是柏拉圖立體中唯一一個(gè)有偶數(shù)邊面——正方形面的,因此,它是柏拉圖立體中獨(dú)一無二的環(huán)帶多面體(它所有相對(duì)的面關(guān)于立方體中心中心對(duì)稱)。 將立方體沿對(duì)角線切開,能得到6個(gè)全等的正4棱柱(但它不是半正的,底面棱長(zhǎng)與側(cè)棱長(zhǎng)之比為2:√3)將其正方形面貼到原來的立方體上,能得到菱形十二面體(Rhombic Dodecahedron)(兩兩共面三角形合成一個(gè)菱形)。 </p> <p> 立方體的對(duì)偶多面體是正八面體。 當(dāng)正八面體在立方體之內(nèi): 正八面體體積: 立方體體積=[(1/3)×高×底面積]×2: 邊=(1/3)(n/2)[(n)/2]2: n=1: 6 星形八面體的對(duì)角線可組成一個(gè)立方體。 截半立方體:從一條棱斬去另一條棱的中點(diǎn)得出 截角立方體 超正方體:立方體在高維度的推廣。更加一般的,立方體是一個(gè)大家族,即立方形家族(又稱超方形、正測(cè)形)的3維成員,它們都具有相似的性質(zhì)(如二面角都是90°、有類似的超體積公式,即Vn-cube=a等)。 長(zhǎng)方體、偏方面體的特例。 </p> <p> 立方體是唯一能夠獨(dú)立密鋪三維歐幾里得空間的柏拉圖正多面體,因此立方體堆砌也是四維唯一的正堆砌(三維空間中的堆砌拓?fù)渖系葍r(jià)于四維多胞體)。它又是柏拉圖立體中唯一一個(gè)有偶數(shù)邊面——正方形面的,因此,它是柏拉圖立體中獨(dú)一無二的環(huán)帶多面體(它所有相對(duì)的面關(guān)于立方體中心中心對(duì)稱)。 將立方體沿對(duì)角線切開,能得到6個(gè)全等的正4棱柱(但它不是半正的,底面棱長(zhǎng)與側(cè)棱長(zhǎng)之比為2:√3)將其正方形面貼到原來的立方體上,能得到菱形十二面體(Rhombic Dodecahedron)(兩兩共面三角形合成一個(gè)菱形)。 </p> </div> ); }; export default Illustration;
2.設(shè)置css 樣式
src/view/Illustration.css
p { text-indent: 2em; line-height: 24px; font-size: 14px; } .cont { width: 80%; max-width: 900px; margin: auto; } .inllustration{ position: relative; float: left; } .canvasWrapper { margin-right: 15px; transition-property: width, height; transition-duration: 1s, 1s; } .small { width: 150px; height: 150px; } .big { width: 100%; height: 100%; } .canvasWrapper canvas { width: 100%; height: 100%; } .btn { position: absolute; top: 0; left: 0; cursor: pointer; }
3.在App.tsx 中,基于Illustration頁(yè)新建一個(gè)路由
src/App.tsx
import React from "react"; import { useRoutes } from "react-router-dom"; import Basics from "./view/Basics"; import RenderStructure from "./view/RenderStructure"; import ResponsiveDesign from "./view/ResponsiveDesign"; import Illustration from "./view/Illustration"; const App: React.FC = (): JSX.Element => { const routing = useRoutes([ …… { path: "Illustration", element: <Illustration />, }, ]); return <>{routing}</>; }; export default App;
4.在首頁(yè)Basics.tsx中再開一個(gè)鏈接
import React from "react"; import { Link } from "react-router-dom"; const Basics: React.FC = (): JSX.Element => { return ( <nav style={{ width: "60%", margin: "auto" }}> <h3>three.js 基礎(chǔ)示例</h3> <ul> …… <li> <Link to="/Illustration">Illustration 三維插圖</Link> </li> </ul> </nav> ); }; export default Basics;
接下來,在首頁(yè)點(diǎn)擊Illustration 鏈接,就可以看到效果。
當(dāng)今大多數(shù)的PC端和移動(dòng)端顯示器都是HD-DPI顯示器。
HD-DPI 是High Definition-Dots Per Inch 的簡(jiǎn)稱,意思是高分辨率顯示器。
不同設(shè)備的顯示器的分辨率是不一樣的。
做過測(cè)試的會(huì)知道,我們需要用不同的設(shè)備測(cè)試項(xiàng)目,比如下面微信小程序開發(fā)者工具里提供的機(jī)型。
以上圖中的iPhone6/7/8 為例:
375667 代表的手機(jī)的屏幕的物理尺寸,如果我們?cè)谄渲薪⒁粋€(gè)100% 充滿屏幕的,那其尺寸就是375667。
Dpr 代表像素密度,2 表示手機(jī)屏幕在寬度上有3752 個(gè)像素,在高度上有6672 個(gè)像素,因此iPhone6/7/8 的屏幕的像素尺寸就是750*1334。
當(dāng)我們?cè)谶@種像素尺寸大于物理尺寸的高分辨率顯示器里繪圖的時(shí)候,就需要考慮一個(gè)問題。
若我們直接在iPhone6/7/8 里建立一個(gè)充滿屏幕的canvas,那其像素尺寸就是375*667。
這個(gè)尺寸并沒發(fā)揮高分辨率顯示器的優(yōu)勢(shì),我們需要先將其像素尺寸設(shè)置為7501334,然后再將其css 尺寸設(shè)置為375667。
這樣,就可以讓canvas畫布以高分辨率的姿態(tài)顯示在顯示器里。
代碼示例:
function resizeRendererToDisplaySize(renderer: WebGLRenderer) { const { width, height, clientWidth, clientHeight } = renderer.domElement; const [w, h] = [clientWidth * devicePixelRatio, clientHeight * devicePixelRatio]; const needResize = width !== w || height !== h; if (needResize) { renderer.setSize(w, h, false); } return needResize; }
上面的devicePixelRatio 就是設(shè)備像素密度,是window下的屬性,即window.devicePixelRatio。
其實(shí),有的時(shí)候若不刻意觀察,canvas 有沒有自適應(yīng)設(shè)備分辨率是很難看出的。
因此,若是對(duì)畫面的渲染質(zhì)量要求不高,可以什么都不做,這樣也能避免canvas 畫布像素尺寸變大后降低渲染效率的問題。
關(guān)于響應(yīng)式設(shè)計(jì)咱們就說到這,接下來咱們說一下three.js 里的內(nèi)置幾何體。
以上就是關(guān)于“three.js響應(yīng)式設(shè)計(jì)的方法”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對(duì)大家有幫助,若想了解更多相關(guān)的知識(shí)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。