要实现的效果:
- 圆环范围内随机点
- 点有镜像渐变效果
- 点中有文字
- 点可以随机大小
- 点可以浮动
- 点与点之间不重合
- 点的数量随机
- 文本跟随点的大小变化
- 缓存第一次位置
一、静态效果
完整代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
</head>
<body>
<canvas height="500" id="myCanvas" width="500">
</canvas>
<script type="text/javascript">
// 获取 canvas 元素和绘图上下文
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 圆心坐标和半径
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const outerRadius = 200;
const innerRadius = 100;
// 绘制圆环
ctx.beginPath();
ctx.arc(centerX, centerY, outerRadius, 0, 2 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(centerX, centerY, innerRadius, 0, 2 * Math.PI);
ctx.stroke();
// 在圆环范围内随机分布点
function getRandomPointInRing(innerRadius, outerRadius) {
const angle = Math.random() * 2 * Math.PI;
const radius = Math.sqrt(Math.random() * (outerRadius * outerRadius - innerRadius * innerRadius) + innerRadius * innerRadius);
const x = centerX + radius * Math.cos(angle);
const y = centerY + radius * Math.sin(angle);
return { x, y };
}
// 检查两个点之间的距离
function distance(point1, point2) {
return Math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2);
}
// 创建镜像渐变
function createMirrorGradient(ctx, x, y, radius) {
const gradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
gradient.addColorStop(0, 'rgba(255, 0, 0, 1)');
gradient.addColorStop(0.5, 'rgba(255, 0, 0, 0)');
gradient.addColorStop(1, 'rgba(255, 0, 0, 1)');
return gradient;
}
// 绘制随机点,确保点与点之间没有交集,并在点的中心添加文本和边框
function drawRandomPoints(numPoints) {
const points = [];
while (points.length < numPoints) {
const point = getRandomPointInRing(innerRadius, outerRadius);
point.radius = Math.random() * 30; // 随机生成点的半径,范围在5到15之间
let isValid = true;
for (let i = 0; i < points.length; i++) {
if (distance(point, points[i]) < point.radius + points[i].radius) {
isValid = false;
break;
}
}
if (isValid) {
points.push(point);
const gradient = createMirrorGradient(ctx, point.x, point.y, point.radius);
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(point.x, point.y, point.radius, 0, 2 * Math.PI);
ctx.fill();
// 绘制点的边框
ctx.strokeStyle = '#f00';
ctx.lineWidth = 2;
ctx.stroke();
// 在点的中心添加文本
ctx.fillStyle = 'black';
ctx.font = '12px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(Math.round(point.radius), point.x, point.y);
}
}
}
// 在圆环内绘制 20 个随机点
drawRandomPoints(30);
</script>
</body>
</html>
二、动态效果
完整代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
</head>
<body>
<canvas height="500" id="myCanvas" width="500">
</canvas>
<script type="text/javascript">
// 获取 canvas 元素和绘图上下文
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 圆心坐标和半径
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const outerRadius = 200;
const innerRadius = 100;
// 在圆环范围内随机分布点
function getRandomPointInRing(innerRadius, outerRadius) {
const angle = Math.random() * 2 * Math.PI;
const radius = Math.sqrt(Math.random() * (outerRadius * outerRadius - innerRadius * innerRadius) + innerRadius * innerRadius);
const x = centerX + radius * Math.cos(angle);
const y = centerY + radius * Math.sin(angle);
return { x, y };
}
// 检查两个点之间的距离
function distance(point1, point2) {
return Math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2);
}
// 创建镜像渐变
function createMirrorGradient(ctx, x, y, radius) {
const gradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
gradient.addColorStop(0, 'rgba(255, 0, 0, 1)');
gradient.addColorStop(0.5, 'rgba(255, 255, 255, 0)');
gradient.addColorStop(1, 'rgba(255, 0, 0, 1)');
return gradient;
}
// 绘制点
function drawPoint(point) {
const gradient = createMirrorGradient(ctx, point.x, point.y, point.radius);
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(point.x, point.y, point.radius, 0, 2 * Math.PI);
ctx.fill();
// 绘制点的边框
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
ctx.stroke();
// 在点的中心添加文本
ctx.fillStyle = 'black';
ctx.font = '12px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(Math.round(point.radius), point.x, point.y);
}
// 绘制随机点,确保点与点之间没有交集
function drawRandomPoints(numPoints) {
const points = [];
while (points.length < numPoints) {
const point = getRandomPointInRing(innerRadius, outerRadius);
point.radius = Math.random() * 30; // 随机生成点的半径,范围在5到15之间
let isValid = true;
for (let i = 0; i < points.length; i++) {
if (distance(point, points[i]) < point.radius + points[i].radius) {
isValid = false;
break;
}
}
if (isValid) {
point.originalY = point.y;
point.offset = Math.random() * 10; // 随机生成浮动幅度
points.push(point);
}
}
return points;
}
// 动画更新点的位置
function updatePoints(points) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制圆环
ctx.beginPath();
ctx.arc(centerX, centerY, outerRadius, 0, 2 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(centerX, centerY, innerRadius, 0, 2 * Math.PI);
ctx.stroke();
points.forEach(point => {
point.y = point.originalY + Math.sin(Date.now() / 500) * point.offset;
drawPoint(point);
});
requestAnimationFrame(() => updatePoints(points));
}
// 初始化并开始动画
const points = drawRandomPoints(30);
updatePoints(points);
</script>
</body>
</html>
三、文本跟随点的大小变化
完整代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
</head>
<body>
<canvas height="500" id="myCanvas" width="500">
<script type="text/javascript">
// 获取 canvas 元素和绘图上下文
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 圆心坐标和半径
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const outerRadius = 200;
const innerRadius = 100;
// 在圆环范围内随机分布点
function getRandomPointInRing(innerRadius, outerRadius) {
const angle = Math.random() * 2 * Math.PI;
const radius = Math.sqrt(Math.random() * (outerRadius * outerRadius - innerRadius * innerRadius) + innerRadius * innerRadius);
const x = centerX + radius * Math.cos(angle);
const y = centerY + radius * Math.sin(angle);
return { x, y };
}
// 检查两个点之间的距离
function distance(point1, point2) {
return Math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2);
}
// 创建镜像渐变
function createMirrorGradient(ctx, x, y, radius) {
const gradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
gradient.addColorStop(0, 'rgba(255, 255, 255, 1)');
gradient.addColorStop(0.5, 'rgba(255, 255, 255, 0)');
gradient.addColorStop(1, 'rgba(255, 255, 255, 1)');
return gradient;
}
// 绘制点
function drawPoint(point) {
const gradient = createMirrorGradient(ctx, point.x, point.y, point.radius);
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(point.x, point.y, point.radius, 0, 2 * Math.PI);
ctx.fill();
// 绘制点的边框
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
ctx.stroke();
// 在点的中心添加文本,字体大小与点的半径成比例
ctx.fillStyle = 'black';
const fontSize = point.radius * 0.8; // 字体大小与点的半径成比例
ctx.font = `${fontSize}px Arial`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('Text', point.x, point.y);
}
// 绘制随机点,确保点与点之间没有交集
function drawRandomPoints(numPoints) {
const points = [];
while (points.length < numPoints) {
const point = getRandomPointInRing(innerRadius, outerRadius);
point.radius = Math.random() * 10 + 5; // 随机生成点的半径,范围在5到15之间
let isValid = true;
for (let i = 0; i < points.length; i++) {
if (distance(point, points[i]) < point.radius + points[i].radius) {
isValid = false;
break;
}
}
if (isValid) {
point.originalY = point.y;
point.offset = Math.random() * 20 - 10; // 随机生成浮动幅度
points.push(point);
}
}
return points;
}
// 动画更新点的位置
function updatePoints(points) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制圆环
ctx.beginPath();
ctx.arc(centerX, centerY, outerRadius, 0, 2 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(centerX, centerY, innerRadius, 0, 2 * Math.PI);
ctx.stroke();
points.forEach(point => {
point.y = point.originalY + Math.sin(Date.now() / 500) * point.offset;
drawPoint(point);
});
requestAnimationFrame(() => updatePoints(points));
}
// 初始化并开始动画
const points = drawRandomPoints(20);
updatePoints(points);
</script>
</body>
</html>
四、缓存第一次位置
完整代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
</head>
<body>
<canvas height="500" id="myCanvas" width="500">
<script type="text/javascript">
// 获取 canvas 元素和绘图上下文
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 圆心坐标和半径
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const outerRadius = 200;
const innerRadius = 100;
// 在圆环范围内随机分布点
function getRandomPointInRing(innerRadius, outerRadius) {
const angle = Math.random() * 2 * Math.PI;
const radius = Math.sqrt(Math.random() * (outerRadius * outerRadius - innerRadius * innerRadius) + innerRadius * innerRadius);
const x = centerX + radius * Math.cos(angle);
const y = centerY + radius * Math.sin(angle);
return { x, y };
}
// 检查两个点之间的距离
function distance(point1, point2) {
return Math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2);
}
// 创建镜像渐变
function createMirrorGradient(ctx, x, y, radius) {
const gradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
gradient.addColorStop(0, 'rgba(255, 255, 255, 1)');
gradient.addColorStop(0.5, 'rgba(255, 255, 255, 0)');
gradient.addColorStop(1, 'rgba(255, 255, 255, 1)');
return gradient;
}
// 绘制点
function drawPoint(point) {
const gradient = createMirrorGradient(ctx, point.x, point.y, point.radius);
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(point.x, point.y, point.radius, 0, 2 * Math.PI);
ctx.fill();
// 绘制点的边框
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
ctx.stroke();
// 在点的中心添加文本,字体大小与点的半径成比例
ctx.fillStyle = 'black';
const fontSize = point.radius * 0.8; // 字体大小与点的半径成比例
ctx.font = `${fontSize}px Arial`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('Text', point.x, point.y);
}
// 绘制随机点,确保点与点之间没有交集
function drawRandomPoints(numPoints) {
const points = [];
while (points.length < numPoints) {
const point = getRandomPointInRing(innerRadius, outerRadius);
point.radius = Math.random() * 10 + 5; // 随机生成点的半径,范围在5到15之间
let isValid = true;
for (let i = 0; i < points.length; i++) {
if (distance(point, points[i]) < point.radius + points[i].radius) {
isValid = false;
break;
}
}
if (isValid) {
point.originalY = point.y;
point.offset = Math.random() * 20 - 10; // 随机生成浮动幅度
points.push(point);
}
}
return points;
}
// 动画更新点的位置
function updatePoints(points) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制圆环
ctx.beginPath();
ctx.arc(centerX, centerY, outerRadius, 0, 2 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(centerX, centerY, innerRadius, 0, 2 * Math.PI);
ctx.stroke();
points.forEach(point => {
point.y = point.originalY + Math.sin(Date.now() / 500) * point.offset;
drawPoint(point);
});
requestAnimationFrame(() => updatePoints(points));
}
// 初始化并开始动画
function init() {
if (!localStorage.getItem('cachedPoints')) {
localStorage.setItem('cachedPoints', JSON.stringify(drawRandomPoints(20)))
}
updatePoints(JSON.parse(localStorage.getItem('cachedPoints')));
}
init();
</script>
</body>
</html>
五、自定义canvas宽高随机方向浮动
完整代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
</head>
<body>
<canvas id="myCanvas">
</canvas>
<script type="text/javascript">
// 获取 canvas 元素和绘图上下文
const canvas = document.getElementById('myCanvas');
// 设置 canvas 的宽高
const canvasWidth = 800; // 自定义宽度
const canvasHeight = 600; // 自定义高度
canvas.width = canvasWidth;
canvas.height = canvasHeight;
const ctx = canvas.getContext('2d');
// 圆心坐标和半径
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const outerRadius = 200;
const innerRadius = 100;
// 用于缓存随机点位置的数据
let cachedPoints = null;
// 在圆环范围内随机分布点
function getRandomPointInRing(innerRadius, outerRadius) {
const angle = Math.random() * 2 * Math.PI;
const radius = Math.sqrt(Math.random() * (outerRadius * outerRadius - innerRadius * innerRadius) + innerRadius * innerRadius);
const x = centerX + radius * Math.cos(angle);
const y = centerY + radius * Math.sin(angle);
return { x, y };
}
// 检查两个点之间的距离
function distance(point1, point2) {
return Math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2);
}
// 创建镜像渐变
function createMirrorGradient(ctx, x, y, radius) {
const gradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
gradient.addColorStop(0, 'rgba(255, 255, 255, 1)');
gradient.addColorStop(0.5, 'rgba(255, 255, 255, 0)');
gradient.addColorStop(1, 'rgba(255, 255, 255, 1)');
return gradient;
}
// 绘制点
function drawPoint(point) {
const gradient = createMirrorGradient(ctx, point.x, point.y, point.radius);
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(point.x, point.y, point.radius, 0, 2 * Math.PI);
ctx.fill();
// 绘制点的边框
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
ctx.stroke();
// 在点的中心添加文本,字体大小与点的半径成比例
ctx.fillStyle = 'black';
const fontSize = point.radius * 0.8; // 字体大小与点的半径成比例
ctx.font = `${fontSize}px Arial`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('Text', point.x, point.y);
}
// 绘制随机点,确保点与点之间没有交集
function drawRandomPoints(numPoints) {
const points = [];
while (points.length < numPoints) {
const point = getRandomPointInRing(innerRadius, outerRadius);
point.radius = Math.random() * 30; // 随机生成点的半径,范围在5到15之间
let isValid = true;
for (let i = 0; i < points.length; i++) {
if (distance(point, points[i]) < point.radius + points[i].radius) {
isValid = false;
break;
}
}
if (isValid) {
point.targetX = point.x;
point.targetY = point.y;
points.push(point);
}
}
return points;
}
// 动画更新点的位置
function updatePoints(points) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制圆环
ctx.beginPath();
ctx.arc(centerX, centerY, outerRadius, 0, 2 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(centerX, centerY, innerRadius, 0, 2 * Math.PI);
ctx.stroke();
points.forEach(point => {
// 如果点已经到达目标位置,生成一个新的目标位置
if (distance(point, { x: point.targetX, y: point.targetY }) < 1) {
const angle = Math.random() * 2 * Math.PI;
const moveDistance = Math.random() * 10; // 随机生成移动的距离,范围在10到30之间
point.targetX = point.x + moveDistance * Math.cos(angle);
point.targetY = point.y + moveDistance * Math.sin(angle);
}
// 计算移动的方向
const dx = point.targetX - point.x;
const dy = point.targetY - point.y;
const distanceToTarget = Math.sqrt(dx * dx + dy * dy);
const moveX = (dx / distanceToTarget) * 0.1; // 每帧移动的距离
const moveY = (dy / distanceToTarget) * 0.1; // 每帧移动的距离
// 更新点的位置
point.x += moveX;
point.y += moveY;
// 检查点是否超出了圆环范围,如果超出则反弹
const dxCenter = point.x - centerX;
const dyCenter = point.y - centerY;
const distanceFromCenter = Math.sqrt(dxCenter * dxCenter + dyCenter * dyCenter);
if (distanceFromCenter + point.radius > outerRadius) {
// 点超出外圆,反弹
const angle = Math.atan2(dyCenter, dxCenter);
point.x = centerX + (outerRadius - point.radius) * Math.cos(angle);
point.y = centerY + (outerRadius - point.radius) * Math.sin(angle);
point.targetX = point.x - moveX;
point.targetY = point.y - moveY;
} else if (distanceFromCenter - point.radius < innerRadius) {
// 点超出内圆,反弹
const angle = Math.atan2(dyCenter, dxCenter);
point.x = centerX + (innerRadius + point.radius) * Math.cos(angle);
point.y = centerY + (innerRadius + point.radius) * Math.sin(angle);
point.targetX = point.x - moveX;
point.targetY = point.y - moveY;
}
// 碰撞检测和处理
points.forEach(otherPoint => {
if (point !== otherPoint && distance(point, otherPoint) < point.radius + otherPoint.radius) {
const overlap = point.radius + otherPoint.radius - distance(point, otherPoint);
const angle = Math.atan2(point.y - otherPoint.y, point.x - otherPoint.x);
point.x += overlap * Math.cos(angle) / 2;
point.y += overlap * Math.sin(angle) / 2;
otherPoint.x -= overlap * Math.cos(angle) / 2;
otherPoint.y -= overlap * Math.sin(angle) / 2;
point.targetX = point.x - moveX;
point.targetY = point.y - moveY;
otherPoint.targetX = otherPoint.x + moveX;
otherPoint.targetY = otherPoint.y + moveY;
}
});
drawPoint(point);
});
requestAnimationFrame(() => updatePoints(points));
}
// 初始化并开始动画
function init() {
if (!cachedPoints) {
cachedPoints = drawRandomPoints(20);
}
updatePoints(cachedPoints);
}
init();
</script>
</body>
</html>