您好,登錄后才能下訂單哦!
這篇“three.js響應式設計的方法”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“three.js響應式設計的方法”文章吧。
canvas 畫布的尺寸有兩種:
像素尺寸,即canvas畫布在高度和寬度上有多少個像素,默認是300*150
css 尺寸,即css 里的width和height
在web前端,dom元素的響應式布局一般是通過css 實現的。
而canvas 則并非如此,canvas 的響應式布局需要考慮其像素尺寸。
接下來,咱們就通過讓canvas 畫布自適應瀏覽器的窗口的尺寸,來說一下canvas 的響應式布局。
1.將之前的RenderStructure.tsx 復制粘貼一份,改名ResponsiveDesign.tsx,用于寫響應式布局。
2.將ResponsiveDesign.tsx 頁面添加到路由中。
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 的尺寸設置。
//renderer.setSize(innerWidth, innerHeight);
4.用css 設置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 導入ResponsiveDesign.tsx
import "./fullScreen.css";
效果如下:
由上圖可見,立方體的邊界出現了鋸齒,這就是位圖被css拉伸后失真導致的,默認canvas 畫布的尺寸只有300*150。
因此,我們需要用canvas 畫布的像素尺寸自適應窗口。
4.建立一個讓canvas 像素尺寸隨css 尺寸同步更新的方法。
resizeRendererToDisplaySize(renderer); // 將渲染尺寸設置為其顯示的尺寸,返回畫布像素尺寸是否等于其顯示(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) 是重置渲染尺寸的方法,在此方法里會根據w,h參數重置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 參數很重要,會用于判斷是否設置canvas 畫布的css 尺寸。
5.當canvas 畫布的尺寸變化了,相機視口的寬高比也需要同步調整。這樣我們拖拽瀏覽器的邊界,縮放瀏覽器的時候,就可以看到canvas 畫布自適應瀏覽器的尺寸了。
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 屬性是相機視口的寬高比
我們在WebGL 里說透視投影矩陣的時候說過,當相機視口的寬高比變了,相機的透視投影矩陣也會隨之改變,因此我們需要使用camera.updateProjectionMatrix() 方法更新透視投影矩陣。
至于我們為什么不把更新相機視口寬高比的方法一起放進resizeRendererToDisplaySize()里,這是為了降低resizeRendererToDisplaySize() 方法和相機的耦合度。具體要不要這么做視項目需求而定。
接下來咱們可以舉個例子,說一下canvas 畫布響應式布局的應用場合。
在下面的例子里,我們會給三維插圖一個縮放功能,從而更好的觀察細節。
1.新建一個Illustration 頁。
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); // 將渲染尺寸設置為其顯示的尺寸,返回畫布像素尺寸是否等于其顯示(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個正方形面組成的正多面體,故又稱正六面體。它有12條邊和8個頂點。其中正方體是特殊的長方體。立方體是一種特殊的正四棱柱、長方體、三角偏方面體、菱形多面體、平行六面體,就如同正方形是特殊的矩形、菱形、平行四邊形一様。立方體具有正八面體對稱性,即考克斯特BC3對稱性,施萊夫利符號 ,考克斯特-迪肯符號,與正八面體對偶。 </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種顏色(類似于四色問題)。 立方體是唯一能夠獨立密鋪三維歐幾里得空間的柏拉圖正多面體,因此立方體堆砌也是四維唯一的正堆砌(三維空間中的堆砌拓撲上等價于四維多胞體)。它又是柏拉圖立體中唯一一個有偶數邊面——正方形面的,因此,它是柏拉圖立體中獨一無二的環帶多面體(它所有相對的面關于立方體中心中心對稱)。 將立方體沿對角線切開,能得到6個全等的正4棱柱(但它不是半正的,底面棱長與側棱長之比為2:√3)將其正方形面貼到原來的立方體上,能得到菱形十二面體(Rhombic Dodecahedron)(兩兩共面三角形合成一個菱形)。 </p> <p> 立方體的對偶多面體是正八面體。 當正八面體在立方體之內: 正八面體體積: 立方體體積=[(1/3)×高×底面積]×2: 邊=(1/3)(n/2)[(n)/2]2: n=1: 6 星形八面體的對角線可組成一個立方體。 截半立方體:從一條棱斬去另一條棱的中點得出 截角立方體 超正方體:立方體在高維度的推廣。更加一般的,立方體是一個大家族,即立方形家族(又稱超方形、正測形)的3維成員,它們都具有相似的性質(如二面角都是90°、有類似的超體積公式,即Vn-cube=a等)。 長方體、偏方面體的特例。 </p> <p> 立方體是唯一能夠獨立密鋪三維歐幾里得空間的柏拉圖正多面體,因此立方體堆砌也是四維唯一的正堆砌(三維空間中的堆砌拓撲上等價于四維多胞體)。它又是柏拉圖立體中唯一一個有偶數邊面——正方形面的,因此,它是柏拉圖立體中獨一無二的環帶多面體(它所有相對的面關于立方體中心中心對稱)。 將立方體沿對角線切開,能得到6個全等的正4棱柱(但它不是半正的,底面棱長與側棱長之比為2:√3)將其正方形面貼到原來的立方體上,能得到菱形十二面體(Rhombic Dodecahedron)(兩兩共面三角形合成一個菱形)。 </p> </div> ); }; export default Illustration;
2.設置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頁新建一個路由
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.在首頁Basics.tsx中再開一個鏈接
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 基礎示例</h3> <ul> …… <li> <Link to="/Illustration">Illustration 三維插圖</Link> </li> </ul> </nav> ); }; export default Basics;
接下來,在首頁點擊Illustration 鏈接,就可以看到效果。
當今大多數的PC端和移動端顯示器都是HD-DPI顯示器。
HD-DPI 是High Definition-Dots Per Inch 的簡稱,意思是高分辨率顯示器。
不同設備的顯示器的分辨率是不一樣的。
做過測試的會知道,我們需要用不同的設備測試項目,比如下面微信小程序開發者工具里提供的機型。
以上圖中的iPhone6/7/8 為例:
375667 代表的手機的屏幕的物理尺寸,如果我們在其中建立一個100% 充滿屏幕的,那其尺寸就是375667。
Dpr 代表像素密度,2 表示手機屏幕在寬度上有3752 個像素,在高度上有6672 個像素,因此iPhone6/7/8 的屏幕的像素尺寸就是750*1334。
當我們在這種像素尺寸大于物理尺寸的高分辨率顯示器里繪圖的時候,就需要考慮一個問題。
若我們直接在iPhone6/7/8 里建立一個充滿屏幕的canvas,那其像素尺寸就是375*667。
這個尺寸并沒發揮高分辨率顯示器的優勢,我們需要先將其像素尺寸設置為7501334,然后再將其css 尺寸設置為375667。
這樣,就可以讓canvas畫布以高分辨率的姿態顯示在顯示器里。
代碼示例:
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 就是設備像素密度,是window下的屬性,即window.devicePixelRatio。
其實,有的時候若不刻意觀察,canvas 有沒有自適應設備分辨率是很難看出的。
因此,若是對畫面的渲染質量要求不高,可以什么都不做,這樣也能避免canvas 畫布像素尺寸變大后降低渲染效率的問題。
關于響應式設計咱們就說到這,接下來咱們說一下three.js 里的內置幾何體。
以上就是關于“three.js響應式設計的方法”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。