Canvas Design by yldgio/vibe-vscode
npx skills add https://github.com/yldgio/vibe-vscode --skill 'Canvas Design'此技能帮助您使用 HTML5 Canvas API 和现代 JavaScript 模式创建交互式图形、数据可视化和动画。
在以下情况下使用此技能:
Canvas API 提供了一种通过 JavaScript 绘制图形的方式。它在以下方面非常强大:
<!DOCTYPE html>
<html>
<head>
<style>
canvas {
border: 1px solid #000;
display: block;
margin: 20px auto;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="800" height="600"></canvas>
<script src="canvas.js"></script>
</body>
</html>
// canvas.js
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 将 canvas 设置为填充窗口
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// 处理调整大小
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
draw(); // 重绘内容
});
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
// 矩形
ctx.fillStyle = '#3b82f6';
ctx.fillRect(50, 50, 200, 100);
// 描边矩形
ctx.strokeStyle = '#1e40af';
ctx.lineWidth = 3;
ctx.strokeRect(50, 200, 200, 100);
// 圆形
ctx.beginPath();
ctx.arc(350, 100, 50, 0, Math.PI * 2);
ctx.fillStyle = '#ef4444';
ctx.fill();
// 圆角矩形
function roundRect(x, y, width, height, radius) {
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
ctx.lineTo(x + width, y + height - radius);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
ctx.lineTo(x + radius, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.closePath();
ctx.fill();
}
// 贝塞尔曲线
ctx.beginPath();
ctx.moveTo(50, 400);
ctx.bezierCurveTo(150, 300, 250, 500, 350, 400);
ctx.strokeStyle = '#8b5cf6';
ctx.lineWidth = 3;
ctx.stroke();
// 二次曲线
ctx.beginPath();
ctx.moveTo(400, 400);
ctx.quadraticCurveTo(500, 300, 600, 400);
ctx.stroke();
class Animation {
constructor(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.lastTime = 0;
this.animate = this.animate.bind(this);
}
animate(currentTime) {
// 计算增量时间
const deltaTime = currentTime - this.lastTime;
this.lastTime = currentTime;
// 清除画布
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// 更新和绘制
this.update(deltaTime);
this.draw();
// 继续动画
requestAnimationFrame(this.animate);
}
update(deltaTime) {
// 在此处更新游戏逻辑
}
draw() {
// 在此处绘制帧
}
start() {
requestAnimationFrame(this.animate);
}
}
// 用法
const animation = new Animation(canvas);
animation.start();
class Particle {
constructor(x, y) {
this.x = x;
this.y = y;
this.vx = (Math.random() - 0.5) * 4;
this.vy = (Math.random() - 0.5) * 4;
this.radius = Math.random() * 3 + 1;
this.life = 1.0;
this.decay = Math.random() * 0.01 + 0.005;
}
update() {
this.x += this.vx;
this.y += this.vy;
this.life -= this.decay;
return this.life > 0;
}
draw(ctx) {
ctx.save();
ctx.globalAlpha = this.life;
ctx.fillStyle = `hsl(${this.life * 60}, 100%, 50%)`;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
}
class ParticleSystem {
constructor() {
this.particles = [];
}
emit(x, y, count = 10) {
for (let i = 0; i < count; i++) {
this.particles.push(new Particle(x, y));
}
}
update() {
this.particles = this.particles.filter(p => p.update());
}
draw(ctx) {
this.particles.forEach(p => p.draw(ctx));
}
}
class InteractiveCanvas {
constructor(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.mouse = { x: 0, y: 0, down: false };
this.setupEventListeners();
}
setupEventListeners() {
this.canvas.addEventListener('mousemove', (e) => {
const rect = this.canvas.getBoundingClientRect();
this.mouse.x = e.clientX - rect.left;
this.mouse.y = e.clientY - rect.top;
});
this.canvas.addEventListener('mousedown', () => {
this.mouse.down = true;
});
this.canvas.addEventListener('mouseup', () => {
this.mouse.down = false;
});
// 触摸支持
this.canvas.addEventListener('touchmove', (e) => {
e.preventDefault();
const rect = this.canvas.getBoundingClientRect();
const touch = e.touches[0];
this.mouse.x = touch.clientX - rect.left;
this.mouse.y = touch.clientY - rect.top;
});
}
isPointInCircle(px, py, cx, cy, radius) {
const dx = px - cx;
const dy = py - cy;
return dx * dx + dy * dy <= radius * radius;
}
}
class BarChart {
constructor(canvas, data) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.data = data;
this.padding = 40;
}
draw() {
const { ctx, canvas, data, padding } = this;
// 清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 计算尺寸
const chartWidth = canvas.width - padding * 2;
const chartHeight = canvas.height - padding * 2;
const barWidth = chartWidth / data.length;
const maxValue = Math.max(...data.map(d => d.value));
// 绘制坐标轴
ctx.strokeStyle = '#333';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(padding, padding);
ctx.lineTo(padding, canvas.height - padding);
ctx.lineTo(canvas.width - padding, canvas.height - padding);
ctx.stroke();
// 绘制柱条
data.forEach((item, index) => {
const barHeight = (item.value / maxValue) * chartHeight;
const x = padding + index * barWidth;
const y = canvas.height - padding - barHeight;
// 柱条
ctx.fillStyle = `hsl(${index * 30}, 70%, 50%)`;
ctx.fillRect(x + 5, y, barWidth - 10, barHeight);
// 标签
ctx.fillStyle = '#333';
ctx.font = '12px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(item.label, x + barWidth / 2, canvas.height - padding + 20);
// 数值
ctx.fillText(item.value.toString(), x + barWidth / 2, y - 5);
});
}
}
// 用法
const data = [
{ label: 'Jan', value: 65 },
{ label: 'Feb', value: 59 },
{ label: 'Mar', value: 80 },
{ label: 'Apr', value: 81 },
{ label: 'May', value: 56 }
];
const chart = new BarChart(canvas, data);
chart.draw();
class LineChart {
constructor(canvas, dataPoints) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.dataPoints = dataPoints;
}
draw() {
const { ctx, canvas, dataPoints } = this;
const padding = 40;
const chartWidth = canvas.width - padding * 2;
const chartHeight = canvas.height - padding * 2;
const maxValue = Math.max(...dataPoints);
const minValue = Math.min(...dataPoints);
const range = maxValue - minValue;
// 绘制线条
ctx.strokeStyle = '#3b82f6';
ctx.lineWidth = 2;
ctx.beginPath();
dataPoints.forEach((value, index) => {
const x = padding + (index / (dataPoints.length - 1)) * chartWidth;
const y = canvas.height - padding - ((value - minValue) / range) * chartHeight;
if (index === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
// 绘制点
ctx.fillStyle = '#1e40af';
dataPoints.forEach((value, index) => {
const x = padding + (index / (dataPoints.length - 1)) * chartWidth;
const y = canvas.height - padding - ((value - minValue) / range) * chartHeight;
ctx.beginPath();
ctx.arc(x, y, 4, 0, Math.PI * 2);
ctx.fill();
});
}
}
// 线性渐变
const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
gradient.addColorStop(0, '#3b82f6');
gradient.addColorStop(1, '#8b5cf6');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 径向渐变
const radial = ctx.createRadialGradient(
canvas.width / 2, canvas.height / 2, 50,
canvas.width / 2, canvas.height / 2, 200
);
radial.addColorStop(0, 'rgba(59, 130, 246, 1)');
radial.addColorStop(1, 'rgba(59, 130, 246, 0)');
ctx.fillStyle = radial;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 从图像创建图案
const img = new Image();
img.onload = () => {
const pattern = ctx.createPattern(img, 'repeat');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, canvas.width, canvas.height);
};
img.src = 'texture.png';
// 保存状态
ctx.save();
// 变换
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(Math.PI / 4);
ctx.scale(1.5, 1.5);
// 绘制
ctx.fillRect(-50, -50, 100, 100);
// 恢复状态
ctx.restore();
// 为静态元素创建离屏 canvas
const bufferCanvas = document.createElement('canvas');
bufferCanvas.width = canvas.width;
bufferCanvas.height = canvas.height;
const bufferCtx = bufferCanvas.getContext('2d');
// 一次性绘制静态内容
drawStaticBackground(bufferCtx);
// 在动画循环中,复制缓冲区
function draw() {
ctx.drawImage(bufferCanvas, 0, 0);
// 绘制动态元素
}
let lastRender = 0;
const fps = 60;
const fpsInterval = 1000 / fps;
function animate(currentTime) {
requestAnimationFrame(animate);
const elapsed = currentTime - lastRender;
if (elapsed > fpsInterval) {
lastRender = currentTime - (elapsed % fpsInterval);
render();
}
}
prefers-reduced-motionclass CanvasApp {
constructor(canvasId) {
this.canvas = document.getElementById(canvasId);
this.ctx = this.canvas.getContext('2d');
this.running = false;
this.resize();
window.addEventListener('resize', () => this.resize());
}
resize() {
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
}
update(deltaTime) {
// 更新逻辑
}
draw() {
// 清除
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// 绘制
}
loop(currentTime) {
if (!this.running) return;
const deltaTime = currentTime - (this.lastTime || currentTime);
this.lastTime = currentTime;
this.update(deltaTime);
this.draw();
requestAnimationFrame((t) => this.loop(t));
}
start() {
this.running = true;
requestAnimationFrame((t) => this.loop(t));
}
stop() {
this.running = false;
}
}
每周安装次数
–
代码仓库
GitHub 星标数
8
首次出现时间
–
安全审计
This skill helps you create interactive graphics, data visualizations, and animations using HTML5 Canvas API with modern JavaScript patterns.
Use this skill when you need to:
The Canvas API provides a way to draw graphics via JavaScript. It's powerful for:
<!DOCTYPE html>
<html>
<head>
<style>
canvas {
border: 1px solid #000;
display: block;
margin: 20px auto;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="800" height="600"></canvas>
<script src="canvas.js"></script>
</body>
</html>
// canvas.js
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// Set canvas to fill window
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// Handle resize
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
draw(); // Redraw content
});
// Rectangle
ctx.fillStyle = '#3b82f6';
ctx.fillRect(50, 50, 200, 100);
// Stroke rectangle
ctx.strokeStyle = '#1e40af';
ctx.lineWidth = 3;
ctx.strokeRect(50, 200, 200, 100);
// Circle
ctx.beginPath();
ctx.arc(350, 100, 50, 0, Math.PI * 2);
ctx.fillStyle = '#ef4444';
ctx.fill();
// Rounded rectangle
function roundRect(x, y, width, height, radius) {
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
ctx.lineTo(x + width, y + height - radius);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
ctx.lineTo(x + radius, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.closePath();
ctx.fill();
}
// Bezier curves
ctx.beginPath();
ctx.moveTo(50, 400);
ctx.bezierCurveTo(150, 300, 250, 500, 350, 400);
ctx.strokeStyle = '#8b5cf6';
ctx.lineWidth = 3;
ctx.stroke();
// Quadratic curve
ctx.beginPath();
ctx.moveTo(400, 400);
ctx.quadraticCurveTo(500, 300, 600, 400);
ctx.stroke();
class Animation {
constructor(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.lastTime = 0;
this.animate = this.animate.bind(this);
}
animate(currentTime) {
// Calculate delta time
const deltaTime = currentTime - this.lastTime;
this.lastTime = currentTime;
// Clear canvas
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// Update and draw
this.update(deltaTime);
this.draw();
// Continue animation
requestAnimationFrame(this.animate);
}
update(deltaTime) {
// Update game logic here
}
draw() {
// Draw frame here
}
start() {
requestAnimationFrame(this.animate);
}
}
// Usage
const animation = new Animation(canvas);
animation.start();
class Particle {
constructor(x, y) {
this.x = x;
this.y = y;
this.vx = (Math.random() - 0.5) * 4;
this.vy = (Math.random() - 0.5) * 4;
this.radius = Math.random() * 3 + 1;
this.life = 1.0;
this.decay = Math.random() * 0.01 + 0.005;
}
update() {
this.x += this.vx;
this.y += this.vy;
this.life -= this.decay;
return this.life > 0;
}
draw(ctx) {
ctx.save();
ctx.globalAlpha = this.life;
ctx.fillStyle = `hsl(${this.life * 60}, 100%, 50%)`;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
}
class ParticleSystem {
constructor() {
this.particles = [];
}
emit(x, y, count = 10) {
for (let i = 0; i < count; i++) {
this.particles.push(new Particle(x, y));
}
}
update() {
this.particles = this.particles.filter(p => p.update());
}
draw(ctx) {
this.particles.forEach(p => p.draw(ctx));
}
}
class InteractiveCanvas {
constructor(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.mouse = { x: 0, y: 0, down: false };
this.setupEventListeners();
}
setupEventListeners() {
this.canvas.addEventListener('mousemove', (e) => {
const rect = this.canvas.getBoundingClientRect();
this.mouse.x = e.clientX - rect.left;
this.mouse.y = e.clientY - rect.top;
});
this.canvas.addEventListener('mousedown', () => {
this.mouse.down = true;
});
this.canvas.addEventListener('mouseup', () => {
this.mouse.down = false;
});
// Touch support
this.canvas.addEventListener('touchmove', (e) => {
e.preventDefault();
const rect = this.canvas.getBoundingClientRect();
const touch = e.touches[0];
this.mouse.x = touch.clientX - rect.left;
this.mouse.y = touch.clientY - rect.top;
});
}
isPointInCircle(px, py, cx, cy, radius) {
const dx = px - cx;
const dy = py - cy;
return dx * dx + dy * dy <= radius * radius;
}
}
class BarChart {
constructor(canvas, data) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.data = data;
this.padding = 40;
}
draw() {
const { ctx, canvas, data, padding } = this;
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Calculate dimensions
const chartWidth = canvas.width - padding * 2;
const chartHeight = canvas.height - padding * 2;
const barWidth = chartWidth / data.length;
const maxValue = Math.max(...data.map(d => d.value));
// Draw axes
ctx.strokeStyle = '#333';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(padding, padding);
ctx.lineTo(padding, canvas.height - padding);
ctx.lineTo(canvas.width - padding, canvas.height - padding);
ctx.stroke();
// Draw bars
data.forEach((item, index) => {
const barHeight = (item.value / maxValue) * chartHeight;
const x = padding + index * barWidth;
const y = canvas.height - padding - barHeight;
// Bar
ctx.fillStyle = `hsl(${index * 30}, 70%, 50%)`;
ctx.fillRect(x + 5, y, barWidth - 10, barHeight);
// Label
ctx.fillStyle = '#333';
ctx.font = '12px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(item.label, x + barWidth / 2, canvas.height - padding + 20);
// Value
ctx.fillText(item.value.toString(), x + barWidth / 2, y - 5);
});
}
}
// Usage
const data = [
{ label: 'Jan', value: 65 },
{ label: 'Feb', value: 59 },
{ label: 'Mar', value: 80 },
{ label: 'Apr', value: 81 },
{ label: 'May', value: 56 }
];
const chart = new BarChart(canvas, data);
chart.draw();
class LineChart {
constructor(canvas, dataPoints) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.dataPoints = dataPoints;
}
draw() {
const { ctx, canvas, dataPoints } = this;
const padding = 40;
const chartWidth = canvas.width - padding * 2;
const chartHeight = canvas.height - padding * 2;
const maxValue = Math.max(...dataPoints);
const minValue = Math.min(...dataPoints);
const range = maxValue - minValue;
// Draw line
ctx.strokeStyle = '#3b82f6';
ctx.lineWidth = 2;
ctx.beginPath();
dataPoints.forEach((value, index) => {
const x = padding + (index / (dataPoints.length - 1)) * chartWidth;
const y = canvas.height - padding - ((value - minValue) / range) * chartHeight;
if (index === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
// Draw points
ctx.fillStyle = '#1e40af';
dataPoints.forEach((value, index) => {
const x = padding + (index / (dataPoints.length - 1)) * chartWidth;
const y = canvas.height - padding - ((value - minValue) / range) * chartHeight;
ctx.beginPath();
ctx.arc(x, y, 4, 0, Math.PI * 2);
ctx.fill();
});
}
}
// Linear gradient
const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
gradient.addColorStop(0, '#3b82f6');
gradient.addColorStop(1, '#8b5cf6');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Radial gradient
const radial = ctx.createRadialGradient(
canvas.width / 2, canvas.height / 2, 50,
canvas.width / 2, canvas.height / 2, 200
);
radial.addColorStop(0, 'rgba(59, 130, 246, 1)');
radial.addColorStop(1, 'rgba(59, 130, 246, 0)');
ctx.fillStyle = radial;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Create pattern from image
const img = new Image();
img.onload = () => {
const pattern = ctx.createPattern(img, 'repeat');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, canvas.width, canvas.height);
};
img.src = 'texture.png';
// Save state
ctx.save();
// Transform
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(Math.PI / 4);
ctx.scale(1.5, 1.5);
// Draw
ctx.fillRect(-50, -50, 100, 100);
// Restore state
ctx.restore();
// Create off-screen canvas for static elements
const bufferCanvas = document.createElement('canvas');
bufferCanvas.width = canvas.width;
bufferCanvas.height = canvas.height;
const bufferCtx = bufferCanvas.getContext('2d');
// Draw static content once
drawStaticBackground(bufferCtx);
// In animation loop, copy buffer
function draw() {
ctx.drawImage(bufferCanvas, 0, 0);
// Draw dynamic elements
}
let lastRender = 0;
const fps = 60;
const fpsInterval = 1000 / fps;
function animate(currentTime) {
requestAnimationFrame(animate);
const elapsed = currentTime - lastRender;
if (elapsed > fpsInterval) {
lastRender = currentTime - (elapsed % fpsInterval);
render();
}
}
prefers-reduced-motionclass CanvasApp {
constructor(canvasId) {
this.canvas = document.getElementById(canvasId);
this.ctx = this.canvas.getContext('2d');
this.running = false;
this.resize();
window.addEventListener('resize', () => this.resize());
}
resize() {
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
}
update(deltaTime) {
// Update logic
}
draw() {
// Clear
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// Draw
}
loop(currentTime) {
if (!this.running) return;
const deltaTime = currentTime - (this.lastTime || currentTime);
this.lastTime = currentTime;
this.update(deltaTime);
this.draw();
requestAnimationFrame((t) => this.loop(t));
}
start() {
this.running = true;
requestAnimationFrame((t) => this.loop(t));
}
stop() {
this.running = false;
}
}
Weekly Installs
–
Repository
GitHub Stars
8
First Seen
–
Security Audits
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
109,600 周安装