下面我来详细讲解一下 “前端canvas中物体边框和控制点的实现示例” 的完整攻略。
简介
在前端开发中,我们可能需要在canvas中绘制一些图形或者物体,同时需要提供控制点以方便用户进行交互。此时,我们就需要实现物体边框和控制点,使得用户可以通过拖动控制点来对物体进行移动、旋转、缩放等操作。本文将详细介绍这个过程的实现。
实现步骤
- 绘制物体
在canvas中绘制物体的代码,可以使用路径(Path)的方式进行,具体步骤如下:
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
// ...
ctx.closePath();
ctx.fill(); // 填充物体内部颜色
- 绘制边框
在绘制物体的基础上,我们需要绘制物体的边框,以便于用户进行交互。在canvas中,我们可以通过绘制一个矩形作为边框,也可以通过路径的方式绘制出物体的轮廓。
对于路径的方式,我们可以通过以下的代码实现:
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
// ...
ctx.closePath();
ctx.stroke(); // 绘制路径线条
- 绘制控制点
控制点是用户进行交互的关键。在canvas中,我们可以绘制一个圆形作为控制点,然后在用户鼠标移动到该圆形范围内时,修改鼠标的样式以方便提示用户进行交互。
绘制圆形的代码如下:
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill(); // 填充圆形内部颜色
- 实现交互
用户在鼠标移动到控制点的范围内时,我们需要修改鼠标样式以提示用户可以进行交互。
canvas.style.cursor = 'move'; // 修改鼠标样式为移动
用户按下鼠标时,我们需要记录下鼠标的位置和物体的位置,以便于之后的移动操作。
在鼠标拖拽的过程中,我们需要计算出鼠标的偏移量,然后对物体进行相应的移动、缩放、旋转等操作。
- 示例说明
下面提供两个示例来说明以上实现步骤:
示例1:绘制矩形,并实现拖动和缩放
下面是实现代码:
// 绘制矩形
ctx.fillStyle = '#ff0000';
ctx.fillRect(50, 50, 100, 100);
// 绘制边框
ctx.strokeStyle = '#0000ff';
ctx.lineWidth = 1;
ctx.strokeRect(50, 50, 100, 100);
// 绘制控制点
const radius = 5;
ctx.fillStyle = '#0000ff';
ctx.beginPath();
ctx.arc(50, 50, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(100, 50, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(100, 100, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(50, 100, radius, 0, Math.PI * 2);
ctx.fill();
// 添加交互事件
canvas.addEventListener('mousedown', handleMouseDown);
canvas.addEventListener('mousemove', handleMouseMove);
canvas.addEventListener('mouseup', handleMouseUp);
// 实现拖动和缩放
let isDragging = false;
let isResizing = false;
let offsetX = 0;
let offsetY = 0;
let offsetWidth = 0;
let offsetHeight = 0;
function handleMouseDown(e) {
const x = e.pageX - canvas.offsetLeft;
const y = e.pageY - canvas.offsetTop;
if (isPointInCircle(x, y, 50, 50, radius)) {
isDragging = true;
offsetX = x - 50;
offsetY = y - 50;
} else if (isPointInCircle(x, y, 100, 50, radius)) {
isResizing = true;
offsetX = x - 100;
offsetY = y - 50;
offsetWidth = 100 - 50;
offsetHeight = 100 - 50;
} else if (isPointInCircle(x, y, 100, 100, radius)) {
isResizing = true;
offsetX = x - 100;
offsetY = y - 100;
offsetWidth = 100 - 50;
offsetHeight = 100 - 50;
} else if (isPointInCircle(x, y, 50, 100, radius)) {
isResizing = true;
offsetX = x - 50;
offsetY = y - 100;
offsetWidth = 100 - 50;
offsetHeight = 100 - 50;
} else {
isDragging = false;
isResizing = false;
}
}
function handleMouseMove(e) {
const x = e.pageX - canvas.offsetLeft;
const y = e.pageY - canvas.offsetTop;
if (isDragging) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#ff0000';
ctx.fillRect(x - offsetX, y - offsetY, 100, 100);
ctx.strokeStyle = '#0000ff';
ctx.lineWidth = 1;
ctx.strokeRect(x - offsetX, y - offsetY, 100, 100);
ctx.fillStyle = '#0000ff';
ctx.beginPath();
ctx.arc(x - offsetX, y - offsetY, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(x - offsetX + 100, y - offsetY, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(x - offsetX + 100, y - offsetY + 100, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(x - offsetX, y - offsetY + 100, radius, 0, Math.PI * 2);
ctx.fill();
} else if (isResizing) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#ff0000';
ctx.fillRect(50, 50, x - offsetX - 50 + offsetWidth, y - offsetY - 50 + offsetHeight);
ctx.strokeStyle = '#0000ff';
ctx.lineWidth = 1;
ctx.strokeRect(50, 50, x - offsetX - 50 + offsetWidth, y - offsetY - 50 + offsetHeight);
ctx.fillStyle = '#0000ff';
ctx.beginPath();
ctx.arc(50, 50, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(x - offsetX + offsetWidth, 50, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(x - offsetX + offsetWidth, y - offsetY + offsetHeight, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(50, y - offsetY + offsetHeight, radius, 0, Math.PI * 2);
ctx.fill();
} else {
if (isPointInCircle(x, y, 50, 50, radius) || isPointInCircle(x, y, 100, 50, radius) || isPointInCircle(x, y, 100, 100, radius) || isPointInCircle(x, y, 50, 100, radius)) {
canvas.style.cursor = 'pointer';
} else {
canvas.style.cursor = 'default';
}
}
}
function handleMouseUp(e) {
isDragging = false;
isResizing = false;
}
function isPointInCircle(x, y, cx, cy, r) {
const dx = x - cx;
const dy = y - cy;
const d = Math.sqrt(dx * dx + dy * dy);
return d <= r;
}
在示例1中,我们实现了一个矩形的拖动和缩放操作。用户可以拖动矩形中的任意一个点进行移动,也可以拖动边框的任意一个角点进行缩放。
示例2:绘制三角形,并实现旋转和缩放
下面是实现代码:
// 绘制三角形
ctx.fillStyle = '#ff0000';
ctx.beginPath();
ctx.moveTo(100, 50);
ctx.lineTo(50, 150);
ctx.lineTo(150, 150);
ctx.closePath();
ctx.fill();
// 绘制边框
ctx.strokeStyle = '#0000ff';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(100, 50);
ctx.lineTo(50, 150);
ctx.lineTo(150, 150);
ctx.closePath();
ctx.stroke();
// 绘制控制点
ctx.fillStyle = '#0000ff';
ctx.beginPath();
ctx.arc(100, 50, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(50, 150, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(150, 150, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(100, 150, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(75, 100, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(125, 100, radius, 0, Math.PI * 2);
ctx.fill();
// 添加交互事件
canvas.addEventListener('mousedown', handleMouseDown);
canvas.addEventListener('mousemove', handleMouseMove);
canvas.addEventListener('mouseup', handleMouseUp);
// 实现旋转和缩放
let isRotating = false;
let isScaling = false;
let centerX = 100;
let centerY = 100;
let startAngle = 0;
let startScaleX = 1;
let startScaleY = 1;
function handleMouseDown(e) {
const x = e.pageX - canvas.offsetLeft;
const y = e.pageY - canvas.offsetTop;
if (isPointInCircle(x, y, 100, 50, radius)) {
isRotating = true;
centerX = 100;
centerY = 100;
startAngle = Math.atan2(y - centerY, x - centerX);
} else if (isPointInCircle(x, y, 50, 150, radius)) {
isScaling = true;
centerX = 100;
centerY = 100;
startScaleX = 150 - 100;
startScaleY = 150 - 50;
} else if (isPointInCircle(x, y, 150, 150, radius)) {
isScaling = true;
centerX = 100;
centerY = 100;
startScaleX = 50 - 100;
startScaleY = 150 - 50;
} else if (isPointInCircle(x, y, 100, 150, radius)) {
isScaling = true;
centerX = 100;
centerY = 100;
startScaleX = 1;
startScaleY = Math.sqrt(Math.pow(startScaleX, 2) - Math.pow(50 - 100, 2));
} else if (isPointInCircle(x, y, 75, 100, radius)) {
isScaling = true;
centerX = 100;
centerY = 100;
startScaleX = Math.sqrt(Math.pow(100 - 75, 2) + Math.pow(100 - 50, 2));
startScaleY = Math.sqrt(Math.pow(100 - 125, 2) + Math.pow(100 - 50, 2));
} else if (isPointInCircle(x, y, 125, 100, radius)) {
isScaling = true;
centerX = 100;
centerY = 100;
startScaleX = Math.sqrt(Math.pow(100 - 125, 2) + Math.pow(100 - 50, 2));
startScaleY = Math.sqrt(Math.pow(100 - 75, 2) + Math.pow(100 - 50, 2));
} else {
isRotating = false;
isScaling = false;
}
}
function handleMouseMove(e) {
const x = e.pageX - canvas.offsetLeft;
const y = e.pageY - canvas.offsetTop;
if (isRotating) {
const angle = Math.atan2(y - centerY, x - centerX);
const deltaAngle = angle - startAngle;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.translate(centerX, centerY);
ctx.rotate(deltaAngle);
ctx.translate(-centerX, -centerY);
ctx.fillStyle = '#ff0000';
ctx.beginPath();
ctx.moveTo(100, 50);
ctx.lineTo(50, 150);
ctx.lineTo(150, 150);
ctx.closePath();
ctx.fill();
ctx.strokeStyle = '#0000ff';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(100, 50);
ctx.lineTo(50, 150);
ctx.lineTo(150, 150);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = '#0000ff';
ctx.beginPath();
ctx.arc(100, 50, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(50, 150, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(150, 150, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(100, 150, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(75, 100, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(125, 100, radius, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
} else if (isScaling) {
const scaleX = Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2)) / Math.sqrt(Math.pow(startScaleX, 2) + Math.pow(startScaleY, 2));
const scaleY = scaleX;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.translate(centerX, centerY);
ctx.scale(scaleX, scaleY);
ctx.translate(-centerX, -centerY);
ctx.fillStyle = '#ff0000';
ctx.beginPath();
ctx.moveTo(100, 50);
ctx.lineTo(50, 150);
ctx.lineTo(150, 150);
ctx.closePath();
ctx.fill();
ctx.strokeStyle = '#0000ff';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(100, 50);
ctx.lineTo(50, 150);
ctx.lineTo(150, 150);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = '#0000ff';
ctx.beginPath();
ctx.arc(100, 50, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(50, 150, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(150, 150, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(100, 150, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(75, 100, radius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(125, 100, radius, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
} else {
const dx = x - centerX;
const dy = y - centerY;
const d = Math.sqrt(dx * dx + dy * dy);
if (d <= radius) {
canvas.style.cursor = 'pointer';
} else {
canvas.style.cursor = 'default';
}
}
}
function handleMouseUp(e) {
isRotating = false;
isScaling = false;
}
function isPointInCircle(x, y, cx, cy, r) {
const dx = x - cx;
const dy = y - cy;
const d = Math.sqrt(dx * dx + dy * dy);
return d <= r;
}
在示例2中,我们实现了一个三角形的旋转和缩放操作。用户可以拖动三角形中的任意一个点进行缩放,也
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:前端canvas中物体边框和控制点的实现示例 - Python技术站