用HTML5 Canvas打造交互式心形粒子动画:从基础到优化实战
用HTML5 Canvas打造交互式心形粒子动画:从基础到优化实战
引言
在Web交互设计中,粒子动画因其动态美感和视觉吸引力被广泛应用于节日特效、情感化界面等场景。本文将通过实战案例,详细讲解如何使用HTML5 Canvas和JavaScript实现一个「心之律动」交互式粒子艺术效果,包含心形粒子循环动画、鼠标轨迹粒子、烟花爆炸及坠落效果,并分享关键优化技巧。
技术栈概览
- HTML5 Canvas:实现高性能粒子渲染
- JavaScript:粒子系统逻辑控制
- Tailwind CSS:快速构建UI界面
- Font Awesome:图标库支持
一、基础框架搭建
1. 画布初始化
<canvas id="canvas"></canvas>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');// 自适应屏幕尺寸
function resizeCanvas() {canvas.width = window.innerWidth;canvas.height = window.innerHeight;
}
window.addEventListener('resize', resizeCanvas);
2. UI界面设计
使用Tailwind CSS构建半透明控制栏和信息面板,实现响应式布局:
<div class="controls"><div class="control-btn" id="reset-btn"><i class="fa fa-refresh"></i></div><!-- 暂停/增加/减少按钮 -->
</div><div class="info-panel"><div>粒子数量: <span id="particle-count">0</span></div><p><i class="fa fa-mouse-pointer"></i> 鼠标移动生成轨迹</p>
</div>
二、核心粒子系统实现
1. 粒子类设计
定义Particle
类,通过type
属性区分不同粒子类型(心形/鼠标轨迹/烟花/坠落),实现多态行为:
class Particle {constructor(x, y, type) {this.x = x;this.y = y;this.type = type;// 根据类型初始化不同属性type === 'heart' ? this.setupHeartParticle() :type === 'mouse' ? this.setupMouseParticle() :type === 'firework' ? this.setupFireworkParticle() :this.setupFallingParticle();}// 心形粒子专属属性setupHeartParticle() {this.layer = Math.floor(Math.random() * 4); // 0-3层this.color = particleColors[this.layer][Math.floor(Math.random() * 3)];this.size = 2 + (8 - this.layer * 2) * Math.random();this.angle = Math.random() * 2 * Math.PI; // 随机方向this.life = 150 + 100 * Math.random() - this.layer * 30; // 分层寿命}// 更新粒子状态update() {// 心形粒子使用极坐标运动if (this.type === 'heart') {this.x += Math.cos(this.angle) * this.speed;this.y += Math.sin(this.angle) * this.speed;}// 烟花粒子使用笛卡尔坐标+物理模拟else if (this.type === 'firework') {this.vx *= this.friction; // 摩擦力this.vy += this.gravity; // 重力this.x += this.vx;this.y += this.vy;}// 生命周期管理this.life--;this.currentAlpha = this.life / this.maxLife;}
}
三、心形动画核心实现
1. 心形参数方程
使用经典心形参数方程生成粒子初始位置:
// 心形参数方程:x=16sin³t,y=13cost-5cos2t-2cos3t-cos4t
generateHeartPoint(t, scale) {const x = 16 * Math.pow(Math.sin(t), 3);const y = 13 * Math.cos(t) - 5 * Math.cos(2*t) - 2 * Math.cos(3*t) - Math.cos(4*t);// 映射到画布中心并缩放return {x: canvas.width/2 + x * (canvas.width*0.35*scale)/16,y: canvas.height/2 - y * (canvas.width*0.35*scale)/16};
}
2. 粒子循环再生机制
通过每帧检测心形粒子数量,动态补充消失的粒子,实现持续动画:
class HeartAnimation {constructor() {this.heartParticleCount = 1500; // 目标粒子数this.heartRegenRate = 5; // 每帧再生数量this.generateHeartParticles(this.heartParticleCount);}animate() {// 检测存活心形粒子数量let heartCount = this.particles.filter(p => p.type === 'heart').length;// 补充缺失粒子if (heartCount < this.heartParticleCount) {const toAdd = Math.min(this.heartRegenRate, this.heartParticleCount - heartCount);this.regenerateHeartParticles(toAdd);}requestAnimationFrame(this.animate.bind(this));}
}
四、烟花效果深度优化
1. 物理模拟增强
- 笛卡尔坐标系:使用
vx/vy
分量精确控制运动 - 重力系统:
this.gravity = 0.05
模拟自由落体 - 空气阻力:
this.friction = 0.97
实现速度衰减
setupFireworkParticle() {this.vx = Math.cos(this.angle) * this.baseSpeed;this.vy = Math.sin(this.angle) * this.baseSpeed;this.gravity = 0.05;this.friction = 0.97 + Math.random()*0.01;
}
2. 多阶段爆炸效果
通过延迟释放不同类型粒子,模拟真实烟花层次感:
createFirework(x, y) {// 主爆炸this.createFireworkWave(x, y, 180, 0);// 150ms后释放外围粒子setTimeout(() => {this.createFireworkWave(x, y, 120, 10, false, 1.5);}, 150);// 250ms后释放精细粒子setTimeout(() => {this.createFireworkWave(x, y, 150, 15, true);}, 250);
}
3. 坠落效果转换
当烟花粒子速度低于阈值时,转换为坠落粒子并添加风力效果:
if (this.type === 'firework' && Math.abs(this.vy) < 0.3) {this.type = 'falling';this.setupFallingParticle(); // 启用风力和更快下落
}
五、交互功能实现
1. 鼠标轨迹生成
通过高频次生成带随机偏移的粒子,形成连续轨迹:
handleMouseMove(e) {const now = Date.now();if (now - this.lastMouseMove > 15) {// 每次移动生成6个偏移粒子for (let i=0; i<6; i++) {this.particles.push(new Particle(e.clientX + (Math.random()-0.5)*20, e.clientY + (Math.random()-0.5)*20, 'mouse'));}this.lastMouseMove = now;}
}
2. 控制按钮逻辑
实现粒子数量调整、动画暂停和重置功能:
handleIncrease() {this.heartParticleCount += 300;this.generateHeartParticles(300); // 批量生成
}handleReset() {this.particles = []; // 清空所有粒子this.generateHeartParticles(this.heartParticleCount); // 重新生成心形
}
六、性能优化要点
- 粒子生命周期管理:及时移除死亡粒子,避免内存泄漏
for (let i=this.particles.length-1; i>=0; i--) {if (!this.particles[i].isAlive()) {this.particles.splice(i, 1); // 逆序删除避免索引错乱}
}
- 画布清理策略:使用
clearRect
而非全量重绘
ctx.clearRect(0, 0, canvas.width, canvas.height); // 只清除可见区域
- 分层渲染优化:将不同类型粒子分组管理,减少状态判断
效果展示
- 基础效果:中心悬浮动态心形,粒子随心跳效果呼吸缩放
- 交互效果:
- 鼠标移动生成彩色拖尾轨迹
- 点击屏幕触发多层烟花爆炸,伴随真实物理坠落
- 底部控制栏可调整粒子数量、暂停动画、重置场景
总结
通过HTML5 Canvas的高性能渲染能力,结合物理模拟和粒子系统设计,我们实现了一个兼具视觉美感和交互乐趣的心形动画。核心技术点包括:
- 基于参数方程的几何图形生成
- 多类型粒子的状态机设计
- 物理引擎(重力、摩擦力、风力)的实现
- 交互式粒子系统的性能优化
完整代码
<div class="overlay"><h1 class="title animate-pulse-slow">心之律动</h1><p class="subtitle">鼠标滑过留下痕迹,点击释放烟花</p>
</div><div class="controls"><div class="control-btn" id="reset-btn" title="重置"><i class="fa fa-refresh"></i></div><div class="control-btn" id="pause-btn" title="暂停/继续"><i class="fa fa-pause"></i></div><div class="control-btn" id="increase-btn" title="增加粒子"><i class="fa fa-plus"></i></div><div class="control-btn" id="decrease-btn" title="减少粒子"><i class="fa fa-minus"></i></div>
</div><div class="info-panel"><div class="particles-count"><span id="particle-count">粒子数量: 0</span></div><div class="instructions"><p><i class="fa fa-mouse-pointer heart-icon"></i> 鼠标移动: 留下粒子轨迹</p><p><i class="fa fa-hand-pointer-o heart-icon"></i> 点击: 释放烟花</p><p><i class="fa fa-refresh heart-icon"></i> 重置: 重新生成心形</p></div>
</div><script>// 初始化画布const canvas = document.getElementById('canvas');const ctx = canvas.getContext('2d');// 设置画布尺寸function resizeCanvas() {canvas.width = window.innerWidth;canvas.height = window.innerHeight;}resizeCanvas();window.addEventListener('resize', resizeCanvas);// 粒子颜色方案const particleColors = ['#FF5E87', '#FF85A2', '#FFB3C6', '#FFC2D1', '#FFD7E4','#FF9AA2', '#FFB7B2', '#FFDAC1', '#E2F0CB', '#B5EAD7', '#C7CEEA','#A79AFF', '#C2A7FF', '#D8A7FF', '#EAA7FF', '#F5A7FF'];// 粒子类class Particle {constructor(x, y, type = 'heart') {this.x = x;this.y = y;this.type = type; // 'heart', 'mouse', 'firework', 'falling'// 根据粒子类型设置不同属性if (type === 'heart') {this.setupHeartParticle();} else if (type === 'mouse') {this.setupMouseParticle();} else if (type === 'firework') {this.setupFireworkParticle();} else if (type === 'falling') {this.setupFallingParticle();}// 为烟花粒子添加延迟效果if (type === 'firework') {this.delay = Math.random() * 15; // 延迟发射时间this.isActive = false;}}setupHeartParticle() {// 粒子层次(0=外层,1=中层,2=内层,3=中心)this.layer = Math.floor(Math.random() * 4);// 根据层次确定颜色const colorPools = [particleColors.slice(0, 3), // 外层颜色 - 冷色particleColors.slice(3, 6), // 中层颜色 - 中色particleColors.slice(6, 9), // 内层颜色 - 暖色particleColors.slice(9) // 中心颜色 - 最暖色];this.color = colorPools[this.layer][Math.floor(Math.random() * colorPools[this.layer].length)];// 根据层次确定大小this.size = 2 + Math.random() * (8 - this.layer * 2);// 根据层次确定速度this.speed = 0.1 + Math.random() * 0.3 + this.layer * 0.05;// 随机方向this.angle = Math.random() * Math.PI * 2;// 粒子寿命this.life = 150 + Math.random() * 100 - this.layer * 30;this.maxLife = this.life;}setupMouseParticle() {// 鼠标轨迹粒子属性 - 延长寿命this.color = particleColors[Math.floor(Math.random() * particleColors.length)];this.size = 1 + Math.random() * 3;this.speed = 0.05 + Math.random() * 0.1; // 降低速度,延长轨迹this.angle = Math.random() * Math.PI * 2;this.life = 100 + Math.random() * 80; // 延长寿命this.maxLife = this.life;}setupFireworkParticle() {// 烟花粒子属性 - 更大范围this.color = particleColors[Math.floor(Math.random() * particleColors.length)];this.size = 1.5 + Math.random() * 4;this.baseSpeed = 2 + Math.random() * 3; // 更高初始速度,更大范围this.angle = Math.random() * Math.PI * 2;this.life = 100 + Math.random() * 80; // 延长烟花粒子寿命this.maxLife = this.life;this.gravity = 0.05; // 增加重力效果this.friction = 0.97 + Math.random() * 0.01; // 添加摩擦力// 使用笛卡尔坐标系统this.vx = Math.cos(this.angle) * this.baseSpeed;this.vy = Math.sin(this.angle) * this.baseSpeed;}setupFallingParticle() {// 坠落粒子属性this.color = particleColors[Math.floor(Math.random() * particleColors.length)];this.size = 0.5 + Math.random() * 2;this.speed = 0.5 + Math.random() * 1.5;// 确保角度主要向下(π到2π之间)this.angle = Math.PI + (Math.random() - 0.5) * Math.PI * 0.6; this.life = 80 + Math.random() * 120;this.maxLife = this.life;this.gravity = 0.03; // 增加重力效果this.wind = (Math.random() - 0.5) * 0.003; // 水平风力,减小偏移}update() {// 烟花粒子延迟激活if (this.type === 'firework' && !this.isActive) {this.delay--;if (this.delay <= 0) {this.isActive = true;}return;}// 更新位置 - 使用笛卡尔坐标系统if (this.type === 'firework' && this.isActive) {// 应用摩擦力this.vx *= this.friction;this.vy *= this.friction;// 应用重力this.vy += this.gravity;this.x += this.vx;this.y += this.vy;// 当烟花粒子速度足够慢时,转换为坠落粒子if (Math.abs(this.vy) > 0.3 && Math.random() < 0.08 && this.life > 40) {this.type = 'falling';this.setupFallingParticle();}} else {// 其他粒子使用极坐标系统this.x += Math.cos(this.angle) * this.speed;this.y += Math.sin(this.angle) * this.speed;}if (this.type === 'falling') {this.speed += this.gravity;this.x += this.wind;}// 更新寿命this.life--;// 心跳效果 - 改变粒子大小和不透明度const heartbeatPhase = (Date.now() / 800) % (Math.PI * 2);const heartbeatFactor = 1.0 + 0.15 * Math.sin(heartbeatPhase);// 对于心形粒子,使用更明显的心跳效果if (this.type === 'heart') {this.currentSize = this.size * heartbeatFactor * (this.life / this.maxLife);this.currentAlpha = (this.life / this.maxLife) * (0.8 + 0.2 * Math.sin(heartbeatPhase + this.layer * 0.5));} else {this.currentSize = this.size * (this.life / this.maxLife);this.currentAlpha = this.life / this.maxLife;}}draw() {// 延迟的烟花粒子不绘制if (this.type === 'firework' && !this.isActive) {return;}// 绘制粒子ctx.fillStyle = this.color;ctx.beginPath();ctx.arc(this.x, this.y, this.currentSize, 0, Math.PI * 2);ctx.closePath();ctx.globalAlpha = this.currentAlpha;ctx.fill();ctx.globalAlpha = 1;}isAlive() {return this.life > 0;}}// 心形粒子动画类class HeartAnimation {constructor() {this.particles = [];this.isPaused = false;this.mouse = { x: 0, y: 0, isDown: false };this.lastMouseMove = 0;this.particleCount = 0;this.heartParticleCount = 1500; // 心形粒子目标数量this.heartRegenRate = 5; // 每帧重新生成的心形粒子数量// 绑定事件处理函数this.handleMouseMove = this.handleMouseMove.bind(this);this.handleMouseDown = this.handleMouseDown.bind(this);this.handleMouseUp = this.handleMouseUp.bind(this);this.handleReset = this.handleReset.bind(this);this.handlePause = this.handlePause.bind(this);this.handleIncrease = this.handleIncrease.bind(this);this.handleDecrease = this.handleDecrease.bind(this);// 注册事件监听器window.addEventListener('mousemove', this.handleMouseMove);window.addEventListener('mousedown', this.handleMouseDown);window.addEventListener('mouseup', this.handleMouseUp);document.getElementById('reset-btn').addEventListener('click', this.handleReset);document.getElementById('pause-btn').addEventListener('click', this.handlePause);document.getElementById('increase-btn').addEventListener('click', this.handleIncrease);document.getElementById('decrease-btn').addEventListener('click', this.handleDecrease);// 生成初始心形粒子this.generateHeartParticles(this.heartParticleCount);// 开始动画循环this.animate();}// 判断点是否在心形内部isInsideHeart(x, y, scale = 1) {// 归一化坐标const centerX = canvas.width / 2;const centerY = canvas.height / 2;const nx = (x - centerX) / (canvas.width / 2);const ny = (y - centerY) / (canvas.height / 2);// 心形方程: (x² + y² - 1)³ - x²y³ ≤ 0const heartEq = Math.pow(nx*nx + ny*ny - 1, 3) - nx*nx*ny*ny*ny;return heartEq <= 0;}// 生成心形参数方程的点generateHeartPoint(t, scale = 1) {// 心形参数方程const x = 16 * Math.pow(Math.sin(t), 3);const y = 13 * Math.cos(t) - 5 * Math.cos(2*t) - 2 * Math.cos(3*t) - Math.cos(4*t);// 归一化并缩放const centerX = canvas.width / 2;const centerY = canvas.height / 2;const heartScale = Math.min(canvas.width, canvas.height) * 0.35 * scale;return {x: centerX + x * heartScale / 16,y: centerY - y * heartScale / 16 // 注意y轴是向下的};}// 生成心形粒子generateHeartParticles(count) {for (let i = 0; i < count; i++) {// 随机选择层次const layer = Math.floor(Math.random() * 4);// 根据层次确定缩放比例let scale;if (layer === 0) scale = 1.0; // 外层else if (layer === 1) scale = 0.95; // 中层else if (layer === 2) scale = 0.85; // 内层else scale = 0.7; // 中心层// 生成随机角度const t = Math.random() * Math.PI * 2;// 计算粒子位置const point = this.generateHeartPoint(t, scale);// 对于中心层,添加一些随机偏移,使其填充更均匀if (layer === 3) {const offset = Math.random() * 0.2 * (canvas.width / 2);point.x += (Math.random() - 0.5) * offset;point.y += (Math.random() - 0.5) * offset;// 确保点仍然在心形内部if (!this.isInsideHeart(point.x, point.y)) {continue;}}// 创建粒子this.particles.push(new Particle(point.x, point.y, 'heart'));}this.updateParticleCount();}// 重新生成心形粒子regenerateHeartParticles(count) {for (let i = 0; i < count; i++) {// 随机选择层次const layer = Math.floor(Math.random() * 4);// 根据层次确定缩放比例let scale;if (layer === 0) scale = 1.0; // 外层else if (layer === 1) scale = 0.95; // 中层else if (layer === 2) scale = 0.85; // 内层else scale = 0.7; // 中心层// 生成随机角度const t = Math.random() * Math.PI * 2;// 计算粒子位置const point = this.generateHeartPoint(t, scale);// 对于中心层,添加一些随机偏移,使其填充更均匀if (layer === 3) {const offset = Math.random() * 0.2 * (canvas.width / 2);point.x += (Math.random() - 0.5) * offset;point.y += (Math.random() - 0.5) * offset;// 确保点仍然在心形内部if (!this.isInsideHeart(point.x, point.y)) {continue;}}// 创建粒子this.particles.push(new Particle(point.x, point.y, 'heart'));}}// 鼠标移动处理handleMouseMove(e) {this.mouse.x = e.clientX;this.mouse.y = e.clientY;// 限制鼠标轨迹粒子生成频率const now = Date.now();if (now - this.lastMouseMove > 15) { // 增加生成频率// 创建更多鼠标轨迹粒子,形成更连续的轨迹for (let i = 0; i < 6; i++) {const offsetX = (Math.random() - 0.5) * 20; // 更大的偏移范围const offsetY = (Math.random() - 0.5) * 20;this.particles.push(new Particle(this.mouse.x + offsetX, this.mouse.y + offsetY, 'mouse'));}this.lastMouseMove = now;this.updateParticleCount();}}// 鼠标按下处理handleMouseDown() {this.mouse.isDown = true;this.createFirework(this.mouse.x, this.mouse.y);}// 鼠标释放处理handleMouseUp() {this.mouse.isDown = false;}// 创建烟花效果createFirework(x, y) {// 主烟花爆炸 - 分阶段释放粒子this.createFireworkWave(x, y, 180, 0);// 外围烟花 - 延迟释放,更大范围setTimeout(() => {if (this.isPaused) return;this.createFireworkWave(x, y, 120, 10, false, 1.5);}, 150);// 精细粒子 - 延迟释放,更精细的粒子setTimeout(() => {if (this.isPaused) return;this.createFireworkWave(x, y, 150, 15, true);}, 250);this.updateParticleCount();}// 创建一波烟花粒子createFireworkWave(x, y, count, baseDelay, fineParticles = false, speedMultiplier = 1) {for (let i = 0; i < count; i++) {const p = new Particle(x, y, 'firework');// 更精细的粒子if (fineParticles) {p.size = 0.5 + Math.random() * 1.5;p.baseSpeed = 1.5 + Math.random() * 2;p.life = 80 + Math.random() * 60;} else {p.size = 1 + Math.random() * 3;p.baseSpeed = (2 + Math.random() * 3) * speedMultiplier;}p.delay = baseDelay + Math.random() * 10;p.vx = Math.cos(p.angle) * p.baseSpeed;p.vy = Math.sin(p.angle) * p.baseSpeed;this.particles.push(p);}}// 重置动画handleReset() {// 清空现有粒子this.particles = [];// 生成新的心形粒子this.generateHeartParticles(this.heartParticleCount);}// 暂停/继续动画handlePause() {this.isPaused = !this.isPaused;const pauseBtn = document.getElementById('pause-btn');pauseBtn.innerHTML = this.isPaused ? '<i class="fa fa-play"></i>' : '<i class="fa fa-pause"></i>';}// 增加粒子数量handleIncrease() {this.heartParticleCount += 300;this.generateHeartParticles(300);}// 减少粒子数量handleDecrease() {// 保留最近添加的300个粒子if (this.heartParticleCount > 300) {this.heartParticleCount -= 300;// 移除部分粒子let removed = 0;for (let i = this.particles.length - 1; i >= 0; i--) {if (this.particles[i].type === 'heart') {this.particles.splice(i, 1);removed++;if (removed >= 300) break;}}this.updateParticleCount();}}// 更新粒子数量显示updateParticleCount() {this.particleCount = this.particles.length;document.getElementById('particle-count').textContent = `粒子数量: ${this.particleCount}`;}// 动画循环animate() {if (!this.isPaused) {// 清除画布ctx.clearRect(0, 0, canvas.width, canvas.height);// 计算当前心形粒子数量let heartParticles = 0;// 更新和绘制所有粒子for (let i = this.particles.length - 1; i >= 0; i--) {const particle = this.particles[i];if (particle.type === 'heart') {heartParticles++;}particle.update();if (particle.isAlive()) {particle.draw();} else {// 移除死亡的粒子this.particles.splice(i, 1);}}// 补充心形粒子if (heartParticles < this.heartParticleCount) {const toGenerate = Math.min(this.heartRegenRate,this.heartParticleCount - heartParticles);this.regenerateHeartParticles(toGenerate);}// 更新粒子数量显示if (this.particleCount !== this.particles.length) {this.updateParticleCount();}}// 继续动画循环requestAnimationFrame(this.animate.bind(this));}}// 初始化动画const animation = new HeartAnimation();
</script>
相关文章:

用HTML5 Canvas打造交互式心形粒子动画:从基础到优化实战
用HTML5 Canvas打造交互式心形粒子动画:从基础到优化实战 引言 在Web交互设计中,粒子动画因其动态美感和视觉吸引力被广泛应用于节日特效、情感化界面等场景。本文将通过实战案例,详细讲解如何使用HTML5 Canvas和JavaScript实现一个「心之律…...
Gartner《How to Create and Maintain a Knowledge Base forHumans and AI》学习报告
核心观点 本研究是一份 Gartne关于如何创建和维护面向人类与人工智能(AI)的知识库的研究报告。报告强调了知识库在知识管理(KM)中的核心地位,尤其是在生成式人工智能(GenAI)时代,一个结构良好的知识库是知识管理成功的关键,反之则可能成为整个知识管理实践的失败点。…...

【软件工具】批量OCR指定区域图片自动识别内容重命名软件使用教程及注意事项
批量OCR指定区域图片自动识别内容重命名软件使用教程及注意事项 1、操作步骤1-5: 安装与启动:安装成功后,在桌面或开始菜单找到软件图标,双击启动。 导入图片:进入软件主界面,点击 “导入图片” 按钮&a…...
PyTorch 中cumprod函数计算张量沿指定维度的累积乘积详解和代码示例
torch.cumprod 是 PyTorch 中用于 计算张量沿指定维度的累积乘积(cumulative product) 的函数。 1、函数原型 torch.cumprod(input, dim, *, dtypeNone, outNone) → Tensor参数说明: 参数说明input输入张量dim累积乘积的维度dtype可选&…...
docker镜像下载到本地,并导入服务器
应用场景 : 本地环境可以连接外网,但服务器连接不了外网,直接用docker pull 命令执行拉起镜像报异常。 1.本地拉取xuxueli/xxl-job-admin:2.2.0及查看所有下载的镜像 docker pull xuxueli/xxl-job-admin:2.2.0 docker images 2.保存镜像到…...

数据通信与计算机网络——数字传输
主要内容 数字到数字转换 线路编码 线路编码方案 块编码 扰动 模拟到数字转换 脉冲码调制(PCM) Delta调制(DM) 传输模式 并行传输 串行传输 一、数字到数字转换 将数字数据转换为数字信号涉及三种技术: 线…...
oracle 归档日志与RECOVERY_FILE_DEST 视图
1. RECOVERY_FILE_DEST 视图的作用 RECOVERY_FILE_DEST 是 Oracle 数据库用于 管理快速恢复区(Fast Recovery Area, FRA) 的一个视图。FRA 是 Oracle 提供的一种集中存储恢复相关文件(如归档日志、备份文件、闪回日志等)的区域。…...

黄柏基因组-小檗碱生物合成的趋同进化-文献精读142
Convergent evolution of berberine biosynthesis 小檗碱生物合成的趋同进化 摘要 小檗碱是一种有效的抗菌和抗糖尿病生物碱,主要从不同植物谱系中提取,特别是从小檗属(毛茛目,早期分支的真双子叶植物)和黄柏属&…...

前端杂货铺——TodoList
个人简介 👀个人主页: 前端杂货铺 🙋♂️学习方向: 主攻前端方向,正逐渐往全干发展 📃个人状态: 研发工程师,现效力于中国工业软件事业 🚀人生格言: 积跬步…...

Spring Boot SSE流式输出+AI消息持久化升级实践:从粗暴到优雅的跃迁
在 AI 应用落地过程中,我们常常需要将用户和 AI 的对话以“完整上下文”的形式持久化到数据库中。但当 AI 回复非常长,甚至接近上万字时,传统的单条消息保存机制就会出问题。 在本篇文章中,我将深入讲解一次实际项目中对 对话持久…...
camera功能真的那么难用吗
背景 Android开发工作过程中,经常需要用到camera相关能力,比如:人脸识别,ai识别,拍照预览,摄像头录制等等需求。都需要使用到camera,且需要拿到camera的预览数据。但是每次开发这块代码都比较繁…...

Model Context Protocol (MCP) 是一个前沿框架
微软发布了 Model Context Protocol (MCP) 课程:mcp-for-beginners。 Model Context Protocol (MCP) 是一个前沿框架,涵盖 C#、Java、JavaScript、TypeScript 和 Python 等主流编程语言,规范 AI 模型与客户端应用之间的交互。 MCP 课程结构 …...
SQL Server 日期时间类型全解析:从精确存储到灵活转换
SQL Server 日期时间类型全解析:从精确存储到灵活转换 一、引言:日期时间处理的核心挑战 在数据管理中,日期时间类型是最常用却最容易出错的数据类型之一。不同业务场景对时间精度、时区感知、存储效率的需求差异极大: 金融交易…...
Android Test3 获取的ANDROID_ID值不同
Android Test3 获取的ANDROID_ID值不同 这篇文章来说明上一篇文章中说到的一个现象:在同一个项目中,创建不同的 app module,运行同一段测试代码,获取到的 ANDROID_ID 的值不同。 我也是第一次认真研究这个现象,这个还…...
[蓝桥杯 2024 国 B] 立定跳远
问题描述 在运动会上,小明从数轴的原点开始向正方向立定跳远。项目设置了 n 个检查点 a1,a2,...,an且 ai≥ai−1>0。小明必须先后跳跃到每个检查点上且只能跳跃到检查点上。同时,小明可以自行再增加 m 个检查点让自己跳得更轻松。在运动会前…...

内容力重塑品牌增长:开源AI大模型驱动下的智能名片与S2B2C商城赋能抖音生态种草范式
摘要:内容力已成为抖音生态中品牌差异化竞争的核心能力,通过有价值、强共鸣的内容实现产品"种草"与转化闭环。本文基于"开源AI大模型AI智能名片S2B2C商城小程序源码"技术架构,提出"技术赋能内容"的新型种草范式…...

手机号在网状态查询接口如何用PHP实现调用?
一、什么是手机号在网状态查询接口 通过精准探测手机号的状态,帮助平台减少此类问题的发生,提供更个性化的服务或进行地域性营销 二、应用场景 1. 金融风控 通过运营商在网态查询接口,金融机构可以核验贷款申请人的手机状态,拦…...

【Java微服务组件】分布式协调P4-一文打通Redisson:从API实战到分布式锁核心源码剖析
欢迎来到啾啾的博客🐱。 记录学习点滴。分享工作思考和实用技巧,偶尔也分享一些杂谈💬。 有很多很多不足的地方,欢迎评论交流,感谢您的阅读和评论😄。 目录 引言Redisson基本信息Redisson网站 Redisson应用…...

一个简单的德劳内三角剖分实现
德劳内(Delaunay)三角剖分是一种经典的将点集进行三角网格化预处理的手段,在NavMesh、随机地牢生成等场景下都有应用。 具体内容百度一大堆,就不介绍了。 比较知名的算法是Bowyer-Watson算法,也就是逐点插入法。 下雨闲…...
Python入门手册:异常处理
在编程过程中,异常处理是一个非常重要的环节。它可以帮助我们处理程序运行时可能出现的错误和异常情况,确保程序的稳定性和可靠性。Python提供了强大的异常处理机制,使得我们能够优雅地处理各种异常情况。今天,就让我们一起深入学…...

C#子线程更新主线程UI及委托回调使用示例
1.声明线程方法 2.线程中传入对象 3.声明委托与使用 声明委托对象 委托作为参数传入方法 4.在线程中传入委托 5.调用传入的委托...

使用VuePress2.X构建个人知识博客,并且用个人域名部署到GitHub Pages中
使用VuePress2.X构建个人知识博客,并且用个人域名部署到GitHub Pages中 什么是VuePress VuePress 是一个以 Markdown 为中心的静态网站生成器。你可以使用 Markdown 来书写内容(如文档、博客等),然后 VuePress 会帮助你生成一个…...

手写Promise.all
前言 之前在看远方os大佬直播的时候看到有让手写的Promise.all的问题,然后心血来潮自己准备手写一个 开始 首先,我们需要明确原本js提供的Promise.all的特性 Promise.all返回的是一个Promise如果传入的数据中有一个reject即整个all返回的就是reject&…...
调试器基本原理
调试器基本原理 前言 调试器(debugger),是一种用于控制其他程序执行流程、监控和修改其他程序状态的软件工具。 调试器通过实时分析程序的执行状态,协助开发者定位代码错误、了解程序工作原理、性能调优及逆向工程等。 1. 调试器核心功能 1.1 控制程…...

2025年6月|注意力机制|面向精度与推理速度提升的YOLOv8模型结构优化研究:融合ACmix的自研改进方案
版本: 8.3.143(Ultralytics YOLOv8框架) ACmix模块原理 在目标检测任务中,小目标(如裂缝、瑕疵、零件边缘等)由于其尺寸较小、纹理信息稀疏,通常更容易受到图像中复杂背景或噪声的干扰,从而导致漏检或误检…...
JAVA开发代码小工具集合
目录 前言编号生成工具EasyExcel 工具断言工具HTTP 工具字符串 工具验证码生成工具Excel 工具Class 工具Enum 工具分页工具断言工具2IP 地址工具Map 工具 前言 这些工具都是日常开发中能用到的,前后端都有,觉得好用就拿过来了… 编号生成工具 import j…...

利用qcustomplot绘制曲线图
本文详细介绍了qcustomplot绘制曲线图的流程,一段代码一段代码运行看效果。通过阅读本文,读者可以了解到每一项怎么用代码进行配置,进而实现自己想要的图表效果。(本文只针对曲线图) 1 最简单的图形(入门&…...

【基础算法】枚举(普通枚举、二进制枚举)
文章目录 一、普通枚举1. 铺地毯(1) 解题思路(2) 代码实现 2. 回文日期(1) 解题思路思路一:暴力枚举思路二:枚举年份思路三:枚举月日 (2) 代码实现 3. 扫雷(2) 解题思路(2) 代码实现 二、二进制枚举1. 子集(1) 解题思路(2) 代码实现 2. 费解的…...

智能对联网页小程序的仓颉之旅
#传统楹联遇上AI智能体:我的Cangjie Magic开发纪实 引言:一场跨越千年的数字对话 "云对雨,雪对风,晚照对晴空"。昨天晚上星空璀璨,当我用仓颉语言写下第一个智能对联网页小程序的Agent DSL代码时࿰…...
Go字符串切片操作详解:str1[:index]
在Go语言中,return str1[:index] 是一个字符串切片操作,它截取字符串的一部分。让我们深入解析这个操作的含义和原理: 基本语法和含义 str1:原始字符串[:index]:切片操作符str1[:index]: 起始…...