效果
实现过程
1. 获取Canvas元素和设置初始参数
// 获取Canvas元素
const canvas = document.querySelector('#scene');
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
const ctx = canvas.getContext('2d');
// 针对高DPI屏幕进行缩放
if (window.devicePixelRatio > 1) {
canvas.width = canvas.clientWidth * 2;
canvas.height = canvas.clientHeight * 2;
ctx.scale(2, 2);
}
// Canvas的宽度和高度
let width = canvas.clientWidth;
let height = canvas.clientHeight;
// 球体旋转的角度
let rotation = 0;
// 存储所有点的数组
let dots = [];
这部分代码主要是获取Canvas元素,并根据设备的DPI进行缩放。然后,定义了一些全局变量,包括Canvas的宽度、高度、球体旋转的角度和存储所有点的数组。
2. 定义一些常量
// 一些常量
const DOTS_AMOUNT = 1000; // 点的数量
const DOT_RADIUS = 2; // 点的半径
let GLOBE_RADIUS = width * 0.7; // 球半径
let GLOBE_CENTER_Z = -GLOBE_RADIUS; // 球心的Z坐标
let PROJECTION_CENTER_X = width / 2; // 画布HTML的X中心
let PROJECTION_CENTER_Y = height / 2; // 画布HTML的Y中心
let FIELD_OF_VIEW = width * 0.8;
这部分代码定义了一些常量,如点的数量、点的半径、球半径等。
3.定义点(Dot)类
// Dot类表示每个点的属性
class Dot {
constructor(x, y, z, color) {
this.x = x;
this.y = y;
this.z = z;
this.color = color; // 添加颜色属性
this.xProject = 0;
this.yProject = 0;
this.sizeProjection = 0;
}
// 将三维坐标映射到二维画布上
project(sin, cos) {
const rotX = cos * this.x + sin * (this.z - GLOBE_CENTER_Z);
const rotZ = -sin * this.x + cos * (this.z - GLOBE_CENTER_Z) + GLOBE_CENTER_Z;
this.sizeProjection = FIELD_OF_VIEW / (FIELD_OF_VIEW - rotZ);
this.xProject = (rotX * this.sizeProjection) + PROJECTION_CENTER_X;
this.yProject = (this.y * this.sizeProjection) + PROJECTION_CENTER_Y;
}
// 在画布上绘制点
draw(sin, cos) {
this.project(sin, cos);
ctx.beginPath();
ctx.arc(this.xProject, this.yProject, DOT_RADIUS * this.sizeProjection, 0, Math.PI * 2);
ctx.fillStyle = this.color; // 使用粒子的颜色属性
ctx.closePath();
ctx.fill();
}
}
这部分代码定义了一个Dot类,表示球体上的每个点的属性和方法。包括构造函数、将三维坐标映射到二维画布上的project方法,以及在画布上绘制点的draw方法
4.创建和渲染球体的函数
// 创建所有点的函数
function createDots() {
// 清空粒子数组
dots.length = 0;
// 创建一半蓝色粒子
for (let i = 0; i < DOTS_AMOUNT / 2; i++) {
const theta = Math.random() * 2 * Math.PI;
const phi = Math.acos((Math.random() * 2) - 1);
const x = GLOBE_RADIUS * Math.sin(phi) * Math.cos(theta);
const y = GLOBE_RADIUS * Math.sin(phi) * Math.sin(theta);
const z = (GLOBE_RADIUS * Math.cos(phi)) + GLOBE_CENTER_Z;
dots.push(new Dot(x, y, z, '#981898'));
}
// 创建一半红色粒子
for (let i = 0; i < DOTS_AMOUNT / 2; i++) {
const theta = Math.random() * 2 * Math.PI;
const phi = Math.acos((Math.random() * 2) - 1);
const x = GLOBE_RADIUS * Math.sin(phi) * Math.cos(theta);
const y = GLOBE_RADIUS * Math.sin(phi) * Math.sin(theta);
const z = (GLOBE_RADIUS * Math.cos(phi)) + GLOBE_CENTER_Z;
dots.push(new Dot(x, y, z, '#E71751'));
}
}
这部分代码定义了createDots函数,用于生成一定数量的点,并分别将一半标记为蓝色,一半标记为红色。这里可以根据需求改动,
// 渲染函数,不断更新球体的旋转
function render(a) {
ctx.clearRect(0, 0, width, height);
rotation = a * 0.0004;
const sineRotation = Math.sin(rotation);
const cosineRotation = Math.cos(rotation);
// 遍历所有点并绘制
for (var i = 0; i < dots.length; i++) {
dots[i].draw(sineRotation, cosineRotation);
}
window.requestAnimationFrame(render);
}
这部分代码定义了render函数,用于在每一帧更新球体的旋转角度,并循环绘制所有的点,产生连续的旋转效果。
5.监听窗口大小变化事件
// 当用户调整窗口大小时重新计算参数
function afterResize() {
width = canvas.offsetWidth;
height = canvas.offsetHeight;
if (window.devicePixelRatio > 1) {
canvas.width = canvas.clientWidth * 2;
canvas.height = canvas.clientHeight * 2;
ctx.scale(2, 2);
} else {
canvas.width = width;
canvas.height = height;
}
GLOBE_RADIUS = width * 0.7;
GLOBE_CENTER_Z = -GLOBE_RADIUS;
PROJECTION_CENTER_X = width / 2;
PROJECTION_CENTER_Y = height / 2;
FIELD_OF_VIEW = width * 0.8;
createDots(); // 重新生成所有点
}
// 变量用于存储用户调整窗口大小时的超时
let resizeTimeout;
// 当用户调整窗口大小时触发的函数
function onResize() {
resizeTimeout = window.clearTimeout(resizeTimeout);
resizeTimeout = window.setTimeout(afterResize, 500);
}
// 监听窗口大小变化事件
window.addEventListener('resize', onResize);
这部分代码用于监听窗口大小变化事件,并在用户调整窗口大小时重新计算参数,以保持效果的稳定性。
最后初始化一下就好了文章来源:https://www.toymoban.com/news/detail-801094.html
// 初始化点数组
createDots();
// 渲染场景
window.requestAnimationFrame(render);
完整代码文章来源地址https://www.toymoban.com/news/detail-801094.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D旋转球体</title>
<style>
body {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: #000;
margin: 0;
}
canvas {
width: 500px;
height: 500px;
}
</style>
</head>
<body>
<canvas id="scene"></canvas>
<script>
// 获取Canvas元素
const canvas = document.querySelector('#scene');
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
const ctx = canvas.getContext('2d');
// 针对高DPI屏幕进行缩放
if (window.devicePixelRatio > 1) {
canvas.width = canvas.clientWidth * 2;
canvas.height = canvas.clientHeight * 2;
ctx.scale(2, 2);
}
// Canvas的宽度和高度
let width = canvas.clientWidth;
let height = canvas.clientHeight;
// 球体旋转的角度
let rotation = 0;
// 存储所有点的数组
let dots = [];
// 一些常量
const DOTS_AMOUNT = 1000; // 点的数量
const DOT_RADIUS = 2; // 点的半径
let GLOBE_RADIUS = width * 0.7; // 球半径
let GLOBE_CENTER_Z = -GLOBE_RADIUS; // 球心的Z坐标
let PROJECTION_CENTER_X = width / 2; // 画布HTML的X中心
let PROJECTION_CENTER_Y = height / 2; // 画布HTML的Y中心
let FIELD_OF_VIEW = width * 0.8;
// Dot类表示每个点的属性
class Dot {
constructor(x, y, z, color) {
this.x = x;
this.y = y;
this.z = z;
this.color = color; // 添加颜色属性
this.xProject = 0;
this.yProject = 0;
this.sizeProjection = 0;
}
// 将三维坐标映射到二维画布上
project(sin, cos) {
const rotX = cos * this.x + sin * (this.z - GLOBE_CENTER_Z);
const rotZ = -sin * this.x + cos * (this.z - GLOBE_CENTER_Z) + GLOBE_CENTER_Z;
this.sizeProjection = FIELD_OF_VIEW / (FIELD_OF_VIEW - rotZ);
this.xProject = (rotX * this.sizeProjection) + PROJECTION_CENTER_X;
this.yProject = (this.y * this.sizeProjection) + PROJECTION_CENTER_Y;
}
// 在画布上绘制点
draw(sin, cos) {
this.project(sin, cos);
ctx.beginPath();
ctx.arc(this.xProject, this.yProject, DOT_RADIUS * this.sizeProjection, 0, Math.PI * 2);
ctx.fillStyle = this.color; // 使用粒子的颜色属性
ctx.closePath();
ctx.fill();
}
}
// 创建所有点的函数
function createDots() {
// 清空粒子数组
dots.length = 0;
// 创建一半蓝色粒子
for (let i = 0; i < DOTS_AMOUNT / 2; i++) {
const theta = Math.random() * 2 * Math.PI;
const phi = Math.acos((Math.random() * 2) - 1);
const x = GLOBE_RADIUS * Math.sin(phi) * Math.cos(theta);
const y = GLOBE_RADIUS * Math.sin(phi) * Math.sin(theta);
const z = (GLOBE_RADIUS * Math.cos(phi)) + GLOBE_CENTER_Z;
dots.push(new Dot(x, y, z, '#981898'));
}
// 创建一半红色粒子
for (let i = 0; i < DOTS_AMOUNT / 2; i++) {
const theta = Math.random() * 2 * Math.PI;
const phi = Math.acos((Math.random() * 2) - 1);
const x = GLOBE_RADIUS * Math.sin(phi) * Math.cos(theta);
const y = GLOBE_RADIUS * Math.sin(phi) * Math.sin(theta);
const z = (GLOBE_RADIUS * Math.cos(phi)) + GLOBE_CENTER_Z;
dots.push(new Dot(x, y, z, '#E71751'));
}
}
// 渲染函数,不断更新球体的旋转
function render(a) {
ctx.clearRect(0, 0, width, height);
rotation = a * 0.0004;
const sineRotation = Math.sin(rotation);
const cosineRotation = Math.cos(rotation);
// 遍历所有点并绘制
for (var i = 0; i < dots.length; i++) {
dots[i].draw(sineRotation, cosineRotation);
}
window.requestAnimationFrame(render);
}
// 当用户调整窗口大小时重新计算参数
function afterResize() {
width = canvas.offsetWidth;
height = canvas.offsetHeight;
if (window.devicePixelRatio > 1) {
canvas.width = canvas.clientWidth * 2;
canvas.height = canvas.clientHeight * 2;
ctx.scale(2, 2);
} else {
canvas.width = width;
canvas.height = height;
}
GLOBE_RADIUS = width * 0.7;
GLOBE_CENTER_Z = -GLOBE_RADIUS;
PROJECTION_CENTER_X = width / 2;
PROJECTION_CENTER_Y = height / 2;
FIELD_OF_VIEW = width * 0.8;
createDots(); // 重新生成所有点
}
// 变量用于存储用户调整窗口大小时的超时
let resizeTimeout;
// 当用户调整窗口大小时触发的函数
function onResize() {
resizeTimeout = window.clearTimeout(resizeTimeout);
resizeTimeout = window.setTimeout(afterResize, 500);
}
// 监听窗口大小变化事件
window.addEventListener('resize', onResize);
// 初始化点数组
createDots();
// 渲染场景
window.requestAnimationFrame(render);
</script>
</body>
</html>
到了这里,关于JavaScript+canvas实现一个旋转的3D球动画效果的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!