可视化面试准备

332 阅读32分钟

three.js 基于webgl
d3.js 基于svg
eCharts 基于canvas

Three.js和D3.js都是JavaScript库,但它们在功能和使用场景上有很大的区别。

Three.js

这是一个轻量级且功能强大的3D库,主要用于WebGL编程。它提供了很多实用的功能,可以方便的创建和显示3D内容。例如,你可以用Three.js创建3D游戏,或者在网页上展示3D模型。

three.js是一个基于WebGL的JavaScript库,用于创建和显示复杂的3D图形。它封装了WebGL的底层API,使开发者能够更容易地创建3D场景、模型和动画。three.js提供了丰富的工具和方法来处理3D渲染需求,例如光照、材质、阴影和相机控制等[1]

示例代码:
// 创建一个场景
var scene = new THREE.Scene();

// 创建一个相机
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

// 创建一个渲染器
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 创建一个立方体
var geometry = new THREE.BoxGeometry();
var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);

// 设置相机位置
camera.position.z = 5;

// 渲染循环
function animate() {
    requestAnimationFrame(animate);
    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
    renderer.render(scene, camera);
}

animate();

D3.js

d3.js(Data-Driven Documents)是一个用于数据可视化的JavaScript库。它主要用于将数据绑定到DOM元素,并通过数据驱动的方式生成动态的图表和图形。D3.js提供了强大的数据驱动的DOM操作能力,可以让你轻松地将数据映射到DOM元素,然后通过改变DOM元素的属性或样式来展示数据。

d3.js支持多种图形格式,包括SVG、Canvas和HTML

示例代码:
// 创建一个SVG容器
var svg = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 500);

// 创建一些数据
var data = [10, 20, 30, 40, 50];

// 创建一个条形图
svg.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x"function(d, i) { return i * 30; })
.attr("y"function(d) { return 500 - d * 10; })
.attr("width", 25)
.attr("height"function(d) { return d * 10; })
.attr("fill""blue");

主要区别

  • 用途:three.js主要用于3D图形和动画的创建,而d3.js主要用于数据可视化。
  • 技术基础:three.js基于WebGL,而d3.js主要使用SVG、Canvas和HTML。
  • 应用领域:three.js常用于游戏开发、虚拟现实和3D建模等领域;d3.js则广泛应用于数据分析、统计图表和信息可视化等领域12

总之,three.js和d3.js各有其独特的优势和应用场景,Three.js更偏向于3D建模和游戏开发,而D3.js则主要用于数据可视化。

面试题

重点

1.场景(Scene)、相机(Camera)、渲染器(Renderer)的作用和关系

  • 场景是 Three.js 中包含所有三维对象的容器。
  • 相机定义了观察场景的视点和投影方式。
  • 渲染器将场景和相机的内容绘制到画布上,以呈现最终的图像。核心组件
  • 场景提供需要渲染的内容(物体、光源等)。
  • 相机决定从什么角度观察场景。
  • 渲染器将场景和相机结合,生成最终的2D图像输出到屏幕。

场景是3D世界的舞台,相机是观察者的眼睛,渲染器是绘制画面的画笔。

2.几何体(Geometry)、材质(Material)、网格(Mesh)的区别与应用

  • 几何体定义了物体的形状和结构。(骨架)
  • 材质定义了物体的外观和如何反射光线。控制物体的颜色、光泽度、透明度、纹理贴图等视觉属性。 常见材质类型
    • MeshBasicMaterial:基础材质,不受光照影响(适用于静态颜色或贴图)。
    • MeshPhongMaterial:高光材质,模拟光滑表面(受光照影响)。
    • MeshStandardMaterial:基于物理渲染(PBR),支持金属度、粗糙度等属性。
  • 网格结合几何体与材质:将几何体(形状)和材质(外观)组合成一个可渲染的3D对象。

几何体是“形状”,材质是“外观”,网格是二者的结合体。

3.渲染循环(requestAnimationFrame)原理

在WebGL或Three.js等Web图形库中,渲染循环(Render Loop)是实现动态3D效果的核心机制,而requestAnimationFrame(简称rAF)是实现这一循环的关键API。以下是其工作原理、优势及实际应用的详细解析。

requestAnimationFrame是浏览器提供的原生API,用于请求浏览器在下次重绘(Repaint)前执行指定的回调函数,通常以每秒60次(60FPS)的频率运行(与屏幕刷新率同步)。

  • 替代方案对比
    • setInterval/setTimeout:固定时间间隔执行,可能因标签页隐藏或系统负载导致卡顿,且无法与屏幕刷新同步。
    • rAF优势
      • 自动匹配显示器刷新率(如60Hz、120Hz),避免过度渲染。
      • 标签页隐藏时自动暂停,节省CPU/GPU资源。

渲染循环的基本原理

graph TB
A[启动循环] --> B[调用requestAnimationFrame]
B --> C[执行回调函数: 更新场景/相机]
C --> D[渲染器.render(scene, camera)]
D --> B

代码示例(Three.js)

function animate() {
  requestAnimationFrame(animate); // 递归调用,形成循环
  
  // 更新物体状态(如旋转)
  mesh.rotation.x += 0.01;
  mesh.rotation.y += 0.01;
  
  // 渲染场景
  renderer.render(scene, camera);
}
animate(); // 启动循环

关键细节与优化 (1) 时间差(Delta Time)处理

  • 问题:直接固定增量(如+= 0.01)会导致不同刷新率设备速度不一致。
  • 解决:通过时间差(deltaTime)实现帧率无关动画。
    let prevTime = 0;
    function animate(currentTime) {
      requestAnimationFrame(animate);
      
      const deltaTime = (currentTime - prevTime) / 1000; // 转换为秒
      prevTime = currentTime;
      
      mesh.rotation.x += 1 * deltaTime; // 1弧度/秒
      renderer.render(scene, camera);
    }
    

(2) 循环的启动与停止

  • 停止循环:通过cancelAnimationFrame取消请求。
    let animationId;
    function startLoop() {
      function animate() {
        animationId = requestAnimationFrame(animate);
        // ...更新与渲染
      }
      animate();
    }
    
    function stopLoop() {
      cancelAnimationFrame(animationId);
    }
    

(3) 性能优化

  • 避免冗余计算:仅在物体状态变化时更新或渲染。
  • 分帧处理:将复杂计算分散到多帧中执行(如大数据量更新)。

常见问题 Q1: 为什么不用setInterval

  • setInterval无法保证与屏幕刷新同步,可能导致卡顿或跳帧。
  • 浏览器标签页隐藏时仍会执行,浪费资源。

Q2: requestAnimationFrame的兼容性如何?

  • 所有现代浏览器均支持,对于旧浏览器(如IE9)可使用polyfill:
    window.requestAnimationFrame = window.requestAnimationFrame || 
                                 window.mozRequestAnimationFrame || 
                                 window.webkitRequestAnimationFrame;
    

Q3: 如何实现更高帧率(如120FPS)?

  • 依赖显示器刷新率,若设备支持120Hz,rAF会自动匹配。无法强制超过物理限制。

D3.js 面试重点

1.data() 、 enter() 、 update() 、 exit() 的链式调用流程

在D3.js中,data()enter()update()exit() 是数据绑定的核心方法链,用于将数据(Data)与DOM元素(或图形元素)动态关联,并根据数据的变化创建、更新或删除元素。以下是它们的链式调用流程和详细解析。

方法作用
data()将数据数组绑定到选中的DOM元素(或图形元素),建立数据与元素的映射关系。
enter()返回一个“占位符”选择集,表示数据比当前元素多时,需要新增的元素
update()默认情况下是data()返回的“更新选择集”,表示数据与元素匹配的部分。
exit()返回需要删除的元素,表示数据比当前元素少时,多余的元素。
// 假设已有SVG容器和初始数据
const svg = d3.select("svg");
const data = [10, 20, 30];

// 绑定数据并处理元素的生命周期
const circles = svg.selectAll("circle")  // 选择所有circle(可能为空)
  .data(data)          // 绑定数据
  .join(               // D3 v5+推荐写法,合并enter/update/exit
    enter => enter.append("circle").attr("class", "new"),
    update => update.attr("class", "updated"),
    exit => exit.remove()
  );

// 传统写法(D3 v4及以下)
svg.selectAll("circle")
  .data(data)
  .enter()             // 处理新增数据
  .append("circle")    // 创建新元素
  .attr("r", d => d);  // 设置属性

svg.selectAll("circle")
  .data(data)
  .exit()              // 处理多余元素
  .remove();           // 删除元素

3. 分步详解
(1) data():数据绑定
  • 输入:一个数据数组(如[10, 20, 30])。
  • 输出:一个“更新选择集”(Update Selection),包含数据与DOM元素的映射关系。
  • 关键行为
    • 数据与元素按索引一一对应(第一个数据绑定到第一个DOM元素,依此类推)。
    • 如果数据比元素多,多余的数据会进入enter()选择集。
    • 如果元素比数据多,多余的元素会进入exit()选择集。
(2) enter():处理新增数据
  • 作用:为“数据比元素多”的部分生成占位符(Placeholder),表示需要新增的元素。
  • 典型操作
    • 通过append()创建新元素。
    • 设置新元素的初始属性(如位置、颜色)。

示例

svg.selectAll("circle")
  .data([10, 20, 30, 40]) // 假设原有3个circle,现在数据有4个
  .enter()                // 进入enter选择集(第4个数据)
  .append("circle")       // 创建第4个circle
  .attr("r", d => d);
(3) update():处理数据与元素匹配的部分
  • 默认行为data()返回的选择集本身就是“更新选择集”。
  • 典型操作
    • 更新已有元素的属性(如修改半径、颜色)。

示例

svg.selectAll("circle")
  .data([15, 25, 35]) // 更新数据
  .attr("r", d => d); // 直接操作更新选择集
(4) exit():处理多余元素
  • 作用:返回“元素比数据多”的部分,表示需要删除的元素。
  • 典型操作
    • 通过remove()删除多余元素。

示例

svg.selectAll("circle")
  .data([10, 20])     // 假设原有3个circle,现在数据只有2个
  .exit()             // 进入exit选择集(第3个circle)
  .remove();          // 删除第3个circle

4. 生命周期图示
graph TB
A[初始DOM元素] --> B[data绑定]
B --> C{数据 vs 元素}
C -->|数据多| D[enter新增元素]
C -->|匹配| E[update更新元素]
C -->|元素多| F[exit删除元素]

5. 实际应用示例

动态柱状图更新

// 初始数据
const data = [30, 50, 20];
const svg = d3.select("svg");

// 首次渲染
svg.selectAll("rect")
  .data(data)
  .join("rect")       // D3 v5+简化写法
  .attr("height", d => d)
  .attr("width", 20);

// 更新数据(新数据比旧数据多)
const newData = [40, 30, 60, 10];
svg.selectAll("rect")
  .data(newData)
  .join(
    enter => enter.append("rect").attr("fill", "green"), // 新增元素
    update => update.attr("fill", "blue"),              // 更新元素
    exit => exit.remove()                               // 删除元素
  )
  .attr("height", d => d)
  .attr("width", 20);

6. 常见问题
Q1: 为什么需要enter()exit()
  • 数据驱动:D3的核心思想是“数据决定元素”,而非手动操作DOM。
  • 动态响应:当数据变化时,自动处理元素的增删改,避免手动维护DOM状态。
Q2: join()是什么?
  • D3 v5+的新API:将enter()updateexit()合并为单个链式调用,简化代码。
  • 等效于传统写法
    .join("rect") 
    // 等同于:
    .enter().append("rect").merge(updateSelection).exit().remove()
    

Q3: 如何强制重新绑定数据?

  • 使用key函数确保数据与元素的正确匹配(避免按索引绑定):
    .data(data, d => d.id) // 根据id绑定而非索引
    

总结
  • data():绑定数据,建立映射关系。
  • enter()append():处理新增数据,创建元素。
  • update:更新已有元素属性。
  • exit()remove():清理多余元素。
  • join()(D3 v5+):简化生命周期管理。

通过这四个方法的链式调用,可以实现数据与DOM元素的动态同步,是D3.js实现数据可视化的核心机制。

2.如何绑定数据到DOM元素(如生成柱状图的 rect )

. 使用D3.js的数据绑定

D3.js提供了强大的数据绑定机制:

javascript

复制

下载

// 假设有以下数据
const data = [10, 20, 30, 40, 50];

// 选择容器并绑定数据
const svg = d3.select("svg");
const bars = svg.selectAll("rect")
  .data(data)
  .enter()
  .append("rect");

// 设置rect属性
bars.attr("x", (d, i) => i * 30)
    .attr("y", d => 100 - d)
    .attr("width", 25)
    .attr("height", d => d)
    .attr("fill", "steelblue");

3.常见图表的实现思路:折线图(路径生成器)、饼图(弧生成器)、力导向图(力模拟)

4.坐标轴(axis)、比例尺(scale)、颜色比例尺(scaleColor)的使用

5.如何实现鼠标事件(缩放、拖拽)?

6.过渡动画(transition)的关键参数(duration、ease)

区别

  • 对比两者差异:Three.js重3D渲染,D3.js重数据映射,结合项目场景说明选择原因
  • 实战案例:用Three.js做过虚拟展厅?用D3.js优化过百万数据渲染性能?
  • 周边知识:WebGL基础、SVG与Canvas区别、数据可视化设计原则

three.js基础知识

1.Three.js 是什么,以及它的主要用途是什么?

Three.js 是一个基于 WebGL 的 JavaScript 3D 渲染库,用于创建和渲染交互式的三维图形应用程序,如游戏、模拟和可视化。

2.请解释 Three.js 的基本架构和组件。

Three.js 架构包括场景(Scene)、相机(Camera)、渲染器(Renderer)、材质(Material)和几何体(Geometry)。这些组件协同工作以创建和渲染三维场景。

3.如何在网页中引入 Three.js 库?

你可以通过在 HTML 文件中添加 <script> 标签引入 Three.js 库,或使用 npm 或 yarn 安装 Three.js 并将其导入到你的 JavaScript 项目中。

4.Three.js 中的场景(Scene)、相机(Camera)和渲染器(Renderer)是什么,它们的作用是什么?

  • 场景是 Three.js 中包含所有三维对象的容器。
  • 相机定义了观察场景的视点和投影方式。
  • 渲染器将场景和相机的内容绘制到画布上,以呈现最终的图像。核心组件

5.请解释什么是材质(Material)和几何体(Geometry)以及它们在 Three.js 中的作用。

  • 材质定义了物体的外观和如何反射光线。基础材质(MeshBasicMaterial)、标准材质(MeshStandardMaterial)和物理材质(MeshPhysicalMaterial)等等
  • 几何体定义了物体的形状和结构。

6.如何创建一个简单的 Three.js 场景?

创建场景、相机和渲染器,然后将相机添加到场景中。创建几何体和材质,将它们组合成网格,然后将网格添加到场景中。最后,使用渲染器将场景渲染到画布上。

7.Three.js 中的光源类型有哪些,它们之间有什么区别?

  • AmbientLight(环境光) :

THREE.AmbientLight 类表示环境光。它是一种均匀分布的光,不具有特定的方向和位置。环境光对场景中的所有物体都产生相同的光照效果,不会产生阴影。它用于模拟全局光照,使场景中的物体不会完全黑暗。

  • DirectionalLight(平行光) :

THREE.DirectionalLight 类表示平行光,也称为太阳光。它具有平行的光线,可以模拟太阳光照射。平行光有方向,但不会随距离衰减,因此在场景中的物体上产生相似的阴影效果。通常用于模拟日光。

  • PointLight(点光源) :

THREE.PointLight 类表示点光源,它发射光线向各个方向辐射。点光源通常位于场景中的某个位置,可以产生逐渐减弱的光照效果,类似于灯泡的光。点光源会在场景中创建类似球形的光照区域,可以产生柔和的阴影。

  • SpotLight(聚光灯) :

THREE.SpotLight 类表示聚光灯,它发射锥形的光束,类似于手电筒的光束。聚光灯有位置和方向,可以模拟聚光效果。你可以调整聚光灯的光锥角度和范围。它也可以产生阴影。 HemisphereLight(半球光) :

THREE.HemisphereLight 类表示半球光,它模拟来自天空和地面的光线。半球光用于模拟室外场景,通常与环境贴图一起使用。它可以产生柔和的全局光照,但不会产生硬阴影。

  • RectAreaLight(矩形区域光) :

THREE.RectAreaLight 类表示矩形区域光,它是一种矩形形状的光源,可以用于模拟发光的平面。它通常用于模拟屏幕或窗户上的发光区域。

这些不同类型的光源用于模拟各种光照效果,根据场景的需要选择合适的光源类型非常重要。注意,不同的光源类型在性能上有差异,因此要根据场景的复杂性和性能需求来选择光源类型。此外,还可以组合多种光源类型来实现更复杂的光照效果。

8.如何加载和显示 3D 模型文件(例如 .obj、.fbx、.gltf)?

你可以使用 Three.js 的加载器(Loader)来加载不同格式的模型文件,然后创建网格并将其添加到场景中。

9.什么是纹理(Texture),如何在 Three.js 中应用纹理?

纹理是一种图像,可应用于材质以改变物体的外观。在 Three.js 中,你可以使用 THREE.Texture 类加载和应用纹理。

10.如何实现相机控制和场景导航?

Three.js 提供了多种相机控制器(例如 OrbitControls、FlyControls),你可以将其添加到你的相机上,以实现用户控制相机的旋转、缩放和平移,从而导航场景。

11.Three.js 中如何实现动画,包括基于时间的动画和骨骼动画?

你可以使用 requestAnimationFrame 或 Three.js 的动画循环来创建基于时间的动画。骨骼动画则涉及骨骼和动画混合器,用于控制模型的骨骼动作。

12.什么是渲染循环(Render Loop)?如何优化渲染性能?

渲染循环是一个持续的循环,用于更新和渲染 Three.js 场景。为了优化性能,可以使用 Frustum Culling、LOD 和合并几何体等技术来减少不必要的渲染操作。

13.Three.js 中的后期处理效果是什么,如何应用它们?

后期处理效果是在渲染场景后应用的图像效果,如模糊、色彩校正等。你可以使用 THREE.EffectComposer 来添加和配置后期处理效果。

14.如何在 Three.js 中实现阴影效果?

Three.js 支持阴影投射和接收。要启用阴影,你需要设置光源为投射阴影,并为接收阴影的物体设置 castShadow 和 receiveShadow 属性。

15.Three.js 中的物理引擎有哪些,如何集成它们?

Three.js 可以与物理引擎集成,例如 Cannon.js 或 Ammo.js。你需要加载物理引擎库,并将模型的物理属性和碰撞体积配置正确。

16.什么是 WebGL,它与 Three.js 之间的关系是什么?

WebGL 是一种用于在浏览器中渲染 2D 和 3D 图形的低级 JavaScript API。Three.js 则是建立在 WebGL 之上的高级库,简化了在 WebGL 上进行三维图形编程的复杂性。

17.什么是网格(Mesh),如何创建和修改网格对象?

网格是几何体和材质的结合体,它决定了一个物体的形状和外观。你可以通过创建一个 THREE.Mesh 实例来创建网格,然后可以修改网格的属性来调整它的外观和行为。

18.Three.js 中的坐标系是怎样的,如何进行坐标变换?

Three.js 使用右手坐标系,其中 x 轴指向右侧,y 轴指向上方,z 轴指向观察者。你可以使用矩阵变换来进行坐标变换,例如平移、旋转和缩放。

19.如何实现对象的运动和变换,例如平移、旋转和缩放?

你可以使用对象的变换属性,如 position、rotation 和 scale,来实现对象的平移、旋转和缩放。这些属性允许你在三维空间中调整对象的位置、方向和大小。

20.什么是 raycasting,如何在 Three.js 中使用它?

Raycasting 是用于检测光线与场景中对象的交点的技术。在 Three.js 中,你可以使用 THREE.Raycaster 类来执行 raycasting 操作,以确定鼠标点击的物体或光线与物体的交点。

21.请解释 Three.js 中的物体层级(Object Hierarchy)是如何工作的。

物体层级是 Three.js 中对象的父子关系。通过将一个对象添加为另一个对象的子对象,你可以构建复杂的层次结构,使得对象之间可以继承和共享变换等属性。

22.如何处理和加载不同格式的纹理映射(Texture Mapping)?

Three.js 支持多种纹理映射,包括颜色纹理、法线纹理、置换纹理等。你可以使用 THREE.TextureLoader 类来加载这些纹理,并将它们应用于材质。

23.Three.js 中如何创建自定义着色器和材质?

你可以使用 THREE.ShaderMaterial 类来创建自定义着色器和材质。这允许你编写自己的顶点着色器和片段着色器,以实现高度定制化的渲染效果。

24.什么是法线贴图(Normal Mapping)和环境贴图(Environment Mapping)?

法线贴图是一种用于模拟高细节表面的技术,环境贴图则模拟了物体周围的环境光照。这些技术可以增加渲染物体的细节和真实感。

25.如何实现渐变背景或天空盒(Skybox)?

渐变背景可以通过设置场景的背景颜色实现,而天空盒则需要加载一个立方体贴图作为背景。你可以使用 THREE.CubeTextureLoader 来加载天空盒贴图。

26.Three.js 中如何实现粒子系统(Particle System)?

你可以使用 THREE.Points 或 THREE.Sprite 来创建粒子系统。这些对象可以表示成千上万的小粒子,并使用自定义材质和纹理来呈现。

27.什么是卡通渲染(Toon Shading),如何实现它?

卡通渲染是一种图像渲染风格,通常用于模拟卡通或手绘风格的渲染。在 Three.js 中,你可以通过编写自定义的着色器来实现卡通渲染效果,例如通过使用阶梯化的光照来模拟阴影。

28.如何在 Three.js 中处理不同的设备和屏幕尺寸(响应式设计)?

响应式设计可以通过监听窗口大小变化,并根据不同的屏幕尺寸和设备类型来调整渲染参数、相机视锥体(Frustum)和UI布局等方面进行处理。

29.请解释 WebGL 渲染流水线(Rendering Pipeline)

WebGL 渲染流水线是一系列的步骤,用于将三维场景转化为最终的图像。这包括几何处理、光照计算、着色器执行和像素绘制等过程

30.Three.js 中的事件处理是如何工作的,如何处理用户输入?

Three.js 提供了鼠标和触摸事件监听器,你可以使用它们来捕捉用户的输入,例如点击、拖动和缩放等操作。通过事件处理,你可以实现交互性。

31.Three.js 中的 post-processing 效果是如何实现的?

后期处理效果是通过创建一个后期处理通道(Post-processing Pass)并将其添加到渲染循环中来实现的。这些通道可以应用各种效果,例如模糊、颜色校正和光照效果。

32.如何创建一个可嵌入到网页中的 Three.js 应用程序?

模型导入器用于加载外部 3D 模型文件到 Three.js 中。Three.js 支持多种模型导入器,如 THREE.OBJLoader、THREE.FBXLoader 和 THREE.GLTFLoader。

你可以创建一个 HTML 文件,引入 Three.js 库和你的 JavaScript 代码,然后在 HTML 中创建一个画布(Canvas)元素,将 Three.js 渲染器连接到该画布上。

33.什么是模型导入器(Model Loader)?Three.js 中有哪些常用的模型导入器?

模型导入器用于加载外部 3D 模型文件到 Three.js 中。Three.js 支持多种模型导入器,如 THREE.OBJLoader、THREE.FBXLoader 和 THREE.GLTFLoader。

34.如何实现阻尼效果(Damping)以平滑对象的运动?

阻尼效果可以通过逐渐减少物体的速度来实现。在 Three.js 中,你可以使用线性插值(Lerp)或指数衰减来实现阻尼效果。

35.Three.js 中的对象克隆(Clone)和实例化(Instance)有什么区别?

克隆是创建一个对象的完全副本,而实例化是创建一个共享几何体和材质的新对象。克隆对象具有独立的属性,而实例化对象与原始对象共享属性,更适合大规模渲染。

36.Three.js 中如何实现自定义的精确碰撞检测?

创建几何体:你需要创建用于检测碰撞的几何体。这可以是简单的形状,如立方体(BoxGeometry)或球体(SphereGeometry),或者是自定义的几何体。

创建物体:将几何体包装在物体(Mesh)中,同时为物体创建一个材质,以便渲染或进行其他操作。

更新物体位置:在每个渲染循环中,确保更新物体的位置和变换,以反映物体的当前状态。

碰撞检测:执行碰撞检测的关键步骤。这通常涉及两个几何体之间的交叉检测,以确定它们是否相交。以下是一些常见的碰撞检测方法:

边界框碰撞检测:这是最简单的碰撞检测方法,它涉及比较两个物体的边界框(Bounding Boxes)是否相交。你可以使用 Box3 类来表示边界框,并使用 intersectsBox() 方法来检测碰撞。 球体碰撞检测:如果你的物体是球体或包含球体的几何体,你可以使用球体碰撞检测,使用 Sphere 类和 intersectsSphere() 方法。 射线碰撞检测:这是一种更精确的方法,允许你发射射线并检测射线是否与物体相交。你可以使用 Raycaster 类来执行射线碰撞检测。 处理碰撞:一旦检测到碰撞,你可以执行相应的操作,例如停止物体的运动、改变物体的属性等。

下面是一个简单的示例,演示了基于射线碰撞检测的碰撞处理:

// 创建一个射线
const raycaster = new THREE.Raycaster();
const intersection = new THREE.Vector3();

// 在渲染循环中更新射线的位置
function updateRaycasterPosition() {
    // 更新射线的起点,例如相机位置
    raycaster.ray.origin.copy(camera.position);

    // 更新射线的方向,例如鼠标点击位置
    raycaster.ray.direction.set(0, 0, -1).applyQuaternion(camera.quaternion);
}

// 在碰撞检测的函数中
function checkCollision() {
    // 更新射线的位置
    updateRaycasterPosition();

    // 检测射线与物体的交点
    const intersects = raycaster.intersectObjects(objectsToCheck);

    if (intersects.length > 0) {
        // 发生碰撞,执行处理逻辑
        const hitObject = intersects[0].object;
        console.log("Collision with object: ", hitObject);
        // 这里可以执行碰撞后的操作
    }
}

// 在渲染循环中调用碰撞检测函数
function animate() {
    // 更新场景和相机
    // ...

    // 执行碰撞检测
    checkCollision();

    // 渲染场景
    renderer.render(scene, camera);

    // 继续下一帧动画
    requestAnimationFrame(animate);
}

使用 Raycaster 类来发射射线,然后检测射线与场景中的对象是否相交。如果相交,我们可以执行相应的碰撞处理逻辑。请注意,你需要在渲染循环中不断更新射线的位置,以保持碰撞检测的准确性。

37.如何在 Three.js 中创建自定义几何体(Custom Geometry)?

创建几何体的顶点和面:自定义几何体的第一步是定义其顶点和面。你需要创建一个顶点数组和一个面(三角形)数组,以描述几何体的形状。每个面由三个顶点组成。

const geometry = new THREE.Geometry();

// 添加顶点
const vertex1 = new THREE.Vector3(0, 0, 0);
const vertex2 = new THREE.Vector3(1, 0, 0);
const vertex3 = new THREE.Vector3(0, 1, 0);
geometry.vertices.push(vertex1, vertex2, vertex3);

// 添加面
const face = new THREE.Face3(0, 1, 2);
geometry.faces.push(face);

计算法向量:为了让渲染器知道如何着色几何体,需要计算每个面的法向量(法线)。法向量用于光照计算和阴影渲染。你可以使用 computeFaceNormals() 方法计算法向量。

geometry.computeFaceNormals();

创建材质:为了给自定义几何体赋予外观,你需要创建一个材质。材质定义了几何体的颜色、纹理、光照和材质属性。

const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });

创建 Mesh 对象:将自定义几何体和材质包装在 Mesh 对象中,以便将其添加到场景中并进行渲染。

const customMesh = new THREE.Mesh(geometry, material);
scene.add(customMesh);

渲染场景:在渲染循环中,确保你的自定义几何体被渲染到画布上。

function animate() {
    // 更新场景和相机
    // ...

    // 渲染场景
    renderer.render(scene, camera);

    // 继续下一帧动画
    requestAnimationFrame(animate);
}

// 启动渲染循环
animate();

创建了一个自定义几何体并将其添加到 Three.js 场景中。你可以根据需要自定义几何体的形状和材质,以满足项目的要求。

优化

1.什么是 LOD(Level of Detail),如何在 Three.js 中使用它?

LOD 是一种用于优化性能的技术,它根据物体距离相机的远近来加载不同级别的细节。在 Three.js 中,你可以使用 THREE.LOD 类来实现 LOD 效果。

2.请分享一个你在 Three.js 中的项目经验,描述你如何解决其中的挑战。

举例子:怎么去优化你的项目性能,总所周知,模型对浏览器很耗费性能,例如我是通过gltf-pipeline技术去压缩模型进行一个优化

3.Three.js 中的性能优化技巧有哪些?

  • 合并几何体(Geometry Merge),如果你有多个相似的物体,可以将它们的几何体合并成一个,以减少渲染调用的数量。这可以通过 BufferGeometry 来实现。
  • 使用纹理集合(Texture Atlas),将多个小纹理图像合并成一个大的纹理图集,减少纹理切换和内存占用。
  • 减少光源数量:光源是渲染成本较高的因素之一。尽量减少不必要的光源,使用平行光或环境光来模拟光照效果。
  • 使用 LOD(Level of Detail),LOD 技术根据物体距离相机的远近,加载不同级别的细节模型。这有助于减少物体的多边形数量。
  • 开启硬件加速:确保浏览器启用了硬件加速,以充分利用 GPU 渲染。
  • 使用 Web Workers:将一些计算密集型任务放在 Web Workers 中,以防止阻塞主线程。
  • 使用 Occlusion Culling:当物体被遮挡时,不需要渲染它们。使用视锥体剔除和遮挡剔除技术来提高渲染效率。
  • 纹理压缩:使用纹理压缩格式,如 DXT、ETC 或 PVRTC,以减少纹理内存占用。
  • 移动端优化:在移动设备上,要特别小心性能。使用适当的分辨率、减少光源和阴影,以提高性能。
  • 事件处理的最小化:不要在每一帧都附加事件监听器,只在需要时附加。事件处理可能会引入性能开销。
  • 使用 requestAnimationFrame:使用 requestAnimationFrame 来控制渲染循环,以确保在性能允许的情况下渲染。
  • 使用外部模型格式:如果可能的话,使用 GLTF 格式的模型,因为它是 Three.js 中性能较高的模型格式。
  • 内存管理:当你不再需要物体或纹理时,记得手动释放它们的内存资源,以避免内存泄漏。
  • 渲染器设置:在创建渲染器时,选择合适的渲染器设置,如 antialiasing(抗锯齿)和 shadows(阴影),以平衡性能和图形质量。
  • 使用 GPU 功能:尽量使用 GPU 进行计算,例如使用着色器来执行复杂的渲染操作。
  • 避免频繁的渲染大小变化:频繁改变渲染画布的大小可能会导致性能下降,尽量避免这种情况。
  • 压缩和合并着色器:将着色器代码压缩和合并,以减少 HTTP 请求和提高加载速度。
  • 定期检查性能:使用浏览器的性能分析工具(例如 Chrome 的开发者工具)来检查性能瓶颈,并优化应用。
  • 控制粒子数量:如果你使用粒子系统,确保粒子数量不会过多,以避免性能下降。
  • 测试不同设备:在不同设备和浏览器上测试你的应用,以确保它在各种环境中都能正常运行。

工具

1.有没有使用过 Three.js 的拓展库或插件,例如 Cannon.js、Tween.js 等?

Three.js 生态系统有许多有用的拓展库和插件,例如 Cannon.js 用于物理模拟,Tween.js 用于动画。你可以根据项目需求来集成这些库。

2.Three.js 中的虚拟现实(VR)和增强现实(AR)有哪些库和工具支持?

Three.js 可以与 WebXR API 集成,用于创建虚拟现实和增强现实应用。此外,还有一些第三方库,如 A-Frame,用于更轻松地创建 VR 和 AR 内容。

3.什么是物理引擎插件,如何在 Three.js 中使用它们?

物理引擎插件用于模拟物理行为,如碰撞、重力等。在 Three.js 中,你可以使用像 Cannon.js、Ammo.js 或 AmmoPlus 这样的物理引擎库,将其集成到你的项目中以实现物理模拟。

实现场景

1.Three.js 中的粒子系统是如何工作的,有哪些应用场景?

粒子系统是通过大量的小粒子来模拟效果,如火花、雨、雪等。在 Three.js 中,你可以使用 THREE.Points 或 THREE.Sprite 来创建粒子系统。

2.如何实现镜面反射和折射效果?

镜面反射和折射效果通常通过使用立方体贴图和反射向量来实现。Three.js 提供了 THREE.CubeCamera 来捕获环境贴图,以实现镜面反射。

3.Three.js 中如何实现立体声音效果?

Three.js 支持音频处理,你可以使用 THREE.AudioListener、THREE.PositionalAudio 等来实现立体声音效果,根据音源位置和听众位置来计算音频效果。

4.如何在 Three.js 中加载和播放视频?

你可以使用 HTML5 元素或 Three.js 的 THREE.VideoTexture 类来加载和播放视频,然后将视频纹理应用到材质上。

d3.js

1.解释什么是d3.js?

D3.js 是一个基于数据创建和操作文档的 JavaScript 库。它使用数字数据来驱动在网络浏览器中运行的动态交互式图形演示的形成和控制。

2.什么时候使用d3.js有帮助?

D3.js 在查看帐户详细信息、电子商务预算、人口等大量数据报告时非常有用。对于此类数据,数据可视化是理解、呈现和分析它的最佳方式。

3.解释什么是 SVG?

SVG 或可缩放矢量图形 (SVG) 是一种 XML,用于确定二维矢量图形的标记语言。SVG 对于图形至关重要,就像 XHTML 对于文本一样。

4.解释D3.js如何选择方法?

D3.js 选择方法使用 CSS3 选择器选择 DOM 元素。D3 查看文档并选择组成标签主体的第一个后代 DOM 元素。一旦选择了元素,D3.js 便可让您对所选元素实施运算符。

5.解释一下d3.js比例?

D3.js 秤附带

  • 定量尺度:  定量尺度具有连续的域,如日期、时间、实数等。
  • 序数尺度:  虽然序数尺度适用于类别、颜色、名称等单独的领域,
  • 线性尺度:  它将域区间中的一个值转换为范围区间中的一个值
  • 身份量表:  它对像素值有好处
  • 功率和对数尺度:  它用于指数增加的值,如 log、pow、sqrt

D3.js 面试问题

6.请提及 d3.js 中有哪些可用的滑块?

d3.js 中可用的滑块有

  • 默认滑块
  • 带起始值的滑块
  • 带滑动事件的滑块
  • 带滑动事件的滑块
  • 带有自定义轴的滑块
  • 具有最小值、最大值和步进值的滑块
  • 垂直滑块

7.解释一下d3.js中的Domain是什么?

在 d3.js 中,域是数据集的开始和结束。它可以是任何可以在 JavaScript 中比较的值。如果数据集发生变化,域也必须改变。

8.解释一下d3.js中“路径数据生成器”的作用是什么?

为了将我们的数据转换为 SVG 路径命令,我们必须告诉路径数据生成器如何从数据中访问 y 和 x 坐标。

9.请提及路径生成器包括什么?

路径生成器包括

  • svg.line-创建新行生成器
  • svg.line.radial-制作一个新的径向线生成器
  • svg.area – 创建一个新的区域生成器
  • svg.chord – 创建一个新的和弦生成器等等

10.解释一下d3.js enter方法的作用?

D3.js enter 方法返回 虚拟输入选择 来自数据运算符。此方法仅适用于数据运算符,因为此类数据运算符是唯一返回三个虚拟选择的运算符。

11.解释一下D3.js Axis组件的作用是什么?

D3.js 轴组件可轻松向任何图形添加水平轴和垂直轴。它会自动显示 D3.js 比例的参考线。它还允许您绘制水平轴线、轴刻度和正确的间距,以使轴看起来合适。

12.请提及在 d3.js 中创建简单轴所用的命令?

在 d3.js 中创建简单轴的命令是 var xAxis = d3.svg.axis()。

13.解释什么是 SVG 组元素?

SVG 组元素用于将 SVG 元素分组在一起;每个 SVG 组元素都是由子 SVG 元素组成的容器。它定义为和。

14.解释如何同时管理多个班级?

要一次设置多个类,可以使用对象文字作为

selection.classed({ 'foo':true, 'bar': false})

15.解释一下 d3.js 中的转换是什么?

d3.js 中的过渡会随着时间的推移逐渐插入属性和样式,过渡用于动画目的。它仅基于两个关键帧, 开始,  和 end。起始关键帧定义 DOM 的当前状态,而结束关键帧则是一组您指定的样式、属性和其他属性。

16.请提及在 d3.js 中插入两个对象的命令是什么?

在 d3.js 命令中插入两个对象 d3.interpolateObject(a,b) 对象插值对于数据空间插值尤其有用,其中插值的是数据而不是属性值。

17.解释命令“d3.ascending(a,b)”的用途是什么?

此命令是用于自然顺序的比较器函数,可以与内置的排列 排序方法按升序排列元素。

18.解释一下 d3.js 中如何调用 XML 文件?

通过使用命令 d3.xml(url[mimeType][,回调]) XML 文件可以调用。此命令将在指定的 网址如果声明了回调,则请求将立即使用 GET 方法处理,并且在文件加载或请求失败时调用回调。

19.如果 d3.js 中没有为 XML 文件指定回调会发生什么?

如果没有指定回调,则可以使用 xhr.get 发出返回的请求并使用 xhr.on 处理。### 21)请说出在d3.js中加入指定数据数组的命令?

要在 d3.js 中加入指定的数据数组,可以使用命令selection.data([values[,key]])。 价值观 这里指定选择中每个组的数据,而  函数决定数据如何连接到元素。

20. 请提及命令 d3.csv.parseRows(string[,accessor]) 的作用是什么?

此命令解析指定的字符串(即 CSV 文件的内容),返回代表解析的行的数组数组。

21.请提及 d3.js 中“Enter”和“Exit”选择有什么用途?

通过使用  “进入”和“退出”  在 d3.js 中选择,您可以为传入数据创建新节点并消除不再需要的传出节点。