hand
_1_12_140
4
返回栏目
0k
3k
5k
1k
2k
0.2k
2k
1k
2k
3k
2k
3k
2k
3k
3k
0.3k
0k
2k
0k
1k
0.1k
0k
0k
2k
2k
3k
0.2k
3k
0k
2k
2k
2k
3k
2k
2k
0k
4k
2k
2k
0k
3k
3k
2k
2k
2k
1k
3k
1k
3k
2k
1k
0.8k
2k
0k
2k
2k
2k
2k
3k
0.4k
4k
2k
5k
2k
3k
2k
3k
3k
4k
2k
3k
2k
3k
0.7k
2k
0.8k
3k
2k
4k
2k
2k
2k
2k
2k
3k
3k
3k
3k
4k
3k
3k
0k
2k
2k
0k
3k
2k
3k
1k
2k
2k
3k
3k
3k
3k
5k
3k
3k
3k
4k
3k
5k
4k
4k
4k
4k
1k
2k
2k
2k
2k
2k
2k
1k
2k
3k
3k
3k
3k
3k
2k
3k
4k
2k
2k
3k
5k
3k
3k
3k
4k
3k
3k
2k
3k
5k
4k
3k
4k
4k
2k
3k
3k
1k
3k
4k
4k
2k
2k
2k
3k
2k
4k
2k
4k
2k
4k
1k
2k
1k
2k
2k
1k
2k
2k
2k
2k
2k
2k
1k
1k
4k
3k
2k
2k
3k
3k
6k
2k
8k
3k
7k
2k
3k
3k
4k
3k
5k
4k
3k
3k
2k
2k
3k
3k
2k
2k
2k
3k
2k
6k
4k
4k
4k
4k
3k
3k
2k
4k
2k
3k
3k
None
None
None
None
None
None
None
None
None
None
None
None
None
None
None
None
None
0k
返回前端 - HTML5栏目
作者:
贺及楼
成为作者
更新日期:2025-02-27 12:27:43
在现代前端开发中,HTML5 的画布元素(<canvas>
)为我们提供了强大的绘图能力,不仅可以进行 2D 绘图,借助 WebGL 还能实现 3D 绘图。而 3D 场景的交互则能让用户与 3D 世界进行更自然、更有趣的互动,提升用户体验。本文将深入探讨如何在 HTML5 画布上实现 3D 绘图,并添加交互功能。
在 HTML5 中,我们主要使用 WebGL 来进行 3D 绘图。WebGL 是一种基于 OpenGL ES 2.0 的 JavaScript API,它允许我们在网页上创建和操作 3D 图形。下面是一个简单的创建 3D 立方体的示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D Cube</title>
<style>
canvas {
display: block;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas id="glCanvas"></canvas>
<script>
// 获取 canvas 元素
const canvas = document.getElementById('glCanvas');
// 获取 WebGL 上下文
const gl = canvas.getContext('webgl');
if (!gl) {
alert('Unable to initialize WebGL. Your browser or machine may not support it.');
return;
}
// 顶点着色器代码
const vertexShaderSource = `
attribute vec4 a_position;
void main() {
gl_Position = a_position;
}
`;
// 片段着色器代码
const fragmentShaderSource = `
precision mediump float;
void main() {
gl_FragColor = vec4(1, 0, 0, 1);
}
`;
// 创建着色器函数
function createShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) {
return shader;
}
console.log(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
}
// 创建着色器程序函数
function createProgram(gl, vertexShader, fragmentShader) {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
const success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (success) {
return program;
}
console.log(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
// 创建顶点和片段着色器
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
// 创建着色器程序
const program = createProgram(gl, vertexShader, fragmentShader);
// 获取属性位置
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
// 创建缓冲区
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// 立方体顶点数据
const positions = [
-0.5, -0.5, 0.5,
0.5, -0.5, 0.5,
0.5, 0.5, 0.5,
-0.5, 0.5, 0.5,
-0.5, -0.5, -0.5,
0.5, -0.5, -0.5,
0.5, 0.5, -0.5,
-0.5, 0.5, -0.5
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
// 调整画布大小
function resizeCanvasToDisplaySize(canvas) {
const displayWidth = canvas.clientWidth;
const displayHeight = canvas.clientHeight;
if (canvas.width!== displayWidth || canvas.height!== displayHeight) {
canvas.width = displayWidth;
canvas.height = displayHeight;
}
}
resizeCanvasToDisplaySize(canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// 启用属性
gl.enableVertexAttribArray(positionAttributeLocation);
const size = 3;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.vertexAttribPointer(positionAttributeLocation, size, type, normalize, stride, offset);
// 使用着色器程序
gl.useProgram(program);
// 绘制立方体
const primitiveType = gl.TRIANGLES;
const offsetDraw = 0;
const count = 36;
gl.drawArrays(primitiveType, offsetDraw, count);
</script>
</body>
</html>
canvas.getContext('webgl')
获取 WebGL 上下文,用于后续的 3D 绘图操作。gl.createBuffer()
创建缓冲区,并使用 gl.bufferData()
将顶点数据存储到缓冲区中。gl.drawArrays()
方法绘制图形。实现 3D 场景的交互通常涉及到鼠标和键盘事件的处理。下面是一个添加鼠标交互功能的示例代码,允许用户通过鼠标拖动来旋转立方体:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D Cube Interaction</title>
<style>
canvas {
display: block;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas id="glCanvas"></canvas>
<script>
const canvas = document.getElementById('glCanvas');
const gl = canvas.getContext('webgl');
if (!gl) {
alert('Unable to initialize WebGL. Your browser or machine may not support it.');
return;
}
// 顶点着色器代码
const vertexShaderSource = `
attribute vec4 a_position;
uniform mat4 u_modelViewMatrix;
uniform mat4 u_projectionMatrix;
void main() {
gl_Position = u_projectionMatrix * u_modelViewMatrix * a_position;
}
`;
// 片段着色器代码
const fragmentShaderSource = `
precision mediump float;
void main() {
gl_FragColor = vec4(1, 0, 0, 1);
}
`;
// 创建着色器和着色器程序
function createShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) {
return shader;
}
console.log(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
}
function createProgram(gl, vertexShader, fragmentShader) {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
const success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (success) {
return program;
}
console.log(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
const program = createProgram(gl, vertexShader, fragmentShader);
// 获取属性和统一变量位置
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
const modelViewMatrixUniformLocation = gl.getUniformLocation(program, 'u_modelViewMatrix');
const projectionMatrixUniformLocation = gl.getUniformLocation(program, 'u_projectionMatrix');
// 创建缓冲区
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// 立方体顶点数据
const positions = [
-0.5, -0.5, 0.5,
0.5, -0.5, 0.5,
0.5, 0.5, 0.5,
-0.5, 0.5, 0.5,
-0.5, -0.5, -0.5,
0.5, -0.5, -0.5,
0.5, 0.5, -0.5,
-0.5, 0.5, -0.5
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
// 调整画布大小
function resizeCanvasToDisplaySize(canvas) {
const displayWidth = canvas.clientWidth;
const displayHeight = canvas.clientHeight;
if (canvas.width!== displayWidth || canvas.height!== displayHeight) {
canvas.width = displayWidth;
canvas.height = displayHeight;
}
}
resizeCanvasToDisplaySize(canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// 启用属性
gl.enableVertexAttribArray(positionAttributeLocation);
const size = 3;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.vertexAttribPointer(positionAttributeLocation, size, type, normalize, stride, offset);
// 创建投影矩阵
const fieldOfView = 45 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.1;
const zFar = 100.0;
const projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar);
// 创建模型视图矩阵
const modelViewMatrix = mat4.create();
mat4.translate(modelViewMatrix, modelViewMatrix, [0.0, 0.0, -6.0]);
// 鼠标交互变量
let isDragging = false;
let lastX = 0;
let lastY = 0;
let rotationX = 0;
let rotationY = 0;
// 鼠标事件处理函数
canvas.addEventListener('mousedown', (event) => {
isDragging = true;
lastX = event.clientX;
lastY = event.clientY;
});
canvas.addEventListener('mousemove', (event) => {
if (isDragging) {
const dx = (event.clientX - lastX) * 0.01;
const dy = (event.clientY - lastY) * 0.01;
rotationX += dy;
rotationY += dx;
lastX = event.clientX;
lastY = event.clientY;
}
});
canvas.addEventListener('mouseup', () => {
isDragging = false;
});
// 渲染函数
function render() {
// 清除画布
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.enable(gl.DEPTH_TEST);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// 更新模型视图矩阵
mat4.identity(modelViewMatrix);
mat4.translate(modelViewMatrix, modelViewMatrix, [0.0, 0.0, -6.0]);
mat4.rotateX(modelViewMatrix, modelViewMatrix, rotationX);
mat4.rotateY(modelViewMatrix, modelViewMatrix, rotationY);
// 设置统一变量
gl.useProgram(program);
gl.uniformMatrix4fv(projectionMatrixUniformLocation, false, projectionMatrix);
gl.uniformMatrix4fv(modelViewMatrixUniformLocation, false, modelViewMatrix);
// 绘制立方体
const primitiveType = gl.TRIANGLES;
const offsetDraw = 0;
const count = 36;
gl.drawArrays(primitiveType, offsetDraw, count);
// 请求下一帧动画
requestAnimationFrame(render);
}
// 开始渲染
render();
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/3.3.0/gl-matrix-min.js"></script>
</body>
</html>
u_modelViewMatrix
和 u_projectionMatrix
统一变量,用于处理模型视图变换和投影变换。mousedown
、mousemove
和 mouseup
事件,实现鼠标拖动旋转立方体的功能。通过本文的介绍,我们了解了如何在 HTML5 画布上使用 WebGL 进行 3D 绘图,并添加交互功能。以下是一个简单的总结表格:
功能 | 描述 |
---|---|
3D 绘图 | 使用 WebGL 创建 3D 图形,包括顶点着色器、片段着色器、缓冲区等 |
3D 场景交互 | 通过监听鼠标和键盘事件,实现 3D 场景的交互,如旋转、缩放等 |
希望本文能帮助你更好地理解和实现 HTML5 画布上的 3D 绘图和交互功能。
前端 - HTML5
整章节共240节
快分享给你的小伙伴吧 ~