three.js(五):canvas 的响应式布局

news2024/11/21 2:33:50

1-canvas 的响应式布局

  • canvas 画布的尺寸有两种:
    • 像素尺寸,即canvas画布在高度和宽度上有多少个像素,默认是300*150
    • css 尺寸,即css 里的width和height
  • 在web前端,dom元素的响应式布局一般是通过css 实现的。
  • 而canvas 则并非如此,canvas 的响应式布局需要考虑其像素尺寸。
  • 基于上一个工程(three.js(四):react + three.js),通过让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%;
}

5.将fullScreen.css 导入ResponsiveDesign.tsx

import "./fullScreen.css";
  • 效果如下:
    在这里插入图片描述
  • 由上图可见,立方体的边界出现了锯齿,这就是位图被css拉伸后失真导致的,默认canvas 画布的尺寸只有300*150。
  • 因此,需要用canvas 画布的像素尺寸自适应窗口。

6.建立一个让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 画布的尺寸。setSize方法源码如下:
	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 尺寸。

7.当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 属性是相机视口的宽高比
  • 当相机视口的宽高比变了,相机的透视投影矩阵也会随之改变,因此需要使用camera.updateProjectionMatrix() 方法更新透视投影矩阵。
  • 为什么不把更新相机视口宽高比的方法一起放进resizeRendererToDisplaySize()里,这是为了降低resizeRendererToDisplaySize() 方法和相机的耦合度。具体要不要这么做视项目需求而定。

示例:自适应布局示例

  • 三维插图一个缩放功能,从而更好的观察细节

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" }}>
      <h2>three.js 基础示例</h2>
      <ul>
        ……
        <li>
          <Link to="/Illustration">Illustration 三维插图</Link>
        </li>
      </ul>
    </nav>
  );
};

export default Basics;
  • 效果如下如:
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/945429.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CocosCreator3.8研究笔记(一)windows环境安装配置

一、安装Cocos 编辑器 &#xff08;1&#xff09;、下载Cocos Dashboard安装文件 Cocos 官方网站Cocos Dashboard下载地址 &#xff1a; https://www.cocos.com/creator-download9下载完成后会得到CocosDashboard-v2.0.1-win-082215.exe 安装文件&#xff0c;双击安装即可。 …

gitlab-runner安装和部署项目

目录 1.安装gitlab-runner 1.1 添加官方仓库 1.2.1 安装最新版本 1.2.2 安装指定版本&#xff08;可选&#xff09; 1.2.3 更新runner&#xff08;可选&#xff09; 1.3 随便点开gitlab上的一个项目 1.4 gitlab-runner的注册 2.配置gitlab-runner 3.runner一些命令 gi…

【Three.js + Vue 构建三维地球-Part One】

Three.js Vue 构建三维地球-Part One Vue 初始化部分Vue-cli 安装初始化 Vue 项目调整目录结构 Three.js 简介Three.js 安装与开始使用 实习的第一个任务是完成一个三维地球的首屏搭建&#xff0c;看了很多的案例&#xff0c;也尝试了用 Echarts 3D地球的模型进行构建&#xf…

盲盒小程序开发必修技能

盲盒小程序怎么开发&#xff1f;实用教程分享 如何制作一个盲盒小程序以进行线上销售 一、注册并登录【乔拓云】账号&#xff0c;进入操作后台 首先&#xff0c;您需要注册并登录到您的【乔拓云】账号&#xff0c;进入操作后台。在那里&#xff0c;您将找到并点击“商城”中的“…

C语言每日一练------Day(5)

本专栏为c语言练习专栏&#xff0c;适合刚刚学完c语言的初学者。本专栏每天会不定时更新&#xff0c;通过每天练习&#xff0c;进一步对c语言的重难点知识进行更深入的学习。 今日练习题关键字&#xff1a;错误的集合 密码检查 &#x1f493;博主csdn个人主页&#xff1a;小小u…

ChatGPT的局限性及商业化应用限制讨论

首先&#xff0c;ChatGPT仅使用公开可用的信息&#xff0c;这是其第一个局限。如果基础信息缺失、过时、模糊或过于泛化&#xff0c;AI生成的内容就将不会准确。 只有在使用企业内部专有信息和知识创建特定的GPT时&#xff0c;才会出现真正的商业化解决方案。但对企业而言&…

驱动 - 20230829

练习 基于platform实现 在根节点下&#xff0c;增加设备树 myplatform {compatible"hqyj,myplatform";interrupts-extended<&gpiof 9 0>, <&gpiof 7 0>, <&gpiof 8 0>;led1-gpio<&gpioe 10 0>;reg<0x12345678 59>;}…

windows环境下QuestaSim软件的使用

文章目录 前言一、QuestaSim使用方法1、编译vlog2、映射vmap3、仿真vism4、ifndef和define&#xff08;常用&#xff09;5、QuestaSim的仿真界面6、完整QuestaSim仿真——TCL脚本 前言 2023.8.29 一、QuestaSim使用方法 1、编译vlog vlog&#xff1a;questasim的编译命令 -s…

利用逻辑回归判断病人肺部是否发生病变

大家好&#xff0c;我是带我去滑雪&#xff01; 判断肺部是否发生病变可以及早发现疾病、指导治疗和监测疾病进展&#xff0c;以及预防和促进肺部健康&#xff0c;定期进行肺部评估和检查对于保护肺健康、预防疾病和提高生活质量至关重要。本期将利用相关医学临床数据结合逻辑回…

【科研论文配图绘制】task6直方图绘制

【科研论文配图绘制】task6直方图绘制 task6 主要掌握直方图的绘制技巧&#xff0c;了解直方图含义&#xff0c;清楚统计指标的添加方式 1.直方图 直方图是一种用于表示数据分布和离散情况的统计图形&#xff0c;它的外观和柱形图相近&#xff0c;但它所 表达的含义和柱形图…

Elasticsearch 面试题

Elasticsearch 面试题 1.为什么要使用 Elasticsearch? 系统中的数据&#xff0c;随着业务的发展&#xff0c;时间的推移&#xff0c;将会非常多&#xff0c;而业务中往往采用模糊查询进行数据的搜索&#xff0c;而模糊查询会导致查询引擎放弃索引&#xff0c;导致系统查询数…

python 半正矢公式计算两GPS坐标距离

如题&#xff0c;直接上代码吧&#xff0c;需要的拿走。 # haversine公式计算两经纬度点距离 import math import os from DebugInfo.DebugInfo import *_earthR: int 6371393class __距离类:__m: floatdef __init__(self,m: float 0):self.__m mpropertydef km(self) ->…

【第四阶段】kotlin语言的set集合

1.set集合定义&#xff0c;不允许重复元素打印 package Stage4fun main() {//set集合定义&#xff0c;不允许重复元素打印// val set : Set<String> setOf<String>("java","kotlin","c","java","c")val set s…

C语言_分支和循环语句(2)

文章目录 前言一、for 循环1.1语法1.2 for 语句的循环控制变量1.3 一些 for 循环的变种 二、do ... while()循环2.1 do 语句的语法2.2 do ... while 循环中的 break 和 continue2.3 练习1 **- 计算n的阶乘**2. - **在一个有序数组中查找具体的某个数字 n** 二分查找算法&#x…

BES A2DP音乐与HFP通话默认音量配置

我V hezkz17进数字音频系统研究开发交流答疑群(课题组) 1 2

浅谈 Android Binder 监控方案

在 Android 应用开发中&#xff0c;Binder 可以说是使用最为普遍的 IPC 机制了。我们考虑监控 Binder 这一 IPC 机制&#xff0c;一般是出于以下两个目的&#xff1a; 卡顿优化&#xff1a;IPC 流程完整链路较长&#xff0c;且依赖于其他进程&#xff0c;耗时不可控&#xff0…

linux开启端口

目录 1.查看防火墙状态 1.1 开启防火墙 1.2 再次查看防火墙状态 2.开启指定端口 3. 重启防火墙 4.重新加载防火墙 5.查看已经开启的端口 1.查看防火墙状态 firewall-cmd --state 如果返回的是 not running&#xff0c;那么需要先开启防火墙&#xff0c; 1.1 开启防火…

MATLAB中符号变量的使用方法解析

简介 MATLAB中常常使用符号变量&#xff0c;这里定义符号变量的函数是syms 使用方法如下 syms x y z 其中&#xff0c;x、y、z 是符号变量&#xff0c;可以是任意字母、数字或下划线组合而成的字符串。 举例1&#xff1a; 代码 以下是一个简单的例子&#xff0c;演示如何…

省级智慧农业大数据平台项目规划建设方案[195页Word]

导读&#xff1a;原文《省级智慧农业大数据平台项目规划建设方案[195页Word]》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 1 农业大数据平台项目概述 1.1 建设…

Ant Design组件动态嵌套表单制作

使用Ant Design组件我们需要使用Form.List对表单进行操作 1.首先将Form.List放入form组件中&#xff0c;并name命名&#xff0c; 2.设置一个命名为数组&#xff0c;添加编辑和删除事件 3.以刚刚设置的数组设置map循环&#xff0c;可以在循环的的括号可以设置对嵌套表单控制 4.…