Javascript提高:点击处产生渐变随机圆-由Deepseek产生
【代码】Javascript提高:点击处产生渐变随机圆-由Deepseek产生。
·
以下是使用 Canvas 实现“点击页面生成渐变圆”的完整代码。点击任意位置会生成一个径向渐变、半径随机的圆,并保留所有已绘制的圆。
```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Canvas 幻彩圆点 | 点击生成随机渐变圆</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none; /* 避免点击时选中文本 */
}
body {
background: #1a2a32;
overflow: hidden;
font-family: 'Segoe UI', 'Poppins', system-ui, -apple-system, sans-serif;
}
canvas {
display: block;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
cursor: crosshair;
background: #f8f9fc;
background-image: radial-gradient(circle at 25% 40%, rgba(0,0,0,0.02) 2%, transparent 2.5%),
radial-gradient(circle at 75% 85%, rgba(0,0,0,0.015) 1.5%, transparent 2%);
background-size: 48px 48px, 32px 32px;
transition: background 0.2s ease;
}
/* 控制面板 - 简洁现代 */
.info-panel {
position: fixed;
bottom: 20px;
left: 20px;
right: 20px;
background: rgba(30, 35, 45, 0.72);
backdrop-filter: blur(12px);
border-radius: 56px;
padding: 10px 20px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
gap: 12px;
z-index: 100;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
border: 1px solid rgba(255, 255, 255, 0.25);
font-weight: 500;
pointer-events: auto;
}
.info-text {
color: white;
font-size: 0.9rem;
letter-spacing: 0.5px;
background: rgba(0,0,0,0.4);
padding: 6px 14px;
border-radius: 40px;
backdrop-filter: blur(4px);
}
.info-text span {
font-weight: bold;
color: #ffd966;
}
button {
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(4px);
border: 1px solid rgba(255,255,240,0.6);
color: white;
padding: 6px 20px;
border-radius: 40px;
font-size: 0.85rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
font-family: inherit;
}
button:hover {
background: #ff8c42;
border-color: white;
transform: scale(0.96);
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
}
.counter {
background: #1e2a36;
color: #ffd966;
padding: 5px 14px;
border-radius: 36px;
font-size: 0.85rem;
font-family: monospace;
font-weight: bold;
}
@media (max-width: 560px) {
.info-panel { padding: 8px 16px; border-radius: 40px; }
.info-text { font-size: 0.7rem; }
button { padding: 4px 16px; font-size: 0.75rem; }
}
</style>
</head>
<body>
<canvas id="magicCanvas"></canvas>
<div class="info-panel">
<div class="info-text">
✨ 点击任意位置 · <span>径向渐变</span> + <span>随机半径</span>
</div>
<div class="counter" id="circleCounter">⚪ 圆点数量: 0</div>
<button id="clearBtn">🧹 清空画布</button>
</div>
<script>
(function() {
// ----- DOM 元素 -----
const canvas = document.getElementById('magicCanvas');
const ctx = canvas.getContext('2d');
const circleCounterSpan = document.getElementById('circleCounter');
const clearBtn = document.getElementById('clearBtn');
// ----- 存储所有圆的数据 -----
let circles = []; // 每个元素: { x, y, radius, outerColor, innerColor }
// ----- 随机工具函数 -----
// 随机整数 [min, max]
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// 随机半径 (20px ~ 130px,范围适中,视觉丰富)
function getRandomRadius() {
return randomInt(22, 128);
}
// 随机生成鲜艳的颜色 (HSL 模式 饱和度65% 亮度65%)
// 返回 hsl 字符串,也可以作为径向渐变外圈主色
function getRandomVividColor() {
const hue = Math.floor(Math.random() * 360);
// 饱和度 60% ~ 85% 鲜艳但柔和
const saturation = randomInt(60, 88);
// 亮度 55% ~ 75% 明亮不刺眼
const lightness = randomInt(55, 78);
return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
}
// 生成内圈颜色 (亮色/暖白带微透明感,让渐变更通透)
function getInnerGlowColor() {
// 柔白 / 淡金色 / 极浅粉,增加层次
const variants = [
'rgba(255, 255, 245, 0.95)',
'rgba(255, 250, 225, 0.92)',
'rgba(255, 248, 225, 0.96)',
'rgba(250, 245, 235, 0.96)',
'#fff9e8'
];
return variants[Math.floor(Math.random() * variants.length)];
}
// 根据圆的参数创建一个径向渐变对象 (基于当前context)
function createRadialGradientForCircle(ctx, cx, cy, radius, innerColor, outerColor) {
// 径向渐变: 从圆心 (cx,cy) 半径 0 开始,到半径 radius 结束
const gradient = ctx.createRadialGradient(cx, cy, 0, cx, cy, radius);
gradient.addColorStop(0, innerColor);
gradient.addColorStop(0.45, innerColor); // 内芯保持通透亮色
gradient.addColorStop(0.78, outerColor);
gradient.addColorStop(1, outerColor);
return gradient;
}
// ----- 绘制所有圆 (保留历史,不丢失) -----
function drawAllCircles() {
if (!ctx) return;
// 获取当前canvas的实际像素尺寸
const w = canvas.width;
const h = canvas.height;
// 清空画布并填充优雅的背景 (浅色网格质感)
ctx.clearRect(0, 0, w, h);
// 底色: 温柔米灰 + 微妙的噪点纹理感(通过径向渐变底纹)
const gradBack = ctx.createLinearGradient(0, 0, w, h);
gradBack.addColorStop(0, '#fefaf5');
gradBack.addColorStop(1, '#f0f2f5');
ctx.fillStyle = gradBack;
ctx.fillRect(0, 0, w, h);
// 极浅网格线装饰 (增加精致感)
ctx.save();
ctx.globalAlpha = 0.2;
ctx.beginPath();
ctx.strokeStyle = '#cbd5e1';
ctx.lineWidth = 0.5;
for (let i = 0; i < w; i += 40) {
ctx.beginPath();
ctx.moveTo(i, 0);
ctx.lineTo(i, h);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, i);
ctx.lineTo(w, i);
ctx.stroke();
}
ctx.restore();
// 绘制所有存储的圆 (从旧到新,后绘制的在上层视觉无影响)
for (let i = 0; i < circles.length; i++) {
const circle = circles[i];
const { x, y, radius, outerColor, innerColor } = circle;
// 创建该圆的径向渐变
const gradient = createRadialGradientForCircle(ctx, x, y, radius, innerColor, outerColor);
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fillStyle = gradient;
ctx.fill();
// 增加极细高光边缘 (让圆更立体,轻微白边)
ctx.save();
ctx.globalCompositeOperation = 'lighter';
ctx.beginPath();
ctx.arc(x, y, radius - 1, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255, 255, 240, 0.18)';
ctx.fill();
ctx.restore();
// 小光晕效果 (增强圆质感)
ctx.save();
ctx.shadowBlur = 0; // 重置阴影避免叠加过多性能
ctx.beginPath();
ctx.arc(x, y, radius * 0.2, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255, 255, 245, 0.4)';
ctx.fill();
ctx.restore();
}
// 更新显示圆的数量
circleCounterSpan.innerHTML = `⚪ 圆点数量: ${circles.length}`;
}
// ----- 添加新的圆 (点击位置) -----
function addCircleAtMouse(clientX, clientY) {
// 获取canvas相对浏览器视口的实际尺寸和偏移量
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width; // canvas物理像素与CSS比例
const scaleY = canvas.height / rect.height;
// 计算 canvas 画布上的实际坐标 (物理像素坐标)
let canvasX = (clientX - rect.left) * scaleX;
let canvasY = (clientY - rect.top) * scaleY;
// 边界修剪(防止超出画布导致渐变异常,但不影响绘制,但限制坐标在画布内更好)
canvasX = Math.min(Math.max(0, canvasX), canvas.width);
canvasY = Math.min(Math.max(0, canvasY), canvas.height);
// 随机半径
const radius = getRandomRadius();
// 随机外圈颜色(鲜艳,每次不同)
const outerColor = getRandomVividColor();
// 内圈柔和亮色
const innerColor = getInnerGlowColor();
// 存储圆的信息
circles.push({
x: canvasX,
y: canvasY,
radius: radius,
outerColor: outerColor,
innerColor: innerColor
});
// 重绘所有圆(包括新圆)
drawAllCircles();
}
// ----- 清空所有圆 -----
function clearAllCircles() {
circles = [];
drawAllCircles();
}
// ----- 自适应 canvas 尺寸 (保持高清,防止模糊) -----
function resizeCanvas() {
const w = window.innerWidth;
const h = window.innerHeight;
// 设置 canvas 实际像素尺寸 (避免CSS拉伸模糊)
canvas.width = w;
canvas.height = h;
// 重置画布样式尺寸
canvas.style.width = `${w}px`;
canvas.style.height = `${h}px`;
// 调整尺寸后必须重绘所有圆 (否则会丢失绘图)
drawAllCircles();
}
// ----- 窗口大小改变时,可选策略:保持原有圆的位置相对于新画布坐标不变还是清空?
// 为了更自然: 保留已绘制的圆,但因为画布大小改变,旧圆的坐标依然基于左上角但视觉上会偏移,
// 不过保留历史圆可以避免用户意外丢失作品,也更有趣。同时由于背景网格适应,坐标在逻辑上没变,
// 只是窗口变化后圆相对窗口位置变化,但用户在缩放后依然能看到之前作品,属于预期行为。不强制清空。
// 但为了完美展示,不清空,保留所有圆并重绘。(平滑过渡)
function handleResize() {
resizeCanvas();
// resizeCanvas 内部已经 drawAllCircles, 因为尺寸重置后,原来的坐标是基于绝对坐标,
// 画面仅仅是在新的canvas尺寸下重新绘制,位置相对于左上角不变。 完美保留。
// (如果希望清空可以调用clearAllCircles,但为了用户友好,保留)
}
// ----- 点击canvas事件 (生成圆) -----
function onCanvasClick(e) {
let clientX, clientY;
if (e.touches) {
// 移动端触摸支持
clientX = e.touches[0].clientX;
clientY = e.touches[0].clientY;
e.preventDefault();
} else {
clientX = e.clientX;
clientY = e.clientY;
}
addCircleAtMouse(clientX, clientY);
}
// ----- 移动端支持触摸生成圆 -----
function onCanvasTouch(e) {
e.preventDefault();
if (e.touches.length) {
const touch = e.touches[0];
addCircleAtMouse(touch.clientX, touch.clientY);
}
}
// ----- 初始化事件与尺寸 -----
function init() {
resizeCanvas();
// 监听鼠标点击
canvas.addEventListener('click', onCanvasClick);
// 触摸屏支持
canvas.addEventListener('touchstart', onCanvasTouch, { passive: false });
// 窗口大小改变
window.addEventListener('resize', () => {
handleResize();
});
// 清空按钮事件
clearBtn.addEventListener('click', (e) => {
e.stopPropagation();
clearAllCircles();
});
// 初始演示预置两个示例圆(吸引用户,展示效果但不干扰体验,可选)
// 为了让初始画布不单调,主动添加2个柔和展示圆(可注释,但添加后更直观)
setTimeout(() => {
if (circles.length === 0) {
// 示例圆1: 画布中央偏左
const demoRadius1 = 64;
const demoX1 = canvas.width * 0.3;
const demoY1 = canvas.height * 0.5;
circles.push({
x: demoX1,
y: demoY1,
radius: demoRadius1,
outerColor: '#f97316', // 暖橙
innerColor: '#fff2dd'
});
// 示例圆2: 右侧区域
const demoRadius2 = 78;
const demoX2 = canvas.width * 0.7;
const demoY2 = canvas.height * 0.65;
circles.push({
x: demoX2,
y: demoY2,
radius: demoRadius2,
outerColor: '#3b82f6', // 碧蓝
innerColor: '#eef5ff'
});
drawAllCircles();
}
}, 100);
}
// 启动
init();
})();
</script>
</body>
</html>
```
更多推荐



所有评论(0)