deepseek好像更新了,这次有点意思!
- 内容介绍
- 文章标签
- 相关推荐
特别声明:非专业测试,仅供各位佬参考。
deepseek好像更新了,我让它写一在鱼缸内放一串鞭炮的视觉动画diamond,然后一次写出来了!
image1499×941 226 KB
<!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>鱼缸鞭炮盛宴 - 菱形爆竹动画</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
body {
background: linear-gradient(145deg, #1a472a 0%, #0e2a1a 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-family: 'Segoe UI', '华文楷书', 'Microsoft YaHei', 'Poppins', cursive;
padding: 20px;
}
.container {
background: #2c1a0e;
border-radius: 48px;
padding: 20px 25px 25px 25px;
box-shadow: 0 20px 35px rgba(0,0,0,0.5), inset 0 1px 4px rgba(255,255,200,0.2);
}
canvas {
display: block;
margin: 0 auto;
border-radius: 32px;
box-shadow: 0 8px 25px rgba(0,0,0,0.5), inset 0 0 0 2px rgba(255,255,200,0.3);
cursor: pointer;
}
.info-panel {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 18px;
gap: 20px;
flex-wrap: wrap;
justify-content: center;
}
.btn-ignite {
background: radial-gradient(circle at 30% 10%, #ff7e2e, #c33c00);
border: none;
color: #fff5e0;
font-size: 1.4rem;
font-weight: bold;
padding: 8px 28px;
border-radius: 60px;
cursor: pointer;
box-shadow: 0 6px 0 0 #6a2c00;
transition: 0.07s linear;
font-family: inherit;
letter-spacing: 4px;
backdrop-filter: blur(2px);
}
.btn-ignite:active {
transform: translateY(4px);
box-shadow: 0 2px 0 0 #6a2c00;
}
.status {
background: #0f2120c9;
backdrop-filter: blur(5px);
padding: 6px 20px;
border-radius: 40px;
color: #fbe9c3;
font-weight: bold;
font-size: 1rem;
border-left: 4px solid #ffb347;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
}
.status span {
color: #ffd966;
font-size: 1.2rem;
margin-right: 8px;
}
.title-tag {
font-size: 0.85rem;
background: #00000066;
padding: 4px 12px;
border-radius: 20px;
color: #ddd;
}
</style>
</head>
<body>
<div>
<div class="container">
<canvas id="aquariumCanvas" width="900" height="550" style="width:900px;height:550px"></canvas>
<div class="info-panel">
<div class="status"><span>🐟</span> 鱼缸 · 菱形鞭炮串</div>
<button class="btn-ignite" id="igniteBtn">💎 点燃钻石鞭炮 💎</button>
<div class="title-tag">✨ 菱形火花 | 水中盛宴 ✨</div>
</div>
</div>
</div>
<script>
(function(){
// ----- 画布元素 -----
const canvas = document.getElementById('aquariumCanvas');
const ctx = canvas.getContext('2d');
// 鱼缸内部边界 (玻璃内胆区域,视觉效果)
const tankBounds = {
x: 55, // 左内壁
y: 48, // 顶内壁
w: 790, // 内宽
h: 470 // 内高
};
// ---------- 全局参数 ----------
let animationId = null;
let lastTimestamp = 0;
// ---------- 鱼群数据 ----------
const fishes = [];
function initFishes() {
fishes.length = 0;
// 两条优雅锦鲤风格
fishes.push({
x: tankBounds.x + 150, y: tankBounds.y + 180,
vx: 0.8, vy: 0.4,
angle: 0, color: '#FFB347', bodyColor: '#FF8C42',
size: 18, type: 'orange'
});
fishes.push({
x: tankBounds.x + 580, y: tankBounds.y + 300,
vx: -0.6, vy: 0.5,
angle: 0, color: '#F4C2C2', bodyColor: '#E6A8D7',
size: 20, type: 'pink'
});
fishes.push({
x: tankBounds.x + 360, y: tankBounds.y + 400,
vx: 0.5, vy: -0.3,
angle: 0, color: '#A7E0E0', bodyColor: '#7FC9C9',
size: 16, type: 'cyan'
});
}
// 气泡数组
let bubbles = [];
function addBubble() {
if (bubbles.length > 45) return;
bubbles.push({
x: tankBounds.x + Math.random() * tankBounds.w,
y: tankBounds.y + tankBounds.h - 5,
radius: 2 + Math.random() * 5,
speed: 0.6 + Math.random() * 1.2,
alpha: 0.6 + Math.random() * 0.4
});
}
// ---------- 💎 菱形鞭炮数据结构 ----------
let firecrackers = []; // 每个元素 {x, y, exploded}
let totalCrackers = 0;
let explodedCount = 0;
// 爆炸粒子系统 (菱形钻石火花)
let particles = [];
// 鞭炮点燃控制
let explodeIntervalId = null;
let currentExplodeIndex = 0;
let isExplodingActive = false; // 是否处于爆炸序列进行中
let resetTimeoutId = null;
let autoResetEnabled = true;
// 防止重复点燃/重置标志
let isResetting = false;
// 引线特效 (小火焰闪烁点)
let fuseSpark = { active: true, x: 0, y: 0, flicker: 0 };
// ---------- 生成鞭炮串 (菱形钻石鞭炮) ----------
function generateFirecrackers() {
const startX = tankBounds.x + 180; // 悬挂起始点偏左优雅
const startY = tankBounds.y + 35;
const count = 12; // 12颗菱形鞭炮
const stepY = 28; // 垂直间距
const amplitude = 18; // 横向摆动幅度 (钻石串自然曲线)
const positions = [];
for (let i = 0; i < count; i++) {
let offsetX = Math.sin(i * 0.65) * amplitude;
// 最后几颗稍微向内收
if(i > 8) offsetX *= 0.8;
let x = startX + offsetX;
let y = startY + i * stepY;
// 确保鞭炮在鱼缸范围内 (边界安全)
x = Math.min(tankBounds.x + tankBounds.w - 18, Math.max(tankBounds.x + 12, x));
y = Math.min(tankBounds.y + tankBounds.h - 22, Math.max(tankBounds.y + 15, y));
positions.push({ x, y, exploded: false });
}
return positions;
}
// 重置整个鞭炮系统 (清空爆炸, 重新生成)
function resetFirecrackersSystem(keepAutoFlag = true) {
if(explodeIntervalId) {
clearInterval(explodeIntervalId);
explodeIntervalId = null;
}
if(resetTimeoutId) {
clearTimeout(resetTimeoutId);
resetTimeoutId = null;
}
isExplodingActive = false;
currentExplodeIndex = 0;
// 重置鞭炮数组
firecrackers = generateFirecrackers();
totalCrackers = firecrackers.length;
explodedCount = firecrackers.filter(c => c.exploded).length;
// 清空粒子 (保留一点水效果,但全部清除)
particles = [];
// 引线火花重新活跃
fuseSpark.active = true;
// 更新引线坐标 (取第一颗鞭炮位置上方)
if(firecrackers.length > 0) {
const first = firecrackers[0];
fuseSpark.x = first.x;
fuseSpark.y = first.y - 12;
}
// 如果保持自动模式,等待1.2秒后自动点燃(全新鞭炮串)
if(keepAutoFlag && autoResetEnabled) {
if(resetTimeoutId) clearTimeout(resetTimeoutId);
resetTimeoutId = setTimeout(() => {
if(!isExplodingActive && !isResetting) {
startExplodeSequence();
}
resetTimeoutId = null;
}, 700);
}
}
// 单个鞭炮爆炸: 生成菱形钻石粒子特效
function explodeSingleCracker(index) {
if(index >= firecrackers.length) return false;
const cracker = firecrackers[index];
if(cracker.exploded) return false;
// 标记为已爆炸
cracker.exploded = true;
explodedCount++;
// 生成钻石火花粒子 (菱形 + 闪耀)
const particleCount = 20 + Math.floor(Math.random() * 12);
const baseX = cracker.x;
const baseY = cracker.y;
for(let i=0; i<particleCount; i++) {
const angle = Math.random() * Math.PI * 2;
const speed = 1.6 + Math.random() * 3.8;
const vx = Math.cos(angle) * speed;
const vy = Math.sin(angle) * speed - 1.2; // 轻微上升飘散
const size = 4 + Math.random() * 7;
const colorChoice = Math.random();
let color;
if(colorChoice < 0.6) color = `hsl(${25 + Math.random() * 25}, 90%, 58%)`; // 橙红
else if(colorChoice < 0.85) color = `hsl(48, 95%, 65%)`; // 金黄
else color = `hsl(0, 85%, 70%)`; // 亮红
// 钻石菱形特效自带光晕
particles.push({
x: baseX, y: baseY,
vx: vx, vy: vy,
size: size,
alpha: 0.95,
color: color,
gravity: 0.18,
life: 0.95,
decay: 0.018 + Math.random() * 0.02,
spin: Math.random() * Math.PI * 2,
shape: 'diamond'
});
}
// 额外添加火星小粒子 (增加华丽感)
for(let i=0;i<8;i++) {
particles.push({
x: baseX, y: baseY,
vx: (Math.random() - 0.5) * 3.2, vy: (Math.random() - 0.8) * 2.5 - 1,
size: 2 + Math.random() * 4,
alpha: 1,
color: `hsl(${35 + Math.random() * 30}, 100%, 65%)`,
gravity: 0.12,
life: 0.9,
decay: 0.025,
shape: 'spark'
});
}
// 爆炸瞬间闪光效果 (在draw中增加全屏闪不太合适, 但局部光晕用粒子已经体现)
return true;
}
// 顺序爆炸序列 (每颗间隔)
function startExplodeSequence() {
if(isExplodingActive) return; // 已经在爆炸中
if(explodeIntervalId) clearInterval(explodeIntervalId);
// 重置爆炸索引,找第一个未爆炸的鞭炮
let firstUnexploded = -1;
for(let i=0; i<firecrackers.length; i++) {
if(!firecrackers[i].exploded) {
firstUnexploded = i;
break;
}
}
if(firstUnexploded === -1) {
// 已经全部爆炸了,等待重置
return;
}
currentExplodeIndex = firstUnexploded;
isExplodingActive = true;
fuseSpark.active = false; // 引线燃尽消失
// 定时逐个爆炸 (模拟引线燃烧)
explodeIntervalId = setInterval(() => {
if(currentExplodeIndex >= firecrackers.length) {
// 全部检查完毕
clearInterval(explodeIntervalId);
explodeIntervalId = null;
isExplodingActive = false;
// 检查是否所有鞭炮均已爆炸,如果是则安排重置
const allExploded = firecrackers.every(c => c.exploded === true);
if(allExploded && autoResetEnabled) {
if(resetTimeoutId) clearTimeout(resetTimeoutId);
resetTimeoutId = setTimeout(() => {
resetFirecrackersSystem(true);
resetTimeoutId = null;
}, 1800);
}
return;
}
// 如果当前鞭炮还未爆炸则引爆
if(!firecrackers[currentExplodeIndex].exploded) {
explodeSingleCracker(currentExplodeIndex);
}
currentExplodeIndex++;
// 如果爆炸期间所有都已经炸完, 下一次interval将结束
if(currentExplodeIndex >= firecrackers.length) {
// 最后检查一次额外爆炸可能遗留的,但会在下一轮停止
const allNow = firecrackers.every(c => c.exploded === true);
if(allNow) {
clearInterval(explodeIntervalId);
explodeIntervalId = null;
isExplodingActive = false;
if(autoResetEnabled) {
if(resetTimeoutId) clearTimeout(resetTimeoutId);
resetTimeoutId = setTimeout(() => {
resetFirecrackersSystem(true);
resetTimeoutId = null;
}, 1800);
}
}
}
}, 180); // 每个鞭炮间隔180ms,噼里啪啦节奏
}
// 手动点燃或重新燃放 (外部按钮调用)
function manualIgnite() {
if(isResetting) return;
// 停止当前所有爆炸及重置计划
if(explodeIntervalId) {
clearInterval(explodeIntervalId);
explodeIntervalId = null;
}
if(resetTimeoutId) {
clearTimeout(resetTimeoutId);
resetTimeoutId = null;
}
isExplodingActive = false;
// 重置整个系统 (重新生成鞭炮串,清除粒子)
isResetting = true;
// 清空粒子并重新初始化
particles = [];
firecrackers = generateFirecrackers();
totalCrackers = firecrackers.length;
explodedCount = 0;
currentExplodeIndex = 0;
// 激活引线火花
if(firecrackers.length > 0) {
fuseSpark.active = true;
const firstPos = firecrackers[0];
fuseSpark.x = firstPos.x;
fuseSpark.y = firstPos.y - 12;
}
isExplodingActive = false;
// 开始新的爆炸序列 (延时一点点让视觉重置完成)
setTimeout(() => {
isResetting = false;
startExplodeSequence();
}, 80);
}
// ---------- 鱼群运动逻辑 ----------
function updateFishes() {
for(let fish of fishes) {
fish.x += fish.vx;
fish.y += fish.vy;
// 边界反弹 (鱼缸内壁预留边距)
const margin = 12;
if(fish.x < tankBounds.x + margin) { fish.x = tankBounds.x + margin; fish.vx = -fish.vx; }
if(fish.x > tankBounds.x + tankBounds.w - margin) { fish.x = tankBounds.x + tankBounds.w - margin; fish.vx = -fish.vx; }
if(fish.y < tankBounds.y + margin) { fish.y = tankBounds.y + margin; fish.vy = -fish.vy; }
if(fish.y > tankBounds.y + tankBounds.h - margin - 8) { fish.y = tankBounds.y + tankBounds.h - margin - 8; fish.vy = -fish.vy; }
// 计算鱼的角度
fish.angle = Math.atan2(fish.vy, fish.vx);
}
}
// 更新气泡
function updateBubbles() {
for(let i=0; i<bubbles.length; i++) {
bubbles[i].y -= bubbles[i].speed;
if(bubbles[i].y < tankBounds.y - 5) {
bubbles.splice(i,1);
i--;
}
}
if(Math.random() < 0.25) addBubble();
}
// 更新粒子系统 (钻石火花)
function updateParticles() {
for(let i=0; i<particles.length; i++) {
const p = particles[i];
p.vx *= 0.99;
p.vy += p.gravity;
p.x += p.vx;
p.y += p.vy;
p.life -= p.decay;
p.alpha = p.life;
if(p.x < tankBounds.x - 30 || p.x > tankBounds.x + tankBounds.w + 30 || p.y > tankBounds.y + tankBounds.h + 50 || p.y < tankBounds.y - 40 || p.life <= 0.02) {
particles.splice(i,1);
i--;
}
}
}
// ---------- 绘图函数 (华丽鱼缸 + 钻石鞭炮) ----------
function drawAquariumBackground() {
// 玻璃外框
ctx.save();
// 鱼缸外框圆角
ctx.shadowBlur = 0;
ctx.beginPath();
ctx.roundRect(tankBounds.x-8, tankBounds.y-8, tankBounds.w+16, tankBounds.h+16, 28);
ctx.fillStyle = '#C7E2F0';
ctx.fill();
ctx.shadowColor = 'rgba(0,0,0,0.3)';
ctx.fillStyle = '#EAF7FF';
ctx.beginPath();
ctx.roundRect(tankBounds.x-5, tankBounds.y-5, tankBounds.w+10, tankBounds.h+10, 20);
ctx.fill();
// 水体渐变
const waterGrad = ctx.createLinearGradient(tankBounds.x, tankBounds.y, tankBounds.x, tankBounds.y+tankBounds.h);
waterGrad.addColorStop(0, '#7ec8e0');
waterGrad.addColorStop(0.5, '#4ba0c0');
waterGrad.addColorStop(1, '#2e6f8f');
ctx.fillStyle = waterGrad;
ctx.beginPath();
ctx.roundRect(tankBounds.x, tankBounds.y, tankBounds.w, tankBounds.h, 18);
ctx.fill();
// 水纹高光
ctx.globalAlpha = 0.2;
for(let i=0;i<70;i++) {
ctx.beginPath();
ctx.arc(tankBounds.x + Math.random()*tankBounds.w, tankBounds.y + Math.random()*tankBounds.h, Math.random()*3+1,0,Math.PI*2);
ctx.fillStyle = '#FFFFFF';
ctx.fill();
}
ctx.globalAlpha = 1;
// 沙石底部
ctx.fillStyle = '#D2B48C';
ctx.beginPath();
ctx.rect(tankBounds.x, tankBounds.y+tankBounds.h-35, tankBounds.w, 38);
ctx.fill();
ctx.fillStyle = '#BC9A6C';
for(let i=0;i<120;i++) {
ctx.beginPath();
ctx.arc(tankBounds.x + Math.random()*tankBounds.w, tankBounds.y+tankBounds.h-25 + Math.random()*18, Math.random()*2+1,0,Math.PI*2);
ctx.fill();
}
// 水草点缀
ctx.fillStyle = '#3C9E3D';
for(let i=0;i<12;i++) {
ctx.beginPath();
ctx.moveTo(tankBounds.x+20+i*65, tankBounds.y+tankBounds.h-28);
ctx.lineTo(tankBounds.x+10+i*65, tankBounds.y+tankBounds.h-48);
ctx.lineTo(tankBounds.x+30+i*65, tankBounds.y+tankBounds.h-52);
ctx.fill();
}
}
function drawFish(f) {
ctx.save();
ctx.translate(f.x, f.y);
ctx.rotate(f.angle);
ctx.beginPath();
ctx.ellipse(0, 0, f.size, f.size*0.7, 0, 0, Math.PI*2);
ctx.fillStyle = f.bodyColor;
ctx.fill();
ctx.fillStyle = '#fff6d0';
ctx.beginPath();
ctx.arc(f.size*0.4, -f.size*0.2, f.size*0.2, 0, Math.PI*2);
ctx.fill();
ctx.fillStyle = '#000000';
ctx.beginPath();
ctx.arc(f.size*0.48, -f.size*0.22, f.size*0.07, 0, Math.PI*2);
ctx.fill();
ctx.beginPath();
ctx.moveTo(f.size*0.7, 0);
ctx.lineTo(f.size*1.1, -f.size*0.25);
ctx.lineTo(f.size*1.1, f.size*0.25);
ctx.fillStyle = '#e09d32';
ctx.fill();
ctx.restore();
}
// 绘制菱形鞭炮 (钻石风格)
function drawDiamondCracker(x, y, isExploded) {
if(isExploded) return;
ctx.save();
ctx.shadowBlur = 8;
ctx.shadowColor = "#ffaa33";
ctx.translate(x, y);
ctx.rotate(Math.PI/4);
const size = 12;
// 菱形主体
ctx.fillStyle = '#E34234';
ctx.beginPath();
ctx.rect(-size/1.8, -size/1.8, size, size);
ctx.fill();
ctx.fillStyle = '#FF6347';
ctx.beginPath();
ctx.rect(-size/2.5, -size/2.5, size-3, size-3);
ctx.fill();
// 金色镶边
ctx.strokeStyle = '#F7D44A';
ctx.lineWidth = 1.8;
ctx.strokeRect(-size/1.8, -size/1.8, size, size);
// 高光
ctx.fillStyle = '#FFF3BF';
ctx.beginPath();
ctx.rect(-size/3.5, -size/3.5, size/2.5, size/2.5);
ctx.fill();
ctx.restore();
// 小亮点
ctx.beginPath();
ctx.arc(x-2, y-2, 2, 0, Math.PI*2);
ctx.fillStyle = '#FFE484';
ctx.shadowBlur = 5;
ctx.fill();
}
function drawFirecrackersString() {
if(firecrackers.length === 0) return;
// 绘制连接绳子
ctx.beginPath();
ctx.strokeStyle = '#8B5A2B';
ctx.lineWidth = 2.5;
ctx.shadowBlur = 2;
for(let i=0; i<firecrackers.length-1; i++) {
if(!firecrackers[i].exploded || !firecrackers[i+1].exploded) {
ctx.beginPath();
ctx.moveTo(firecrackers[i].x, firecrackers[i].y-3);
ctx.lineTo(firecrackers[i+1].x, firecrackers[i+1].y-3);
ctx.stroke();
}
}
// 绘制顶部悬挂线 & 引线
if(firecrackers.length>0 && !firecrackers[0].exploded) {
const first = firecrackers[0];
ctx.beginPath();
ctx.moveTo(first.x, first.y-12);
ctx.lineTo(first.x-8, first.y-28);
ctx.lineTo(first.x+4, first.y-44);
ctx.strokeStyle = '#AA7C4A';
ctx.lineWidth = 2;
ctx.stroke();
// 引线火焰 (点燃之前如果 fuseSpark.active 未爆炸时显示)
if(fuseSpark.active && !isExplodingActive && explodedCount===0) {
ctx.beginPath();
ctx.arc(first.x-3, first.y-38, 5+Math.sin(Date.now()*0.015)*2, 0, Math.PI*2);
ctx.fillStyle = `hsl(30, 100%, 60%)`;
ctx.fill();
ctx.beginPath();
ctx.arc(first.x-3, first.y-38, 2+Math.sin(Date.now()*0.02)*1, 0, Math.PI*2);
ctx.fillStyle = `#FFD966`;
ctx.fill();
}
}
// 绘制每个未爆炸的菱形鞭炮
for(let c of firecrackers) {
if(!c.exploded) {
drawDiamondCracker(c.x, c.y, false);
}
}
ctx.shadowBlur = 0;
}
// 绘制钻石粒子 (所有爆炸碎片)
function drawParticles() {
for(let p of particles) {
ctx.save();
ctx.globalAlpha = Math.min(p.alpha, 0.9);
ctx.shadowBlur = 5;
ctx.shadowColor = "#ffaa66";
if(p.shape === 'diamond') {
ctx.translate(p.x, p.y);
ctx.rotate(Math.PI/4);
ctx.fillStyle = p.color;
ctx.fillRect(-p.size/2, -p.size/2, p.size, p.size);
ctx.fillStyle = 'gold';
ctx.fillRect(-p.size/3, -p.size/3, p.size/1.6, p.size/1.6);
} else {
ctx.beginPath();
ctx.arc(p.x, p.y, p.size/1.6, 0, Math.PI*2);
ctx.fillStyle = p.color;
ctx.fill();
}
ctx.restore();
}
}
// 绘制气泡
function drawBubbles() {
for(let b of bubbles) {
ctx.beginPath();
ctx.arc(b.x, b.y, b.radius, 0, Math.PI*2);
ctx.fillStyle = `rgba(255, 255, 245, ${b.alpha*0.6})`;
ctx.fill();
ctx.strokeStyle = `rgba(255,255,210,0.5)`;
ctx.stroke();
}
}
// 主动画循环
function animate() {
updateFishes();
updateBubbles();
updateParticles();
// 清空并重绘
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawAquariumBackground();
drawBubbles();
for(let fish of fishes) drawFish(fish);
drawFirecrackersString();
drawParticles();
// 额外: 如果鞭炮全部炸完且未重置 显示提示
if(explodedCount === totalCrackers && totalCrackers>0 && !isExplodingActive && !resetTimeoutId && autoResetEnabled) {
ctx.font = "bold 18m 'Segoe UI'";
ctx.fillStyle = "#FFF8E7";
ctx.shadowBlur = 6;
ctx.fillText("✨ 鞭炮绽放,新的一串即将出现 ✨", tankBounds.x+200, tankBounds.y+40);
}
requestAnimationFrame(animate);
}
// 初始化整个场景
function init() {
initFishes();
for(let i=0;i<25;i++) addBubble();
firecrackers = generateFirecrackers();
totalCrackers = firecrackers.length;
explodedCount = 0;
if(firecrackers.length) {
fuseSpark.active = true;
fuseSpark.x = firecrackers[0].x;
fuseSpark.y = firecrackers[0].y - 12;
}
autoResetEnabled = true;
// 自动启动点燃序列(首次入场)
setTimeout(() => {
if(!isExplodingActive && firecrackers.some(c=>!c.exploded)) {
startExplodeSequence();
}
}, 600);
// 绑定按钮事件
const btn = document.getElementById('igniteBtn');
btn.addEventListener('click', () => {
manualIgnite();
});
animate();
}
// 工具: canvas roundRect
if (!CanvasRenderingContext2D.prototype.roundRect) {
CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) {
if (w < 2 * r) r = w / 2;
if (h < 2 * r) r = h / 2;
this.moveTo(x+r, y);
this.lineTo(x+w-r, y);
this.quadraticCurveTo(x+w, y, x+w, y+r);
this.lineTo(x+w, y+h-r);
this.quadraticCurveTo(x+w, y+h, x+w-r, y+h);
this.lineTo(x+r, y+h);
this.quadraticCurveTo(x, y+h, x, y+h-r);
this.lineTo(x, y+r);
this.quadraticCurveTo(x, y, x+r, y);
return this;
};
}
init();
})();
</script>
</body>
</html>
随后测试了Qwen3.6PlusFree,geminipro,ChatGPT,MiMo-V2-Pro,GLM-5。具体效果各位自行判断。所有是测试除了Qwen3.6PlusFree官网没找到在OpenCode,其它的都是在官方的网页上测试的。
Qwen3.6PlusFree
geminipro
MiMo-V2-Pro
ChatGPT
GLM-5
--【壹】--:
代表谴责一下,为什么要在鱼缸里面放鞭炮!!!
--【贰】--:
这种前端代码,感觉基本都能搞定的,之前的版本你是测试写出来不对吗?
--【叁】--:
抓紧发布才是硬道理
--【肆】--:
要有对比的吧,万一之前就可以呢
--【伍】--:
期待ds的高光时刻
--【陆】--:
感觉这就是deepseek的v4版本,只是还在调整阶段,调整好了就可以发布了
--【柒】--:
不知道什么时候正式发布v4
--【捌】--:
赶紧发布吧,等了好久了
--【玖】--:
image1686×1403 598 KB
同样的提示词,我试了下,没一次就跑通
--【拾】--:
因为这个最直观,也有一定的代码能力,逻辑能力,视觉能力的验证
--【拾壹】--:
Ds工作的老同学说在灰度 这次会有很大提升 所以大家坐等官方发布吧
--【拾贰】--:
测试视觉效果的好多,有什么说法吗
--【拾叁】--:
感觉效果挺好的,看什么时候官宣了
--【拾肆】--:
这个月初能发吗
--【拾伍】--:
期待国产崛起
--【拾陆】--:
期待国产崛起
--【拾柒】--:
看起来效果不错啊
--【拾捌】--:
前两天就看到说在测试了
--【拾玖】--:
这阵子一直在传的 v4 lite?
特别声明:非专业测试,仅供各位佬参考。
deepseek好像更新了,我让它写一在鱼缸内放一串鞭炮的视觉动画diamond,然后一次写出来了!
image1499×941 226 KB
<!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>鱼缸鞭炮盛宴 - 菱形爆竹动画</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
body {
background: linear-gradient(145deg, #1a472a 0%, #0e2a1a 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-family: 'Segoe UI', '华文楷书', 'Microsoft YaHei', 'Poppins', cursive;
padding: 20px;
}
.container {
background: #2c1a0e;
border-radius: 48px;
padding: 20px 25px 25px 25px;
box-shadow: 0 20px 35px rgba(0,0,0,0.5), inset 0 1px 4px rgba(255,255,200,0.2);
}
canvas {
display: block;
margin: 0 auto;
border-radius: 32px;
box-shadow: 0 8px 25px rgba(0,0,0,0.5), inset 0 0 0 2px rgba(255,255,200,0.3);
cursor: pointer;
}
.info-panel {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 18px;
gap: 20px;
flex-wrap: wrap;
justify-content: center;
}
.btn-ignite {
background: radial-gradient(circle at 30% 10%, #ff7e2e, #c33c00);
border: none;
color: #fff5e0;
font-size: 1.4rem;
font-weight: bold;
padding: 8px 28px;
border-radius: 60px;
cursor: pointer;
box-shadow: 0 6px 0 0 #6a2c00;
transition: 0.07s linear;
font-family: inherit;
letter-spacing: 4px;
backdrop-filter: blur(2px);
}
.btn-ignite:active {
transform: translateY(4px);
box-shadow: 0 2px 0 0 #6a2c00;
}
.status {
background: #0f2120c9;
backdrop-filter: blur(5px);
padding: 6px 20px;
border-radius: 40px;
color: #fbe9c3;
font-weight: bold;
font-size: 1rem;
border-left: 4px solid #ffb347;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
}
.status span {
color: #ffd966;
font-size: 1.2rem;
margin-right: 8px;
}
.title-tag {
font-size: 0.85rem;
background: #00000066;
padding: 4px 12px;
border-radius: 20px;
color: #ddd;
}
</style>
</head>
<body>
<div>
<div class="container">
<canvas id="aquariumCanvas" width="900" height="550" style="width:900px;height:550px"></canvas>
<div class="info-panel">
<div class="status"><span>🐟</span> 鱼缸 · 菱形鞭炮串</div>
<button class="btn-ignite" id="igniteBtn">💎 点燃钻石鞭炮 💎</button>
<div class="title-tag">✨ 菱形火花 | 水中盛宴 ✨</div>
</div>
</div>
</div>
<script>
(function(){
// ----- 画布元素 -----
const canvas = document.getElementById('aquariumCanvas');
const ctx = canvas.getContext('2d');
// 鱼缸内部边界 (玻璃内胆区域,视觉效果)
const tankBounds = {
x: 55, // 左内壁
y: 48, // 顶内壁
w: 790, // 内宽
h: 470 // 内高
};
// ---------- 全局参数 ----------
let animationId = null;
let lastTimestamp = 0;
// ---------- 鱼群数据 ----------
const fishes = [];
function initFishes() {
fishes.length = 0;
// 两条优雅锦鲤风格
fishes.push({
x: tankBounds.x + 150, y: tankBounds.y + 180,
vx: 0.8, vy: 0.4,
angle: 0, color: '#FFB347', bodyColor: '#FF8C42',
size: 18, type: 'orange'
});
fishes.push({
x: tankBounds.x + 580, y: tankBounds.y + 300,
vx: -0.6, vy: 0.5,
angle: 0, color: '#F4C2C2', bodyColor: '#E6A8D7',
size: 20, type: 'pink'
});
fishes.push({
x: tankBounds.x + 360, y: tankBounds.y + 400,
vx: 0.5, vy: -0.3,
angle: 0, color: '#A7E0E0', bodyColor: '#7FC9C9',
size: 16, type: 'cyan'
});
}
// 气泡数组
let bubbles = [];
function addBubble() {
if (bubbles.length > 45) return;
bubbles.push({
x: tankBounds.x + Math.random() * tankBounds.w,
y: tankBounds.y + tankBounds.h - 5,
radius: 2 + Math.random() * 5,
speed: 0.6 + Math.random() * 1.2,
alpha: 0.6 + Math.random() * 0.4
});
}
// ---------- 💎 菱形鞭炮数据结构 ----------
let firecrackers = []; // 每个元素 {x, y, exploded}
let totalCrackers = 0;
let explodedCount = 0;
// 爆炸粒子系统 (菱形钻石火花)
let particles = [];
// 鞭炮点燃控制
let explodeIntervalId = null;
let currentExplodeIndex = 0;
let isExplodingActive = false; // 是否处于爆炸序列进行中
let resetTimeoutId = null;
let autoResetEnabled = true;
// 防止重复点燃/重置标志
let isResetting = false;
// 引线特效 (小火焰闪烁点)
let fuseSpark = { active: true, x: 0, y: 0, flicker: 0 };
// ---------- 生成鞭炮串 (菱形钻石鞭炮) ----------
function generateFirecrackers() {
const startX = tankBounds.x + 180; // 悬挂起始点偏左优雅
const startY = tankBounds.y + 35;
const count = 12; // 12颗菱形鞭炮
const stepY = 28; // 垂直间距
const amplitude = 18; // 横向摆动幅度 (钻石串自然曲线)
const positions = [];
for (let i = 0; i < count; i++) {
let offsetX = Math.sin(i * 0.65) * amplitude;
// 最后几颗稍微向内收
if(i > 8) offsetX *= 0.8;
let x = startX + offsetX;
let y = startY + i * stepY;
// 确保鞭炮在鱼缸范围内 (边界安全)
x = Math.min(tankBounds.x + tankBounds.w - 18, Math.max(tankBounds.x + 12, x));
y = Math.min(tankBounds.y + tankBounds.h - 22, Math.max(tankBounds.y + 15, y));
positions.push({ x, y, exploded: false });
}
return positions;
}
// 重置整个鞭炮系统 (清空爆炸, 重新生成)
function resetFirecrackersSystem(keepAutoFlag = true) {
if(explodeIntervalId) {
clearInterval(explodeIntervalId);
explodeIntervalId = null;
}
if(resetTimeoutId) {
clearTimeout(resetTimeoutId);
resetTimeoutId = null;
}
isExplodingActive = false;
currentExplodeIndex = 0;
// 重置鞭炮数组
firecrackers = generateFirecrackers();
totalCrackers = firecrackers.length;
explodedCount = firecrackers.filter(c => c.exploded).length;
// 清空粒子 (保留一点水效果,但全部清除)
particles = [];
// 引线火花重新活跃
fuseSpark.active = true;
// 更新引线坐标 (取第一颗鞭炮位置上方)
if(firecrackers.length > 0) {
const first = firecrackers[0];
fuseSpark.x = first.x;
fuseSpark.y = first.y - 12;
}
// 如果保持自动模式,等待1.2秒后自动点燃(全新鞭炮串)
if(keepAutoFlag && autoResetEnabled) {
if(resetTimeoutId) clearTimeout(resetTimeoutId);
resetTimeoutId = setTimeout(() => {
if(!isExplodingActive && !isResetting) {
startExplodeSequence();
}
resetTimeoutId = null;
}, 700);
}
}
// 单个鞭炮爆炸: 生成菱形钻石粒子特效
function explodeSingleCracker(index) {
if(index >= firecrackers.length) return false;
const cracker = firecrackers[index];
if(cracker.exploded) return false;
// 标记为已爆炸
cracker.exploded = true;
explodedCount++;
// 生成钻石火花粒子 (菱形 + 闪耀)
const particleCount = 20 + Math.floor(Math.random() * 12);
const baseX = cracker.x;
const baseY = cracker.y;
for(let i=0; i<particleCount; i++) {
const angle = Math.random() * Math.PI * 2;
const speed = 1.6 + Math.random() * 3.8;
const vx = Math.cos(angle) * speed;
const vy = Math.sin(angle) * speed - 1.2; // 轻微上升飘散
const size = 4 + Math.random() * 7;
const colorChoice = Math.random();
let color;
if(colorChoice < 0.6) color = `hsl(${25 + Math.random() * 25}, 90%, 58%)`; // 橙红
else if(colorChoice < 0.85) color = `hsl(48, 95%, 65%)`; // 金黄
else color = `hsl(0, 85%, 70%)`; // 亮红
// 钻石菱形特效自带光晕
particles.push({
x: baseX, y: baseY,
vx: vx, vy: vy,
size: size,
alpha: 0.95,
color: color,
gravity: 0.18,
life: 0.95,
decay: 0.018 + Math.random() * 0.02,
spin: Math.random() * Math.PI * 2,
shape: 'diamond'
});
}
// 额外添加火星小粒子 (增加华丽感)
for(let i=0;i<8;i++) {
particles.push({
x: baseX, y: baseY,
vx: (Math.random() - 0.5) * 3.2, vy: (Math.random() - 0.8) * 2.5 - 1,
size: 2 + Math.random() * 4,
alpha: 1,
color: `hsl(${35 + Math.random() * 30}, 100%, 65%)`,
gravity: 0.12,
life: 0.9,
decay: 0.025,
shape: 'spark'
});
}
// 爆炸瞬间闪光效果 (在draw中增加全屏闪不太合适, 但局部光晕用粒子已经体现)
return true;
}
// 顺序爆炸序列 (每颗间隔)
function startExplodeSequence() {
if(isExplodingActive) return; // 已经在爆炸中
if(explodeIntervalId) clearInterval(explodeIntervalId);
// 重置爆炸索引,找第一个未爆炸的鞭炮
let firstUnexploded = -1;
for(let i=0; i<firecrackers.length; i++) {
if(!firecrackers[i].exploded) {
firstUnexploded = i;
break;
}
}
if(firstUnexploded === -1) {
// 已经全部爆炸了,等待重置
return;
}
currentExplodeIndex = firstUnexploded;
isExplodingActive = true;
fuseSpark.active = false; // 引线燃尽消失
// 定时逐个爆炸 (模拟引线燃烧)
explodeIntervalId = setInterval(() => {
if(currentExplodeIndex >= firecrackers.length) {
// 全部检查完毕
clearInterval(explodeIntervalId);
explodeIntervalId = null;
isExplodingActive = false;
// 检查是否所有鞭炮均已爆炸,如果是则安排重置
const allExploded = firecrackers.every(c => c.exploded === true);
if(allExploded && autoResetEnabled) {
if(resetTimeoutId) clearTimeout(resetTimeoutId);
resetTimeoutId = setTimeout(() => {
resetFirecrackersSystem(true);
resetTimeoutId = null;
}, 1800);
}
return;
}
// 如果当前鞭炮还未爆炸则引爆
if(!firecrackers[currentExplodeIndex].exploded) {
explodeSingleCracker(currentExplodeIndex);
}
currentExplodeIndex++;
// 如果爆炸期间所有都已经炸完, 下一次interval将结束
if(currentExplodeIndex >= firecrackers.length) {
// 最后检查一次额外爆炸可能遗留的,但会在下一轮停止
const allNow = firecrackers.every(c => c.exploded === true);
if(allNow) {
clearInterval(explodeIntervalId);
explodeIntervalId = null;
isExplodingActive = false;
if(autoResetEnabled) {
if(resetTimeoutId) clearTimeout(resetTimeoutId);
resetTimeoutId = setTimeout(() => {
resetFirecrackersSystem(true);
resetTimeoutId = null;
}, 1800);
}
}
}
}, 180); // 每个鞭炮间隔180ms,噼里啪啦节奏
}
// 手动点燃或重新燃放 (外部按钮调用)
function manualIgnite() {
if(isResetting) return;
// 停止当前所有爆炸及重置计划
if(explodeIntervalId) {
clearInterval(explodeIntervalId);
explodeIntervalId = null;
}
if(resetTimeoutId) {
clearTimeout(resetTimeoutId);
resetTimeoutId = null;
}
isExplodingActive = false;
// 重置整个系统 (重新生成鞭炮串,清除粒子)
isResetting = true;
// 清空粒子并重新初始化
particles = [];
firecrackers = generateFirecrackers();
totalCrackers = firecrackers.length;
explodedCount = 0;
currentExplodeIndex = 0;
// 激活引线火花
if(firecrackers.length > 0) {
fuseSpark.active = true;
const firstPos = firecrackers[0];
fuseSpark.x = firstPos.x;
fuseSpark.y = firstPos.y - 12;
}
isExplodingActive = false;
// 开始新的爆炸序列 (延时一点点让视觉重置完成)
setTimeout(() => {
isResetting = false;
startExplodeSequence();
}, 80);
}
// ---------- 鱼群运动逻辑 ----------
function updateFishes() {
for(let fish of fishes) {
fish.x += fish.vx;
fish.y += fish.vy;
// 边界反弹 (鱼缸内壁预留边距)
const margin = 12;
if(fish.x < tankBounds.x + margin) { fish.x = tankBounds.x + margin; fish.vx = -fish.vx; }
if(fish.x > tankBounds.x + tankBounds.w - margin) { fish.x = tankBounds.x + tankBounds.w - margin; fish.vx = -fish.vx; }
if(fish.y < tankBounds.y + margin) { fish.y = tankBounds.y + margin; fish.vy = -fish.vy; }
if(fish.y > tankBounds.y + tankBounds.h - margin - 8) { fish.y = tankBounds.y + tankBounds.h - margin - 8; fish.vy = -fish.vy; }
// 计算鱼的角度
fish.angle = Math.atan2(fish.vy, fish.vx);
}
}
// 更新气泡
function updateBubbles() {
for(let i=0; i<bubbles.length; i++) {
bubbles[i].y -= bubbles[i].speed;
if(bubbles[i].y < tankBounds.y - 5) {
bubbles.splice(i,1);
i--;
}
}
if(Math.random() < 0.25) addBubble();
}
// 更新粒子系统 (钻石火花)
function updateParticles() {
for(let i=0; i<particles.length; i++) {
const p = particles[i];
p.vx *= 0.99;
p.vy += p.gravity;
p.x += p.vx;
p.y += p.vy;
p.life -= p.decay;
p.alpha = p.life;
if(p.x < tankBounds.x - 30 || p.x > tankBounds.x + tankBounds.w + 30 || p.y > tankBounds.y + tankBounds.h + 50 || p.y < tankBounds.y - 40 || p.life <= 0.02) {
particles.splice(i,1);
i--;
}
}
}
// ---------- 绘图函数 (华丽鱼缸 + 钻石鞭炮) ----------
function drawAquariumBackground() {
// 玻璃外框
ctx.save();
// 鱼缸外框圆角
ctx.shadowBlur = 0;
ctx.beginPath();
ctx.roundRect(tankBounds.x-8, tankBounds.y-8, tankBounds.w+16, tankBounds.h+16, 28);
ctx.fillStyle = '#C7E2F0';
ctx.fill();
ctx.shadowColor = 'rgba(0,0,0,0.3)';
ctx.fillStyle = '#EAF7FF';
ctx.beginPath();
ctx.roundRect(tankBounds.x-5, tankBounds.y-5, tankBounds.w+10, tankBounds.h+10, 20);
ctx.fill();
// 水体渐变
const waterGrad = ctx.createLinearGradient(tankBounds.x, tankBounds.y, tankBounds.x, tankBounds.y+tankBounds.h);
waterGrad.addColorStop(0, '#7ec8e0');
waterGrad.addColorStop(0.5, '#4ba0c0');
waterGrad.addColorStop(1, '#2e6f8f');
ctx.fillStyle = waterGrad;
ctx.beginPath();
ctx.roundRect(tankBounds.x, tankBounds.y, tankBounds.w, tankBounds.h, 18);
ctx.fill();
// 水纹高光
ctx.globalAlpha = 0.2;
for(let i=0;i<70;i++) {
ctx.beginPath();
ctx.arc(tankBounds.x + Math.random()*tankBounds.w, tankBounds.y + Math.random()*tankBounds.h, Math.random()*3+1,0,Math.PI*2);
ctx.fillStyle = '#FFFFFF';
ctx.fill();
}
ctx.globalAlpha = 1;
// 沙石底部
ctx.fillStyle = '#D2B48C';
ctx.beginPath();
ctx.rect(tankBounds.x, tankBounds.y+tankBounds.h-35, tankBounds.w, 38);
ctx.fill();
ctx.fillStyle = '#BC9A6C';
for(let i=0;i<120;i++) {
ctx.beginPath();
ctx.arc(tankBounds.x + Math.random()*tankBounds.w, tankBounds.y+tankBounds.h-25 + Math.random()*18, Math.random()*2+1,0,Math.PI*2);
ctx.fill();
}
// 水草点缀
ctx.fillStyle = '#3C9E3D';
for(let i=0;i<12;i++) {
ctx.beginPath();
ctx.moveTo(tankBounds.x+20+i*65, tankBounds.y+tankBounds.h-28);
ctx.lineTo(tankBounds.x+10+i*65, tankBounds.y+tankBounds.h-48);
ctx.lineTo(tankBounds.x+30+i*65, tankBounds.y+tankBounds.h-52);
ctx.fill();
}
}
function drawFish(f) {
ctx.save();
ctx.translate(f.x, f.y);
ctx.rotate(f.angle);
ctx.beginPath();
ctx.ellipse(0, 0, f.size, f.size*0.7, 0, 0, Math.PI*2);
ctx.fillStyle = f.bodyColor;
ctx.fill();
ctx.fillStyle = '#fff6d0';
ctx.beginPath();
ctx.arc(f.size*0.4, -f.size*0.2, f.size*0.2, 0, Math.PI*2);
ctx.fill();
ctx.fillStyle = '#000000';
ctx.beginPath();
ctx.arc(f.size*0.48, -f.size*0.22, f.size*0.07, 0, Math.PI*2);
ctx.fill();
ctx.beginPath();
ctx.moveTo(f.size*0.7, 0);
ctx.lineTo(f.size*1.1, -f.size*0.25);
ctx.lineTo(f.size*1.1, f.size*0.25);
ctx.fillStyle = '#e09d32';
ctx.fill();
ctx.restore();
}
// 绘制菱形鞭炮 (钻石风格)
function drawDiamondCracker(x, y, isExploded) {
if(isExploded) return;
ctx.save();
ctx.shadowBlur = 8;
ctx.shadowColor = "#ffaa33";
ctx.translate(x, y);
ctx.rotate(Math.PI/4);
const size = 12;
// 菱形主体
ctx.fillStyle = '#E34234';
ctx.beginPath();
ctx.rect(-size/1.8, -size/1.8, size, size);
ctx.fill();
ctx.fillStyle = '#FF6347';
ctx.beginPath();
ctx.rect(-size/2.5, -size/2.5, size-3, size-3);
ctx.fill();
// 金色镶边
ctx.strokeStyle = '#F7D44A';
ctx.lineWidth = 1.8;
ctx.strokeRect(-size/1.8, -size/1.8, size, size);
// 高光
ctx.fillStyle = '#FFF3BF';
ctx.beginPath();
ctx.rect(-size/3.5, -size/3.5, size/2.5, size/2.5);
ctx.fill();
ctx.restore();
// 小亮点
ctx.beginPath();
ctx.arc(x-2, y-2, 2, 0, Math.PI*2);
ctx.fillStyle = '#FFE484';
ctx.shadowBlur = 5;
ctx.fill();
}
function drawFirecrackersString() {
if(firecrackers.length === 0) return;
// 绘制连接绳子
ctx.beginPath();
ctx.strokeStyle = '#8B5A2B';
ctx.lineWidth = 2.5;
ctx.shadowBlur = 2;
for(let i=0; i<firecrackers.length-1; i++) {
if(!firecrackers[i].exploded || !firecrackers[i+1].exploded) {
ctx.beginPath();
ctx.moveTo(firecrackers[i].x, firecrackers[i].y-3);
ctx.lineTo(firecrackers[i+1].x, firecrackers[i+1].y-3);
ctx.stroke();
}
}
// 绘制顶部悬挂线 & 引线
if(firecrackers.length>0 && !firecrackers[0].exploded) {
const first = firecrackers[0];
ctx.beginPath();
ctx.moveTo(first.x, first.y-12);
ctx.lineTo(first.x-8, first.y-28);
ctx.lineTo(first.x+4, first.y-44);
ctx.strokeStyle = '#AA7C4A';
ctx.lineWidth = 2;
ctx.stroke();
// 引线火焰 (点燃之前如果 fuseSpark.active 未爆炸时显示)
if(fuseSpark.active && !isExplodingActive && explodedCount===0) {
ctx.beginPath();
ctx.arc(first.x-3, first.y-38, 5+Math.sin(Date.now()*0.015)*2, 0, Math.PI*2);
ctx.fillStyle = `hsl(30, 100%, 60%)`;
ctx.fill();
ctx.beginPath();
ctx.arc(first.x-3, first.y-38, 2+Math.sin(Date.now()*0.02)*1, 0, Math.PI*2);
ctx.fillStyle = `#FFD966`;
ctx.fill();
}
}
// 绘制每个未爆炸的菱形鞭炮
for(let c of firecrackers) {
if(!c.exploded) {
drawDiamondCracker(c.x, c.y, false);
}
}
ctx.shadowBlur = 0;
}
// 绘制钻石粒子 (所有爆炸碎片)
function drawParticles() {
for(let p of particles) {
ctx.save();
ctx.globalAlpha = Math.min(p.alpha, 0.9);
ctx.shadowBlur = 5;
ctx.shadowColor = "#ffaa66";
if(p.shape === 'diamond') {
ctx.translate(p.x, p.y);
ctx.rotate(Math.PI/4);
ctx.fillStyle = p.color;
ctx.fillRect(-p.size/2, -p.size/2, p.size, p.size);
ctx.fillStyle = 'gold';
ctx.fillRect(-p.size/3, -p.size/3, p.size/1.6, p.size/1.6);
} else {
ctx.beginPath();
ctx.arc(p.x, p.y, p.size/1.6, 0, Math.PI*2);
ctx.fillStyle = p.color;
ctx.fill();
}
ctx.restore();
}
}
// 绘制气泡
function drawBubbles() {
for(let b of bubbles) {
ctx.beginPath();
ctx.arc(b.x, b.y, b.radius, 0, Math.PI*2);
ctx.fillStyle = `rgba(255, 255, 245, ${b.alpha*0.6})`;
ctx.fill();
ctx.strokeStyle = `rgba(255,255,210,0.5)`;
ctx.stroke();
}
}
// 主动画循环
function animate() {
updateFishes();
updateBubbles();
updateParticles();
// 清空并重绘
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawAquariumBackground();
drawBubbles();
for(let fish of fishes) drawFish(fish);
drawFirecrackersString();
drawParticles();
// 额外: 如果鞭炮全部炸完且未重置 显示提示
if(explodedCount === totalCrackers && totalCrackers>0 && !isExplodingActive && !resetTimeoutId && autoResetEnabled) {
ctx.font = "bold 18m 'Segoe UI'";
ctx.fillStyle = "#FFF8E7";
ctx.shadowBlur = 6;
ctx.fillText("✨ 鞭炮绽放,新的一串即将出现 ✨", tankBounds.x+200, tankBounds.y+40);
}
requestAnimationFrame(animate);
}
// 初始化整个场景
function init() {
initFishes();
for(let i=0;i<25;i++) addBubble();
firecrackers = generateFirecrackers();
totalCrackers = firecrackers.length;
explodedCount = 0;
if(firecrackers.length) {
fuseSpark.active = true;
fuseSpark.x = firecrackers[0].x;
fuseSpark.y = firecrackers[0].y - 12;
}
autoResetEnabled = true;
// 自动启动点燃序列(首次入场)
setTimeout(() => {
if(!isExplodingActive && firecrackers.some(c=>!c.exploded)) {
startExplodeSequence();
}
}, 600);
// 绑定按钮事件
const btn = document.getElementById('igniteBtn');
btn.addEventListener('click', () => {
manualIgnite();
});
animate();
}
// 工具: canvas roundRect
if (!CanvasRenderingContext2D.prototype.roundRect) {
CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) {
if (w < 2 * r) r = w / 2;
if (h < 2 * r) r = h / 2;
this.moveTo(x+r, y);
this.lineTo(x+w-r, y);
this.quadraticCurveTo(x+w, y, x+w, y+r);
this.lineTo(x+w, y+h-r);
this.quadraticCurveTo(x+w, y+h, x+w-r, y+h);
this.lineTo(x+r, y+h);
this.quadraticCurveTo(x, y+h, x, y+h-r);
this.lineTo(x, y+r);
this.quadraticCurveTo(x, y, x+r, y);
return this;
};
}
init();
})();
</script>
</body>
</html>
随后测试了Qwen3.6PlusFree,geminipro,ChatGPT,MiMo-V2-Pro,GLM-5。具体效果各位自行判断。所有是测试除了Qwen3.6PlusFree官网没找到在OpenCode,其它的都是在官方的网页上测试的。
Qwen3.6PlusFree
geminipro
MiMo-V2-Pro
ChatGPT
GLM-5
--【壹】--:
代表谴责一下,为什么要在鱼缸里面放鞭炮!!!
--【贰】--:
这种前端代码,感觉基本都能搞定的,之前的版本你是测试写出来不对吗?
--【叁】--:
抓紧发布才是硬道理
--【肆】--:
要有对比的吧,万一之前就可以呢
--【伍】--:
期待ds的高光时刻
--【陆】--:
感觉这就是deepseek的v4版本,只是还在调整阶段,调整好了就可以发布了
--【柒】--:
不知道什么时候正式发布v4
--【捌】--:
赶紧发布吧,等了好久了
--【玖】--:
image1686×1403 598 KB
同样的提示词,我试了下,没一次就跑通
--【拾】--:
因为这个最直观,也有一定的代码能力,逻辑能力,视觉能力的验证
--【拾壹】--:
Ds工作的老同学说在灰度 这次会有很大提升 所以大家坐等官方发布吧
--【拾贰】--:
测试视觉效果的好多,有什么说法吗
--【拾叁】--:
感觉效果挺好的,看什么时候官宣了
--【拾肆】--:
这个月初能发吗
--【拾伍】--:
期待国产崛起
--【拾陆】--:
期待国产崛起
--【拾柒】--:
看起来效果不错啊
--【拾捌】--:
前两天就看到说在测试了
--【拾玖】--:
这阵子一直在传的 v4 lite?

