溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

three.js響應(yīng)式設(shè)計(jì)的方法

發(fā)布時(shí)間:2022-04-25 13:48:30 來源:億速云 閱讀:159 作者:iii 欄目:開發(fā)技術(shù)

這篇“three.js響應(yīng)式設(shè)計(jì)的方法”文章的知識(shí)點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“three.js響應(yīng)式設(shè)計(jì)的方法”文章吧。

1-canvas 的響應(yīng)式布局

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";

效果如下:

three.js響應(yīng)式設(shè)計(jì)的方法

由上圖可見,立方體的邊界出現(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é)。

three.js響應(yīng)式設(shè)計(jì)的方法

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 鏈接,就可以看到效果。

2-自適應(yīng)設(shè)備分辨率

當(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ī)型。

three.js響應(yīng)式設(shè)計(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è)資訊頻道。

向AI問一下細(xì)節(jié)

免責(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)容。

AI