前端Canvas:让你的网站更具视觉冲击力

前端Canvas:让你的网站更具视觉冲击力

毒舌时刻

前端Canvas?这不是游戏开发才用的吗?

"Canvas性能差,我不用"——结果错过了丰富的视觉效果,
"Canvas太复杂了,我学不会"——结果只能用静态图片,
"我用CSS就够了,要Canvas干嘛"——结果无法实现复杂的动画效果。

醒醒吧,Canvas不是游戏开发的专利,前端也可以用它来创建丰富的视觉效果!

为什么你需要这个?

  • 丰富的视觉效果:创建动态图形、动画和游戏
  • 高性能:直接操作像素,性能优异
  • 交互性:支持鼠标、触摸等交互
  • 数据可视化:绘制图表、仪表盘等
  • 跨平台:在所有现代浏览器中运行

反面教材

// 反面教材:简单的Canvas绘制 function drawCircle() { const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); // 绘制一个简单的圆形 ctx.beginPath(); ctx.arc(100, 100, 50, 0, Math.PI * 2); ctx.fillStyle = 'red'; ctx.fill(); ctx.stroke(); } // 反面教材:没有优化的动画 function animate() { const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); let x = 0; setInterval(() => { // 每次都清除整个画布 ctx.clearRect(0, 0, canvas.width, canvas.height); // 绘制移动的矩形 ctx.fillStyle = 'blue'; ctx.fillRect(x, 50, 50, 50); x += 1; if (x > canvas.width) { x = 0; } }, 16); } 

正确的做法

// 正确的做法:Canvas基础绘制 function basicDrawing() { const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); // 设置画布尺寸 canvas.width = 800; canvas.height = 600; // 绘制矩形 ctx.fillStyle = 'blue'; ctx.fillRect(50, 50, 100, 100); // 绘制圆形 ctx.beginPath(); ctx.arc(250, 100, 50, 0, Math.PI * 2); ctx.fillStyle = 'red'; ctx.fill(); // 绘制路径 ctx.beginPath(); ctx.moveTo(350, 50); ctx.lineTo(450, 150); ctx.lineTo(350, 150); ctx.closePath(); ctx.strokeStyle = 'green'; ctx.lineWidth = 2; ctx.stroke(); // 绘制文本 ctx.font = '24px Arial'; ctx.fillStyle = 'black'; ctx.textAlign = 'center'; ctx.fillText('Canvas绘制示例', canvas.width / 2, 30); } // 正确的做法:Canvas动画 function canvasAnimation() { const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); canvas.width = 800; canvas.height = 600; let x = 0; let y = canvas.height / 2; let dx = 2; let dy = 2; const radius = 20; function animate() { // 清除画布 ctx.clearRect(0, 0, canvas.width, canvas.height); // 绘制移动的圆形 ctx.beginPath(); ctx.arc(x, y, radius, 0, Math.PI * 2); ctx.fillStyle = 'purple'; ctx.fill(); // 边界检测 if (x + radius > canvas.width || x - radius < 0) { dx = -dx; } if (y + radius > canvas.height || y - radius < 0) { dy = -dy; } // 更新位置 x += dx; y += dy; // 使用requestAnimationFrame requestAnimationFrame(animate); } animate(); } // 正确的做法:Canvas交互 function canvasInteraction() { const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); canvas.width = 800; canvas.height = 600; let isDrawing = false; let lastX = 0; let lastY = 0; // 鼠标按下事件 canvas.addEventListener('mousedown', (e) => { isDrawing = true; [lastX, lastY] = [e.offsetX, e.offsetY]; }); // 鼠标移动事件 canvas.addEventListener('mousemove', (e) => { if (!isDrawing) return; ctx.beginPath(); ctx.moveTo(lastX, lastY); ctx.lineTo(e.offsetX, e.offsetY); ctx.strokeStyle = 'black'; ctx.lineWidth = 2; ctx.stroke(); [lastX, lastY] = [e.offsetX, e.offsetY]; }); // 鼠标释放事件 canvas.addEventListener('mouseup', () => { isDrawing = false; }); // 鼠标离开事件 canvas.addEventListener('mouseout', () => { isDrawing = false; }); } // 正确的做法:Canvas数据可视化 function canvasChart() { const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); canvas.width = 800; canvas.height = 600; // 数据 const data = [12, 19, 3, 5, 2, 3, 7, 11, 15, 18, 10, 6]; const labels = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']; // 绘制坐标系 const padding = 50; const chartWidth = canvas.width - padding * 2; const chartHeight = canvas.height - padding * 2; // X轴 ctx.beginPath(); ctx.moveTo(padding, canvas.height - padding); ctx.lineTo(canvas.width - padding, canvas.height - padding); ctx.stroke(); // Y轴 ctx.beginPath(); ctx.moveTo(padding, padding); ctx.lineTo(padding, canvas.height - padding); ctx.stroke(); // 绘制数据 const maxValue = Math.max(...data); const barWidth = chartWidth / data.length; data.forEach((value, index) => { const barHeight = (value / maxValue) * chartHeight; const x = padding + index * barWidth; const y = canvas.height - padding - barHeight; // 绘制柱子 ctx.fillStyle = 'rgba(54, 162, 235, 0.6)'; ctx.fillRect(x + 5, y, barWidth - 10, barHeight); // 绘制标签 ctx.font = '12px Arial'; ctx.fillStyle = 'black'; ctx.textAlign = 'center'; ctx.fillText(labels[index], x + barWidth / 2, canvas.height - padding + 15); }); // 绘制标题 ctx.font = '20px Arial'; ctx.fillStyle = 'black'; ctx.textAlign = 'center'; ctx.fillText('月度销售数据', canvas.width / 2, padding / 2); } // 正确的做法:Canvas图像处理 function canvasImageProcessing() { const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); canvas.width = 800; canvas.height = 600; // 加载图像 const img = new Image(); img.crossOrigin = 'anonymous'; img.onload = () => { // 绘制原始图像 ctx.drawImage(img, 0, 0, 400, 300); // 获取图像数据 const imageData = ctx.getImageData(0, 0, 400, 300); const data = imageData.data; // 处理图像(灰度) for (let i = 0; i < data.length; i += 4) { const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; const gray = (r + g + b) / 3; data[i] = gray; data[i + 1] = gray; data[i + 2] = gray; } // 绘制处理后的图像 ctx.putImageData(imageData, 400, 0); }; img.src = 'https://picsum.photos/800/600'; } 

毒舌点评

看看,这才叫前端Canvas!不是简单地绘制几个图形,而是创建丰富的视觉效果、动画和交互。

记住,Canvas是一个强大的工具,它可以让你创建从简单的图表到复杂的游戏等各种视觉效果。合理使用Canvas,可以让你的网站更具视觉冲击力。

所以,别再觉得Canvas复杂了,它是前端视觉效果的重要工具!

总结

  • 基础绘制:矩形、圆形、路径、文本等
  • 动画效果:使用requestAnimationFrame实现流畅动画
  • 交互操作:鼠标、触摸等事件处理
  • 数据可视化:绘制图表、仪表盘等
  • 图像处理:像素级操作,实现滤镜效果
  • 性能优化:合理使用缓存、避免频繁重绘
  • 响应式:根据容器大小调整画布尺寸
  • 工具库:使用Chart.js、Fabric.js等库简化开发

前端Canvas,让你的网站更具视觉冲击力!

Read more

把 AI 小助手接入企业微信:用一个回调接口做群聊机器人实战篇

你也许已经有了一个「看起来还挺像样」的 AI 小助手服务,比如: * 有 HTTP 接口 /v1/chat; * 能识别不同 Skill(待办、日报、FAQ 等); * 甚至已经有网页版前端。 但现实是:同事们每天真正打开的是企业微信,很少会专门去打开一个新网页跟机器人聊天。 这篇文章就做一件很实用的小事: 在不动你现有 AI 服务核心逻辑的前提下, 用一个企业微信“回调接口”, 把它变成「群聊里的 @ 机器人」。 一、整体思路:后端不重写,只加一层「翻译器」 假设你现在的 AI 服务长这样: * 接口:POST /v1/chat 返回: { "answer": "上午开会,下午写代码……"

基于动态三维环境下的Q-Learning算法无人机自主避障路径规划研究(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭:行百里者,半于九十。 📋📋📋本文内容如下:🎁🎁🎁  ⛳️赠与读者 👨‍💻做科研,涉及到一个深在的思想系统,需要科研者逻辑缜密,踏实认真,但是不能只是努力,很多时候借力比努力更重要,然后还要有仰望星空的创新点和启发点。建议读者按目录次序逐一浏览,免得骤然跌入幽暗的迷宫找不到来时的路,它不足为你揭示全部问题的答案,但若能解答你胸中升起的一朵朵疑云,也未尝不会酿成晚霞斑斓的别一番景致,万一它给你带来了一场精神世界的苦雨,那就借机洗刷一下原来存放在那儿的“躺平”上的尘埃吧。      或许,雨过云收,神驰的天地更清朗.......🔎🔎🔎 💥第一部分——内容介绍 基于 Q-learning 的三维无人机动态避障导航方法研究 摘要 针对低空复杂三维环境下无人机自主飞行的安全与路径优化问题,本文提出一种基于 Q-learning 强化学习的无人机导航与避障方法。该方法在离散化

OpenClaw多智能体路由实战:飞书多机器人配置指南

文章目录 * 飞书重新安装问题 * 批量增加机器人 * 缺点 * 多个飞书机器人名称包含大小写的问题 * 多个Agent名称包含大小写的问题 目前我已经完成了OpenClaw的基本安装,但是在对话框只有一个,机器人也只绑定到主会话,一次只能处理一个消息。很多时候我在聊天窗口,说A任务,然后做了一半,又发了关于B任务的指令。一是每次发完消息,如果OpenClaw还在处理,剩下的消息要么进入队列、要么看不到(实际还在队列)。两个任务切来切去,感觉体验很不好。 要彻底解决这个问题,实现网上演示的那种对各Agent、每个对话机器人对应一个Agent,就需要用到多智能体路由技术。 实现的步骤如下: * 在飞书创建一个新的机器人 * 通过控制台创建新的智能体 * 按照指引将飞书配置上去 * 根据需要创建多个Agent和机器人,并对应配置上去(略) 飞书重新安装问题 明明我已经安装好了飞书,系统还是会提示我安装,否则就跳过了添加飞书这步。应该是系统Bug。这次安装的飞书位置在~/.openclaw/extensions/feishu,其实和~/.npm-globa

阿里云的moltbot机器人使用钉钉的Stream流式接入

注意 1. 这个不需要工作流 2. 这个不需要开放外网 具体方法: 1.check代码https://github.com/DingTalk-Real-AI/dingtalk-moltbot-connector 2.package.json增加如下代码 "moltbot": { "extensions": ["./plugin.ts"], "channels": ["dingtalk-connector"], "installDependencies": true } 3.安装插件 moltbot plugins install dingtalk-moltbot-connector 4.增加钉钉配置~/.moltbot/moltbot.json;如果有了进行提花 { "channels"