<style>
.bezier-container { position: relative; width: 100%; }
canvas { background: #1e293b; border-radius: 8px; cursor: crosshair; }
.info { margin-top: 12px; font-size: 13px; color: #94a3b8; }
.info code { background: #334155; padding: 2px 6px; border-radius: 4px; color: #38bdf8; }
.legend { display: flex; gap: 16px; margin-top: 8px; flex-wrap: wrap; }
.legend-item { display: flex; align-items: center; gap: 6px; font-size: 12px; }
.dot { width: 10px; height: 10px; border-radius: 50%; }
</style>
<div class="bezier-container">
<canvas id="bezierCanvas" width="500" height="280"></canvas>
<div class="legend">
<div class="legend-item"><div class="dot" style="background:#ef4444"></div> 起点 P0</div>
<div class="legend-item"><div class="dot" style="background:#22c55e"></div> 控制点 P1</div>
<div class="legend-item"><div class="dot" style="background:#22c55e"></div> 控制点 P2</div>
<div class="legend-item"><div class="dot" style="background:#3b82f6"></div> 终点 P3</div>
</div>
<div class="info">
<strong>💡 拖拽绿色控制点</strong> 修改曲线形状 |
CSS 缓动: <code id="cssCode">cubic-bezier(0.4, 0.8, 0.6, 0.2)</code>
</div>
</div>
<script>
const canvas = document.getElementById('bezierCanvas');
const ctx = canvas.getContext('2d');
const cssCodeEl = document.getElementById('cssCode');
// 控制点(三次贝塞尔:4个点)
let points = [
{ x: 40, y: 220, color: '#ef4444', label: 'P0', draggable: false },
{ x: 120, y: 60, color: '#22c55e', label: 'P1', draggable: true },
{ x: 380, y: 60, color: '#22c55e', label: 'P2', draggable: true },
{ x: 460, y: 220, color: '#3b82f6', label: 'P3', draggable: false }
];
let dragging = null;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制控制线
ctx.strokeStyle = '#475569';
ctx.lineWidth = 1;
ctx.setLineDash([5, 5]);
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
ctx.lineTo(points[1].x, points[1].y);
ctx.lineTo(points[2].x, points[2].y);
ctx.lineTo(points[3].x, points[3].y);
ctx.stroke();
ctx.setLineDash([]);
// 绘制贝塞尔曲线
ctx.strokeStyle = '#8b5cf6';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
ctx.bezierCurveTo(
points[1].x, points[1].y,
points[2].x, points[2].y,
points[3].x, points[3].y
);
ctx.stroke();
// 绘制控制点
points.forEach((p, i) => {
ctx.beginPath();
ctx.arc(p.x, p.y, p.draggable ? 10 : 8, 0, Math.PI * 2);
ctx.fillStyle = p.color;
ctx.fill();
ctx.fillStyle = '#fff';
ctx.font = '10px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(p.label, p.x, p.y + 24);
});
// 更新 CSS cubic-bezier 值
const x1 = ((points[1].x - 40) / 420).toFixed(2);
const y1 = ((220 - points[1].y) / 160).toFixed(2);
const x2 = ((points[2].x - 40) / 420).toFixed(2);
const y2 = ((220 - points[2].y) / 160).toFixed(2);
cssCodeEl.textContent = 'cubic-bezier(' + x1 + ', ' + y1 + ', ' + x2 + ', ' + y2 + ')';
}
canvas.addEventListener('mousedown', e => {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
points.forEach((p, i) => {
if (p.draggable && Math.hypot(x - p.x, y - p.y) < 15) {
dragging = i;
}
});
});
canvas.addEventListener('mousemove', e => {
if (dragging !== null) {
const rect = canvas.getBoundingClientRect();
points[dragging].x = Math.max(20, Math.min(480, e.clientX - rect.left));
points[dragging].y = Math.max(20, Math.min(260, e.clientY - rect.top));
draw();
}
});
canvas.addEventListener('mouseup', () => dragging = null);
canvas.addEventListener('mouseleave', () => dragging = null);
draw();
</script>