前端canvas中物体边框和控制点的实现示例

下面我来详细讲解一下 “前端canvas中物体边框和控制点的实现示例” 的完整攻略。

简介

在前端开发中,我们可能需要在canvas中绘制一些图形或者物体,同时需要提供控制点以方便用户进行交互。此时,我们就需要实现物体边框和控制点,使得用户可以通过拖动控制点来对物体进行移动、旋转、缩放等操作。本文将详细介绍这个过程的实现。

实现步骤

  1. 绘制物体

在canvas中绘制物体的代码,可以使用路径(Path)的方式进行,具体步骤如下:

ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
// ...
ctx.closePath();
ctx.fill(); // 填充物体内部颜色
  1. 绘制边框

在绘制物体的基础上,我们需要绘制物体的边框,以便于用户进行交互。在canvas中,我们可以通过绘制一个矩形作为边框,也可以通过路径的方式绘制出物体的轮廓。

对于路径的方式,我们可以通过以下的代码实现:

ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
// ...
ctx.closePath();
ctx.stroke(); // 绘制路径线条
  1. 绘制控制点

控制点是用户进行交互的关键。在canvas中,我们可以绘制一个圆形作为控制点,然后在用户鼠标移动到该圆形范围内时,修改鼠标的样式以方便提示用户进行交互。

绘制圆形的代码如下:

ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill(); // 填充圆形内部颜色
  1. 实现交互

用户在鼠标移动到控制点的范围内时,我们需要修改鼠标样式以提示用户可以进行交互。

canvas.style.cursor = 'move'; // 修改鼠标样式为移动

用户按下鼠标时,我们需要记录下鼠标的位置和物体的位置,以便于之后的移动操作。

在鼠标拖拽的过程中,我们需要计算出鼠标的偏移量,然后对物体进行相应的移动、缩放、旋转等操作。

  1. 示例说明

下面提供两个示例来说明以上实现步骤:

示例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技术站

(0)
上一篇 2023年6月10日
下一篇 2023年6月10日

相关文章

  • 浏览器中的data类型的Url格式 data:image/png,data:image/jpeg!

    浏览器中的data类型的URL格式是一种特殊的URL格式,可以将数据编码为URL格式的字符串直接在页面中展示或引用。这种格式的URL以”data:”开头,后面跟着用逗号分隔的MIME类型和数据。 MIME类型 MIME类型是每个data类型URL中的必需项,它指定了数据的类型。MIME类型常见的有image/png、image/jpeg、text/plain…

    css 2023年6月10日
    00
  • Postman如何设置黑色背景?Postman设置背景教程

    Postman是一款强大的API测试工具,它能够帮助开发人员、测试人员、系统架构师和其他负责API建模和管理的人员快速、简便地管理API。Postman支持多种主题,并且用户可以根据自己的偏好来自定义主题色。如果你希望将Postman的背景颜色改为黑色,可以按照以下步骤操作。 安装Postman在开始设置Postman主题前,必须先下载安装Postman。P…

    css 2023年6月10日
    00
  • margin-top塌陷问题的现象与解决的具体方法

    关于“margin-top塌陷问题”的详细讲解和解决方法如下: 什么是margin-top塌陷问题? 在网页设计中,我们常常使用margin来控制元素之间的间距。但是在某些情况下,上一个元素的margin-top值却会被下一个元素的margin-top值所代替,这样就会造成上一个元素的margin-top值消失,这个现象被称为margin-top塌陷问题。 …

    css 2023年6月10日
    00
  • JS实现图片局部放大或缩小的方法

    当我们需要展示一张图片,常常需要提供局部放大或缩小的功能,以便用户能够更好地查看细节。下面是实现JavaScript图片局部放大或缩小的方法: 实现方法 实现图片局部放大或缩小的方法有多种,以下是两种示例: 示例1:鼠标悬停局部放大 首先需要在HTML中定义一个图片元素,并设置一个容器元素将其包裹起来。 接下来,我们需要通过JavaScript获取图片和容器…

    css 2023年6月10日
    00
  • 暗黑3第三赛季什么时候结束 s3持续时间介绍

    暗黑3第三赛季什么时候结束?s3持续时间介绍 暗黑3第三赛季开始于2021年4月9日,结束于2021年7月18日。因此,第三赛季持续时间为约3个月。 第三赛季简介 第三赛季的主要特点是提高了目标物品掉落的机会和经验增益。此外还引入了新的赛季徽章、赛季限定成就和装饰品。玩家可以通过完成指定的任务和达成特定成就来获得徽章、头像框和翅膀等奖励。 第三赛季结束时间 …

    css 2023年6月10日
    00
  • JS/HTML5游戏常用算法之碰撞检测 像素检测算法实例详解

    《JS/HTML5游戏常用算法之碰撞检测 像素检测算法实例详解》这篇文章主要讲解了游戏中常用的碰撞检测算法以及像素检测的具体实现方法。 文章分为以下几个部分: 碰撞检测算法分类介绍 像素检测算法实现原理解析 具体实例分析 接下来,我将逐一进行阐述: 碰撞检测算法分类介绍 文章首先介绍了常见的碰撞检测算法的分类,包括: 矩形碰撞检测算法 圆形碰撞检测算法 多边…

    css 2023年6月10日
    00
  • 如何理解 CSS 布局和块级格式上下文

    CSS 布局是指如何将 HTML 元素放置在页面上,它涉及到元素的尺寸、位置和对页面上其他元素的影响。而块级格式化上下文(Block Formatting Context,BFC)则是一种渲染页面的机制,它可以影响元素的布局和表现。 理解 CSS 布局和 BFC 对于有效的页面设计和创建至关重要。下面是针对这两个主题的完整攻略: 如何理解 CSS 布局 1.…

    css 2023年6月9日
    00
  • CSS制作框架 Sass 3.4.4 今日发布

    CSS制作框架Sass 3.4.4今日发布,这是一个非常值得关注的新版本。Sass 是一款流行的 CSS 预处理器。Sass 为 CSS 添加了许多高级特性,例如嵌套规则、变量、mixin 以及向导使用。 Sass 3.4.4 主要改进 Sass 3.4.4 版本是对 3.4 版本的补丁,主要改进包括: 修复了多个 bug。 修复了 Linux 下部分命令失…

    css 2023年6月10日
    00
合作推广
合作推广
分享本页
返回顶部