掌握 JavaScript 旋转技术

发布于:2025-03-01 ⋅ 阅读:(119) ⋅ 点赞:(0)

1. 基本概念

1.1 角度与弧度

在讨论旋转之前,首先需要了解角度和弧度的概念:

  • 角度:通常用度数表示,一个圆周为360度。
  • 弧度:数学上更常用的角度单位,一个圆周为2π弧度。

在JavaScript中,大多数三角函数(如Math.sin()Math.cos())使用的是弧度而不是度数。因此,在进行旋转计算时,经常需要在两者之间进行转换。

转换公式:
function degreesToRadians(degrees) {
    return degrees * (Math.PI / 180);
}

function radiansToDegrees(radians) {
    return radians * (180 / Math.PI);
}

1.2 旋转矩阵

在二维平面上,旋转可以通过旋转矩阵来描述。给定一个点 (x, y) 和旋转角度 θ(以弧度表示),新的坐标 (x', y') 可以通过以下公式计算:

x' = x * cos(θ) - y * sin(θ)
y' = x * sin(θ) + y * cos(θ)

在三维空间中,旋转变得更加复杂,通常使用四元数或旋转矩阵来表示旋转。


2. 使用 CSS 实现旋转

2.1 CSS Transform 属性

CSS 提供了 transform 属性来实现元素的旋转。最常用的旋转方式是使用 rotate() 函数。

示例:
.rotate {
    transform: rotate(45deg); /* 顺时针旋转45度 */
}
结合JavaScript动态设置:
const element = document.querySelector('.rotate');
element.style.transform = 'rotate(90deg)';

2.2 三维旋转

除了二维旋转,CSS还支持三维旋转,可以使用 rotateX(), rotateY(), rotateZ()rotate3d() 函数。

示例:
.rotate3d {
    transform: rotateX(45deg) rotateY(45deg) rotateZ(45deg);
}

3. 使用 Canvas 实现旋转

HTML5 的 <canvas> 元素提供了一个强大的绘图API,可以用来绘制和操作图像、形状等。在Canvas中,旋转主要通过 context.rotate() 方法实现。

3.1 基本旋转

const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

// 保存当前状态
ctx.save();

// 平移到画布中心
ctx.translate(canvas.width / 2, canvas.height / 2);

// 旋转45度(弧度)
ctx.rotate(degreesToRadians(45));

// 绘制一个矩形
ctx.fillStyle = 'blue';
ctx.fillRect(-50, -50, 100, 100);

// 恢复到之前的变换状态
ctx.restore();

3.2 复杂变换

结合平移、缩放和旋转,可以实现复杂的变换效果。

示例:
ctx.save();
ctx.translate(100, 100); // 平移到指定位置
ctx.scale(2, 2);         // 缩放
ctx.rotate(degreesToRadians(45)); // 旋转
ctx.fillStyle = 'red';
ctx.fillRect(-50, -50, 100, 100); // 绘制一个矩形
ctx.restore();

4. 使用 SVG 实现旋转

SVG(可缩放矢量图形)是一种基于XML的图形格式,广泛用于Web上的矢量图形绘制。SVG支持通过 transform 属性来实现旋转。

4.1 基本旋转

<svg width="200" height="200">
    <rect x="50" y="50" width="100" height="100" fill="blue" transform="rotate(45, 100, 100)" />
</svg>

rotate(45, 100, 100) 表示将矩形绕其中心点 (100, 100) 旋转45度。

4.2 动态旋转

通过JavaScript动态修改SVG元素的 transform 属性,可以实现动画效果。

示例:
<svg id="mySvg" width="200" height="200">
    <rect id="myRect" x="50" y="50" width="100" height="100" fill="blue" />
</svg>

<script>
    const rect = document.getElementById('myRect');
    let angle = 0;

    function rotateRect() {
        angle += 1;
        if (angle >= 360) angle -= 360;
        rect.setAttribute('transform', `rotate(${angle}, 100, 100)`);
        requestAnimationFrame(rotateRect);
    }

    rotateRect();
</script>

5. 高级技巧

5.1 矩阵变换

在Canvas中,可以使用变换矩阵来实现更复杂的旋转和平移操作。context.setTransform()context.transform() 方法允许你直接操作变换矩阵。

示例:
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

// 设置变换矩阵
ctx.setTransform(
    Math.cos(degreesToRadians(45)), Math.sin(degreesToRadians(45)),
    -Math.sin(degreesToRadians(45)), Math.cos(degreesToRadians(45)),
    canvas.width / 2, canvas.height / 2
);

// 绘制一个矩形
ctx.fillStyle = 'green';
ctx.fillRect(-50, -50, 100, 100);

5.2 三维旋转

虽然CSS和Canvas支持三维旋转,但在WebGL中可以实现更复杂的三维图形和动画。WebGL是一个低级别的图形API,适用于高性能的3D渲染。

示例:
const vertexShaderSource = `
    attribute vec4 a_position;
    uniform mat4 u_matrix;
    void main() {
        gl_Position = u_matrix * a_position;
    }
`;

const fragmentShaderSource = `
    precision mediump float;
    void main() {
        gl_FragColor = vec4(1, 0, 0, 1); // 红色
    }
`;

// 初始化WebGL上下文
const canvas = document.getElementById('myCanvas');
const gl = canvas.getContext('webgl');

// 创建着色器程序
function createShader(gl, type, source) {
    const shader = gl.createShader(type);
    gl.shaderSource(shader, source);
    gl.compileShader(shader);
    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        console.error(gl.getShaderInfoLog(shader));
        gl.deleteShader(shader);
        return null;
    }
    return shader;
}

const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);

const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    console.error(gl.getProgramInfoLog(program));
}

gl.useProgram(program);

// 设置顶点数据
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
const positions = [
    0, 0,
    0, 0.5,
    0.7, 0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
gl.enableVertexAttribArray(positionAttributeLocation);
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);

// 创建旋转矩阵
function createRotationMatrix(angleInRadians) {
    return [
        Math.cos(angleInRadians), Math.sin(angleInRadians),
        -Math.sin(angleInRadians), Math.cos(angleInRadians),
    ];
}

const matrixLocation = gl.getUniformLocation(program, "u_matrix");

function render(time) {
    time *= 0.001; // 将时间转换为秒

    const angleInRadians = time * Math.PI * 2 / 3; // 三秒一圈
    const rotationMatrix = createRotationMatrix(angleInRadians);

    gl.uniformMatrix4fv(matrixLocation, false, new Float32Array([
        ...rotationMatrix, 0, 0,
        0, 0, ...rotationMatrix, 0,
        0, 0, 0, 0, 1
    ]));

    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.TRIANGLES, 0, 3);

    requestAnimationFrame(render);
}

requestAnimationFrame(render);


网站公告

今日签到

点亮在社区的每一天
去签到