新技术

新技术

以下是近年来前端领域值得关注的几项新技术和趋势,涵盖框架、工具链、性能优化和新兴规范,帮助开发者提升效率并适应现代开发需求:

1. 新一代前端框架

1.1 React Server Components (RSC)

  • 核心思想:将组件渲染逻辑从客户端移到服务器,减少客户端JS体积,提升首屏性能。
  • 优势
    • 支持流式渲染(Streaming SSR),逐步发送HTML。
    • 直接访问后端服务(如数据库),避免客户端额外API请求。
  • 应用场景:内容密集型应用(如博客、电商详情页)。
  • 现状:Next.js 13+ 已集成,逐步成为全栈框架标配。

1.2 Qwik

  • 亮点:主打“瞬间启动”(Instant-On),通过序列化DOM状态实现超快 hydration。
  • 原理:延迟加载JS,仅按需激活交互部分。
  • 适合场景:对TTI(Time to Interactive)要求极高的营销页、低端设备应用。

1.3 Astro

  • 创新点:“岛屿架构”(Islands Architecture),默认服务端渲染静态内容,按需激活交互组件。
  • 优势
    • 极低的客户端JS体积(可接近传统MPA)。
    • 支持React、Vue、Svelte等框架混合开发。
  • 适用场景:内容为主的网站(如文档站、博客)。

2. 工具链革新

2.1 Vite

  • 定位:下一代前端构建工具,替代Webpack。
  • 核心优化
    • 基于ESM的按需编译,启动速度提升10倍以上。
    • 内置Rollup打包,支持SWC/Rust优化。
  • 生态:已覆盖React、Vue、Svelte等主流框架模板。

2.2 Turbopack (来自 Vercel)

  • 背景:Webpack作者Tobias新作,定位为“增量式超级打包工具”。
  • 性能:比Vite快5倍,适合超大型项目(如含5万模块的代码库)。
  • 现状:仍在Alpha阶段,但值得关注。

2.3 Bun

  • 目标:替代Node.js的All-in-One工具链(运行时、包管理、打包)。
  • 优势
    • 极速npm包安装(利用系统级Zig语言)。
    • 内置兼容Node/Web API的JavaScript运行时。
  • 潜力:可能重塑前端工具生态。

3. 状态管理新思路

3.1 Zustand

  • 特点:轻量级(<1KB)、基于React Hooks的状态管理。
  • 优势:无样板代码,支持中间件(如持久化、Immer不可变更新)。
  • 适用:中小型应用快速开发。

3.2 Jotai

  • 设计:原子化状态管理,灵感来自Recoil。
  • 亮点:自动依赖追踪、零渲染浪费,适合细粒度状态更新。

4. 性能优化技术

4.1 Partytown

  • 问题:第三方脚本(如分析、广告)阻塞主线程。
  • 方案:将脚本移至Web Worker执行,释放主线程。
  • 效果:提升LCP(Largest Contentful Paint)和交互响应速度。

4.2 React Forget(编译时自动Memoization)

  • 目标:通过编译器自动生成useMemo/useCallback,减少手动优化成本。
  • 进展:Meta内部测试中,可能随未来React版本发布。

5. CSS 新特性

5.1 容器查询(Container Queries)

  • 能力:组件根据容器尺寸(而非视口)自适应样式。
  • 用法
    1
    2
    3
    4
    5
    6
    .card {
    container-type: inline-size;
    }
    @container (min-width: 400px) {
    .title { font-size: 1.2rem; }
    }
  • 支持:Chrome/Safari已实现,Firefox 2023年支持。

5.2 层叠样式(CSS Cascade Layers)

  • 作用:控制样式优先级,避免!important滥用。
  • 示例
    1
    2
    3
    @layer base, theme;
    @layer base { button { color: red; } }
    @layer theme { button { color: blue; } } /* 最终应用blue */

6. 新兴语言与标准

6.1 TypeScript 5.0+

  • 新特性
    • satisfies运算符:验证类型同时保留字面量类型。
    • 装饰器标准落地(ECMAScript Stage 3)。
    • 更快的类型检查和构建速度。

6.2 WebAssembly(WASM)

  • 前端应用
    • 图像/音视频处理(如FFmpeg.wasm)。
    • 浏览器内AI推理(如TensorFlow.js + WASM后端)。
  • 工具链:Rust/Go语言编译为WASM,通过wasm-pack与前端集成。

7. 低代码/无代码工具

  • 代表平台
    • Webflow:可视化响应式网站搭建。
    • Builder.io:基于React的可视化编辑,支持CMS集成。
    • Tldraw:开源白板工具,展示协作类应用开发趋势。

总结

前端技术正朝着更快的工具链更智能的框架更精细的性能控制更强大的原生能力方向发展。开发者应关注:

  1. 按需学习:结合项目需求选择技术(如内容站优先Astro,管理后台选React+Next.js)。
  2. 性能优先:拥抱Vite、Turbopack等现代工具,善用Partytown优化第三方脚本。
  3. 全栈能力:掌握Server Components等前后端协作模式。
  4. 标准化:跟进CSS新特性、TypeScript和WASM生态。
    保持对新技术的敏感度,同时避免盲目追逐热点,才能在快速迭代的前端生态中高效构建可靠应用。

热更新

热更新

热更新

前端热更新(Hot Module Replacement,简称HMR)是一种在应用运行时替换、添加或删除模块,而无需重新加载整个页面的技术。以下是几种处理前端热更新的方法:

  1. WebSocket通信
    • 通过WebSocket与服务器建立持久连接,服务器在检测到文件变化时通过WebSocket向客户端推送消息。客户端接收到消息后,根据消息类型(如HTML、CSS或JS文件更新)执行相应的更新操作。例如,如果是CSS文件更新,可以重新加载CSS文件;如果是JS文件更新,则可能需要重新加载模块或执行特定的更新逻辑。
  2. HMR API
    • 使用模块热替换(HMR)API,如import.meta.hot,在Vite或Webpack等现代前端构建工具中实现热更新。这些API允许模块告知系统它们可以处理更新,例如import.meta.hot.accept()用于接受更新,import.meta.hot.dispose()用于在模块被替换前执行清理操作。
  3. 模块依赖图和热更新边界
    • 构建工具(如Vite)会维护一个模块依赖图,并在检测到文件变化时更新受影响的模块节点属性。然后,找出热更新边界模块,通过WebSocket通知客户端更新,并带上边界模块信息。
  4. 客户端执行热更新方法
    • 客户端接收到更新信息后,根据更新类型作出对应的更新行为。对于JS模块的热更新,客户端会找到对应的边界模块的热更新回调并执行以完成最终的更新。
  5. JSONP请求获取更新
    • 在Webpack中,可以使用JSONP请求获取最新的模块代码,因为JSONP请求的代码可以直接执行。这是在客户端获取更新代码的一种方式。
  6. webpackHotUpdate方法
    • 当客户端获取到最新的代码后,可以通过window.webpackHotUpdate方法更新模块缓存,替换旧模块,并执行新的模块代码。
  7. 插件支持
    • 对于不同的语言环境,如React、Vue或Svelte,可以使用对应的插件来支持HMR API的调用处理,例如@vitejs/plugin-react@vitejs/plugin-vue@vitejs/plugin-svelte

网络请求

网络请求

fetch和xhr的区别

FetchXMLHttpRequest(XHR)都是用于在浏览器中发起网络请求的工具,但它们在设计、使用方式和特性上有许多区别。以下是它们的主要区别:

1. 设计和语法

  • XMLHttpRequest (XHR)
    • 设计:较早出现,主要用于实现 AJAX(Asynchronous JavaScript and XML)功能,允许网页在不重新加载页面的情况下与服务器交互。
    • 语法:较为复杂,需要手动设置请求方法、URL、回调函数等。
    • 示例代码
      1
      2
      3
      4
      5
      6
      7
      8
      var xhr = new XMLHttpRequest();
      xhr.open('GET', 'https://example.com/data', true);
      xhr.onreadystatechange = function () {
      if (xhr.readyState === 4 && xhr.status === 200) {
      console.log(xhr.responseText);
      }
      };
      xhr.send();
  • Fetch
    • 设计:是现代的网络请求接口,基于 Promises 设计,语法更简洁,更符合现代 JavaScript 的编程习惯。
    • 语法:使用 Promise,支持链式调用,代码更简洁。
    • 示例代码
      1
      2
      3
      4
      fetch('https://example.com/data')
      .then(response => response.json())
      .then(data => console.log(data))
      .catch(error => console.error('Error:', error));

2. 返回值

  • XMLHttpRequest
    • 返回值是一个 XMLHttpRequest 对象,通过 onreadystatechange 事件和 readyState 属性来处理响应。
    • 需要手动检查 readyStatestatus 来判断请求是否成功。
  • Fetch
    • 返回值是一个 Promise,直接返回响应对象。
    • 使用 .then().catch() 来处理成功和失败的情况,更符合现代异步编程模式。

3. 错误处理

  • XMLHttpRequest
    • 错误处理较为复杂,需要在 onreadystatechange 中检查 statusstatusText
    • 网络错误(如请求超时、网络中断等)可能不会触发 onreadystatechange
  • Fetch
    • 错误处理更简单,通过 .catch() 捕获网络错误。
    • 但需要注意,fetch 只会在网络错误时触发 .catch(),如果服务器返回 HTTP 错误状态码(如 404、500),需要手动检查 response.okresponse.status

4. 功能支持

  • XMLHttpRequest
    • 支持同步请求(async = false),但不推荐使用,因为会阻塞主线程。
    • 支持多种请求方法(GET、POST、PUT、DELETE 等)和多种响应格式(如 JSON、XML、文本等)。
  • Fetch
    • 默认只支持异步请求,无法实现同步请求。
    • 支持多种请求方法和响应格式,但语法更简洁,例如通过 response.json()response.text() 等方法直接解析响应内容。
    • 支持更现代的特性,如 CORS(跨域资源共享)预检请求、请求头和响应头的处理等。

5. 兼容性

  • XMLHttpRequest
    • 兼容性较好,几乎所有现代浏览器和较旧浏览器都支持。
  • Fetch
    • 在较新的浏览器中广泛支持,但在一些较旧的浏览器(如 IE)中可能需要使用 polyfill(如 whatwg-fetch)来兼容。

6. 使用场景

  • XMLHttpRequest
    • 适用于需要对请求和响应进行精细控制的场景,例如需要处理进度事件、上传文件等。
  • Fetch
    • 适用于大多数现代的网络请求场景,尤其是需要简洁代码和现代异步编程支持的场景。

总结

  • 如果你正在开发一个现代的 Web 应用,并且需要简洁的语法和现代的异步编程支持,建议使用 Fetch
  • 如果你需要兼容较旧的浏览器,或者需要对请求和响应进行更精细的控制(如同步请求、进度事件等),可以使用 XMLHttpRequest

00 canvas

00 canvas

目录

webgl

  • webgl 01 入门
  • webgl 02 多图形绘制和变换
  • webgl 03 颜色和纹理
  • webgl 04 原理 OpenGL ES 语言
  • webgl 05 三维世界
  • webgl 06 光照
  • webgl 07 进阶
  • webgl 08 常用的数学

threejs

  • threejs 01 入门
  • threejs 02 组件
  • threejs 03 光源
  • threejs 04 材质
  • threejs 05 几何体
  • threejs 06 动画和相机
  • threejs 07 纹理
  • threejs 08 粒子

Canvas API 和 WebGL API

是HTML5新增的一个 DOM 元素

  • 二维图形可以使用 ( Canvas API 或 WebGL API)
    • CanvasRenderingContext2D 
    • canvas.getContext(‘2d’)
  • 三维图形使用 WebGL API
    • WebGLRenderingContext 
    • canvas.getContext(‘webgl ‘)
    • canvas.getContext(‘webgl2’)

WebGL

webgl是一种3D绘图协议,衍生于 OpenGL ES2.0,可以结合 Html5 和 JavaScript 在网页上绘制和渲染二/三维图形。

  • 数据可视化
  • 图形/游戏引擎
  • 交互演示,图像渲染
  • 室内设计
  • 城市规划

00 Cesium

00 Cesium

常用 api

Cesium 是一个功能强大的 3D 地球可视化引擎,提供了丰富的 API 用于开发各种地理信息系统(GIS)应用。以下是一些常用 API 的介绍和示例代码,帮助你快速上手。

1. 初始化 Cesium Viewer

这是使用 Cesium 的第一步,用于创建一个 3D 地球视图。

1
2
3
4
5
6
var viewer = new Cesium.Viewer('cesiumContainer', {
terrainProvider: Cesium.createWorldTerrain(), // 启用地形
imageryProvider: Cesium.createWorldImagery(), // 启用影像
scene3DOnly: true, // 仅使用 3D 场景
shouldAnimate: true // 自动动画
});

2. 添加实体(Entity)

实体是 Cesium 中用于表示地理对象的基本单位,可以是点、线、面等。

1
2
3
4
5
6
7
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706),
point: {
pixelSize: 10, // 点的大小
color: Cesium.Color.RED // 点的颜色
}
});

3. 添加多段线(Polyline)

用于绘制连接多个点的线条,常用于路径规划或连接线。

1
2
3
4
5
6
7
8
9
10
viewer.entities.add({
polyline: {
positions: Cesium.Cartesian3.fromDegreesArray([
-123.0744619, 44.0503706,
-123.0744619, 44.0503706
]),
width: 5, // 线宽
material: Cesium.Color.BLUE // 线的颜色
}
});

4. 添加多边形(Polygon)

用于绘制多边形区域,例如国家边界或地理区域。

1
2
3
4
5
6
7
8
9
10
viewer.entities.add({
polygon: {
hierarchy: Cesium.Cartesian3.fromDegreesArray([
-123.0744619, 44.0503706,
-123.0744619, 44.0503706,
-123.0744619, 44.0503706
]),
material: Cesium.Color.RED.withAlpha(0.5) // 半透明颜色
}
});

5. 添加标签(Label)

用于在地图上显示文本标签。

1
2
3
4
5
6
7
8
9
10
11
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706),
label: {
text: 'Hello Cesium!',
font: '14pt monospace',
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
outlineWidth: 2,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0, -9)
}
});

6. 添加模型(Model)

用于加载 3D 模型,例如建筑物、飞机等。

1
2
3
4
5
6
7
8
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706),
model: {
uri: 'path/to/model.glb', // 模型文件路径
scale: 1.0, // 模型缩放比例
minimumPixelSize: 128 // 最小像素大小
}
});

7. 设置相机(Camera)

用于控制视图的视角和位置。

1
2
3
4
5
6
7
8
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, 500000), // 飞往指定位置
orientation: {
heading: Cesium.Math.toRadians(0), // 方向
pitch: Cesium.Math.toRadians(-90), // 倾斜角度
roll: 0.0 // 滚动角度
}
});

8. 事件监听

用于监听用户的交互事件,例如鼠标点击。

1
2
3
4
5
6
viewer.screenSpaceEventHandler.setInputAction(function(event) {
var picked = viewer.scene.pick(event.position);
if (Cesium.defined(picked)) {
console.log('点击了对象:', picked);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

9. 时间动态效果

用于控制时间轴和动态效果。

1
2
3
4
5
6
viewer.clock.multiplier = 10; // 设置时间加速倍数
viewer.clock.startTime = Cesium.JulianDate.now(); // 设置开始时间
viewer.clock.currentTime = Cesium.JulianDate.now(); // 设置当前时间
viewer.clock.stopTime = Cesium.JulianDate.addSeconds(Cesium.JulianDate.now(), 60, new Cesium.JulianDate()); // 设置结束时间
viewer.clock.clockStep = Cesium.ClockStep.SYSTEM_CLOCK_MULTIPLIER; // 设置时间步进
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; // 设置时间范围

10. 加载外部数据

例如加载 GeoJSON 数据。

1
2
3
4
5
6
7
var geoJsonDataSource = new Cesium.GeoJsonDataSource.load('path/to/geojson.json', {
stroke: Cesium.Color.RED,
fill: Cesium.Color.BLUE,
strokeWidth: 3
}).then(function(dataSource) {
viewer.dataSources.add(dataSource);
});

11. 自定义材质(Material)

用于创建自定义的视觉效果,例如流动的线条。

1
2
3
4
5
6
7
8
9
10
11
12
13
var polyline = viewer.entities.add({
polyline: {
positions: Cesium.Cartesian3.fromDegreesArray([
-123.0744619, 44.0503706,
-123.0744619, 44.0503706
]),
width: 5,
material: new Cesium.PolylineGlowMaterialProperty({
color: Cesium.Color.BLUE,
glowPower: 0.7
})
}
});

12. 动态更新实体

用于根据时间或其他条件动态更新实体的属性。

1
2
3
4
5
6
7
8
9
var entity = viewer.entities.add({
position: new Cesium.CallbackProperty(function(time) {
return Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706);
}, false),
point: {
pixelSize: 10,
color: Cesium.Color.RED
}
});

13. 添加自定义控件

例如添加一个按钮来控制相机。

1
2
3
4
5
6
7
8
9
10
11
var button = document.createElement('button');
button.textContent = '飞往北京';
button.style.position = 'absolute';
button.style.top = '10px';
button.style.left = '10px';
document.body.appendChild(button);
button.addEventListener('click', function() {
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(116.397428, 39.90923)
});
});

14. 加载地形和影像

用于加载高精度的地形和影像数据。

1
2
3
4
5
6
7
viewer.terrainProvider = Cesium.createWorldTerrain({
requestWaterMask: true, // 请求水体掩模
requestVertexNormals: true // 请求顶点法线
});
viewer.imageryProvider = Cesium.createWorldImagery({
style: Cesium.IonWorldImageryStyle.LAND_OCEAN
});

15. 性能优化

例如限制帧率和减少渲染负载。

1
2
viewer.scene.maximumRenderTimeChange = 1000.0; // 设置最大渲染时间
viewer.scene.screenSpaceCameraController.enableCollisionDetection = false; // 禁用碰撞检测

这些 API 是 Cesium 中最常用的部分,通过它们可以实现大部分的 3D 地球可视化功能。如果你有更具体的需求,比如加载特定格式的数据或实现复杂的交互效果,可以进一步说明,我可以为你提供更详细的代码示例。

threejs 01 入门

threejs 01 入门

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
*{
margin: 0;
padding: 0;
}
</style>
<script src="../lib/three/three.js"></script>
</head>
<body>
</body>
</html>
<script>
// 创建一个场景
const scene = new THREE.Scene();
// 创建一个相机 视点
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
// 设置相机的位置
camera.position.set(0,0,20);
// 创建一个渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染器尺寸
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 添加一个立方体
// 定义了一个立方体的对象
const cubeGeometry = new THREE.BoxGeometry(2, 2, 2);
// 创建材质
const cubeMaterial = new THREE.MeshLambertMaterial({ color: 0xff0000, wireframe: false });
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
// 添加到场景里
scene.add(cube);
// 添加一个球体
const sphereGeometry = new THREE.SphereGeometry(1,10,10);
const sphereMaterial = new THREE.MeshLambertMaterial({ color: 0x00ff00, wireframe: false });
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.x = 3;
sphere.position.y = 1;
scene.add(sphere);
// 添加灯光
const spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-10,10,10);
scene.add(spotLight);
const animation = () => {
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
// 渲染
renderer.render(scene, camera);
requestAnimationFrame(animation);
}
animation()
</script>

webgl 01 入门

webgl 01 入门

hellow world

一个最简单的 canvas 实例

1
2
3
4
const ctx = document.getElementById('canvas')
const c = ctx.getContext('2d')
c.fillStyle = 'red'
c.fillRect(10,10,100,100)

一个最简单的 webgl 实例

1
2
3
4
5
6
7
8
9
const ctx = document.getElementById('canvas')
const gl = ctx.getContext('webgl')
//gl.clearColor(r,g,b,a) 取值区间为 0.0~1.0
gl.clearColor(1.0,0.0,0.0,1.0) // red 1.0 green 0.0 blue 0.0 alpha 1.0
//gl.clear(buffer) 清空canvas
//gl.COLOR_BUFFER_BIT 清空颜色缓存 ✅常用
//gl.DEPTH_BUFFER_BIT 清空深度缓冲区
//gl.STENCIL_BUFFER_BIT 清空模板缓冲区
gl.clear(gl.COLOR_BUFFER_BIT)

着色器

着色器就是让开发者自己去编写一段程序,用来代替固定渲染管线,来处理图像的渲染。2d 的渲染比较简单,3d 的渲染需要着色器的帮助。

1
2
3
4
5
---
title: 顶点着色器
---
flowchart LR
顶点着色器-- 用来描述顶点的特性 -->通过计算获取位置信息

顶点是指⼆维三维空间中的⼀个点,可以理解为一个个坐标。

1
2
3
4
5
---
title: 片元着色器
---
flowchart LR
片元着色器-- 进行逐片元处理程序 -->通过计算获取颜色信息

片元可以理解为一个个像素。

特点

  • 区分大小写
  • 结尾;必须
  • 入口 main 函数
  • 强语言类型
    • 不能以 gl_, webgl_开头

工作流程

JavaScript 读取相关着色器信息,传递给 webgl 并进行使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
flowchart TB
subgraph 浏览器
渲染成功
end
subgraph WebGL程序
subgraph 片元着色器
颜色
end
subgraph 顶点着色器
坐标系
end
end
subgraph JavaScript程序
着色器源码
end
JavaScript程序 --> WebGL程序
WebGL程序 --> 浏览器
1
2
3
4
5
6
7
8
flowchart TB
subgraph 初始化着色器Shader
direction LR
创建顶点着色器 --> 创建片元着色器 --> 关联着色器和着色器源码 --> 编译着色器 --> 创建program --> 关联着色器和program --> 使用program
end
开始 --> 获得canvas元素 --> 获取WebGL绘图上下文 --> 初始化顶点着色器源程序 --> 初始化片元着色器源程序
初始化片元着色器源程序 --> 创建顶点着色器
使用program --> 绘图渲染

DEMO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
const ctx = document.getElementById('canvas')
const gl = ctx.getContext('webgl')
// 创建着色器源码
// 顶点着色器源码
const VERTEX_SHADER_SOURCE = `
// 必须要存在 main 函数
void main() {
// 要绘制的点的坐标
gl_Position = vec4(0.0,0.0,0.0,1.0);
// 点的大小
gl_PointSize = 30.0;
}
`;
// gl_Position vec4(0.0,0.0,0.0,1.0) x, y, z, w齐次坐标 (x/w, y/w, z/w)
// gl_FragColor vec4(1.0,0.0,0.0,1.0) r, g, b, a
// 片元着色器源码
const FRAGMENT_SHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`;
//初始化着色器Shader
function initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) {
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
//指定顶点着色器的源码
gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE)
// 指定片元着色器的源码
gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE)
// 编译着色器
gl.compileShader(vertexShader)
gl.compileShader(fragmentShader)
// 创建一个程序对象
const program = gl.createProgram();
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
gl.linkProgram(program)
gl.useProgram(program)
return program;
}
const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)
// 执行绘制
// 要绘制的图形是什么, 从哪个开始, 使用几个顶点
gl.drawArrays(gl.POINTS, 0, 1);
gl.drawArrays(gl.LINES, 0, 1); // 最少需要有两个点,
gl.drawArrays(gl.TRIANGLES, 0, 1); // 3个点
// 3个顶点
// 0.0, 0.0, 0.0
// 0.2, 0.0, 0.0
// 0.4, 0.0, 0.0
gl.drawArrays(gl.POINTS, 0, 1);
gl.drawArrays(gl.LINES, 1, 2);

坐标系

!330
与 canvas 的坐标系不同
webgl三维坐标系类似下图
!344
!391
有个左手坐标系和右手坐标系的争论

通过attribute变量绘制一个点

声明 attribute 变量

存储限定符 类型 变量名 分号
attribute vec4 aPosition ;
attribute 变量只能在顶点着色器中使用,不能在片元着色器中使用

使用

vertexAttrib4f

流程

实战

通过鼠标点击绘制点

改变点的颜色

01 d3

D3 基础

1 相关文档

D3官网
D3中文文档
画廊
官方示例库
SVG属性表
d3.js资源汇总,包括示例、书籍、API文档等
本课程老师库
清华学生该课程作业
1&2.pdf

2 前言

D3:Data-Driven Documents
数据驱动文档

3 HTML

超文本标记语言超文本标记语言
HTML不是编程语言,HTML用来描述我们常见的网页
DOM -> Document Object Model

4 JavaScript

解释型的编程语言 不需要编译

4.1 把函数作为参数

  • 一个变量可以是一个函数
    • const myFunction = function(a, b) { return a + b; }
    • 类似于C/C++的函数指针
  • 回调(CallBack):
    • JavaScript脚本中(尤其D3.js编程中)常见把函数作为变量输入
    • 用于实现异步编程
    • setTimeout( function () { console.log(‘hello world!’) }, 1000);

4.2 d3开发中常用js函数

  • 数组排序 a.sort()
    1
    2
    3
    a.sort(function(a,b){ 
    return new Date(b.date) - new Date(a.date)
    })
  • 数组查询
    1
    a.find( d => d.name === ‘WenYang’)

5 SVG可缩放矢量模型

可缩放矢量图形(英語:Scalable Vector Graphics,SVG)
其他图片通过像素点秒回图像,svg通过描述图中的所有形状,实现缩放不失真的功能

1
2
3
4
5
6
7
8
9
10
11
//01svg.html
<svg style='display: block; margin: 0 auto;'>
<g transform='translate(0,60)'>
<rect width=100 height=100 fill='#EEEEEE' />
<circle r=15 fill='#72bf67' cx=25 cy=30 />
<circle r=15 fill='rgb(100, 149, 237)' cx=75 cy=30 />
<g transform='translate(15,60) rotate(10)'>
<path d="M0,0 A40,40 10 0,0 65,0" fill='none' stroke='gray' stroke-width=5 />
</g>
</g>
</svg>

6 D3操作svg

  • 单选d3.select(…)
    • 只找一个,若有重名也只返回第一个
    • 多为ID
  • 多选d3.selectAll(…)
    • 有多少返回多少
  • 赋值element.attr(‘y’, ‘100’)
    • 多用链式调用selection.attr(…).attr(…).attr(…)
  • 添加element.append(…)
    • 链式:d3.select(‘#mainsvg’).append(‘g’).attr(‘id’, ‘maingroup’)
  • 删除element.remove()
1
2
3
4
5
6
//D3操作svg
const svg = d3.select('svg');
const circle = d3.selectAll('circle')
circle.attr('r','20').attr('fill','#aaaaff')
d3.select('#gid').append('g').attr('id','gid-1')
d3.select('#gid-1').remove()

7 D3常用操作

  • 最大值d3.max(array)
  • 最小值d3.min(array)
  • 最大最小值d3.extent(array)
    • 同时返回最小值与最大值,以数组的形式,即[最小值,最大值]
    • 数组中的内容可以是任意对象d3.extent(a, d => d.height)
  • 文件读取 d3.csv
    • d3.csv(‘path/to/data.csv’).then( data => { console.log(data)} )
    • d3.csv被调用后,其返回值是一个JavaScript的‘Promise’对象(object)
    • Promise‘询问’:数据读取好了之后要做什么?‘做什么’即对应.then()中函数的内 容。

02 d3基础

02 基础

2-数据可视化编程2023-基础-第二节.mp4

D3比例尺

scaleLinear

  • d3.scaleLinear()
    • 定义一个线性比例尺,返回的是一个函数。
    • e.g.let scale = d3.scaleLinear();
  • scale.domain([min_d, max_d]).range([min, max]):
    • 设置比例尺的定义域值域
    • 定义域 : 数据的范围
    • 值域:坐标轴的px范围
    • 线性比例尺的定义域和值域都是连续的(Continuous),需分别给出最大值与最小值。
    • e.g.const scale = d3.scaleLinear().domain([20, 80]).range([0, 120]);
  • 常在初始化中结合读取的数据与d3.max等接口连用
1
const xScale = d3.scaleLinear().domain([0, d3.max(data, d => d.value)]).range([0, innerWidth]);

scaleBand

  • d3.scaleBand():
    • 定义一个‘条带’比例尺,返回的是一个函数
    • e.g. let scale = d3.scaleBand();
  • scale.domain(array).range([min, max]):
    • 设置比例尺的定义域与值域
    • Band比例尺的定义域是离散的(Discrete),值域是连续的(柱状图)
    • e.g., const scale = d3.scaleBand().domain([‘a’, ‘b’, ‘c’]).range([0, 120]);
  • 常结合JavaScript的array.map接口一起使用:
1
2
3
let a = [{name: 'Shao-Kui', value:6}, {name:'Wen-Yang', value:6}, {name:’Yuan Liang', value:16}] 
a.map(d => d.name) // [‘Shao-Kui’, ‘Wen-Yang’, ‘Yuan Liang’]
const yScale = d3.scaleBand() .domain(data.map(d => d.name)) .range([0, innerHeight])
  • scale.padding(0.1):设置条带的间距占各自区域的比重
  • scale.bandwidth():返回条带的长度。

坐标轴

  • 定义坐标轴(获得结果仍是函数):
    • const yAxis = d3.axisLeft(yScale);
    • const xAxis = d3.axisBottom(xScale);
    • axisLeft:左侧坐标轴。
    • axisBottom:底侧坐标轴。
    • 坐标轴的刻度对应比例尺的定义域。
    • 坐标轴在画布的绘制对应比例尺的值域。
    • 仅是对坐标轴的定义,还未绘制。
  • 绘制坐标轴
    • const yAxisGroup = g.append(‘g’).call(yAxis);
    • const xAxisGroup = g.append(‘g’).call(xAxis);
    • 实际配置后会发现<g>中增添了与坐标轴相关的元素
  • 任何坐标轴在初始化之后会默认放置在坐标原点,需要进一步的平移

关于 SELECTION.CALL(…)

call的使用

  • 函数的输入为另一个函数。
  • 另一个函数以selection本身(即图元)作为输入。
  • 另一个函数中会根据函数体的内容修改selection对应的图元。
  • 定义一个空白的<g>,D3会帮助我们定义好另一个函数,我们通过 .call(…)让<g>得以在另一个函数中修改。
  • const yAxis = d3.axisLeft(yScale);
  • const yAxisGroup = g.append(‘g’).call(yAxis);