讲师中心 微信公众号
AI工具推荐 视频效率加速

Processing 2D图形变换:实现鼠标拖动与独立旋转

冬雪同学_6671

冬雪同学_6671

发布时间:2025-11-17 12:15:12

|

950人浏览过

|

来源于php中文网

原创

Processing 2D图形变换:实现鼠标拖动与独立旋转

本教程将深入探讨在processing中如何利用2d图形变换实现复杂的交互效果,特别是针对图形的鼠标拖动和原地旋转。我们将解决图形在旋转或平移时脱离屏幕的问题,通过引入`translate()`、`rotate()`以及`pushmatrix()`/`popmatrix()`等核心函数,并结合相对坐标系,实现图形的稳定拖动和独立的、方向相反的旋转效果,从而提升动画的动态表现力。

在Processing中进行图形绘制时,我们经常需要对图形进行平移、旋转或缩放等操作。然而,当图形使用绝对坐标绘制时,直接应用rotate()或translate()函数往往会导致图形偏离预期的位置,甚至移出屏幕。本教程旨在解决这一常见问题,并演示如何通过高效的2D变换技巧,实现图形的稳定拖动以及独立的、原地旋转。

理解绝对坐标与变换问题

最初的代码中,所有图形(如星星的三角形)都使用屏幕上的固定像素坐标进行定义。例如 triangle(1000,600,900,850,800,600)。当我们在draw()函数中直接调用 rotate() 或 translate() 时,这些变换会应用于整个坐标系。如果图形的中心不在坐标系原点 (0,0),旋转就会围绕 (0,0) 进行,导致图形看起来像是在屏幕上“漂移”而非原地旋转。同样,translate() 会将整个画布移动,如果原始坐标较大,图形很快就会超出可见区域。

实现鼠标拖动:全局平移

要实现图形的鼠标拖动,最直观的方法是利用translate()函数来平移整个绘图上下文。通过捕获鼠标的拖动事件,我们可以实时更新平移的偏移量。

  1. 定义平移变量: 在代码开头声明 float x = 0; 和 float y = 0; 用于存储平移的偏移量。

  2. 在 draw() 中应用 translate(): 在所有绘制命令之前调用 translate(x, y);。这将把整个坐标系的 (0,0) 点移动到 (x, y)。

  3. 使用 mouseDragged() 更新偏移量:mouseDragged() 是Processing提供的一个事件处理函数,在鼠标被拖动时触发。我们可以在此函数中更新 x 和 y 的值。为了使拖动更符合直觉(即鼠标位置成为新的原点),我们可以将 mouseX 和 mouseY 直接赋值给 x 和 y。

以下是实现鼠标拖动功能的代码示例:

float x = 0; // 全局X平移量
float y = 0; // 全局Y平移量
float r = 0; // 旋转量,暂时不用

void setup(){
  size (1800,1000);
}

void draw() {
  background (0); // 清除背景

  translate(x, y); // 应用全局平移

  // 绘制第一个星形(使用原始的绝对坐标,但现在它们是相对于新的原点(x,y))
  beginShape();
  fill(139,19,191);
  triangle(1000,600,900,850,800,600);
  fill(20,134,245);
  triangle (1100,400,1250,600,1000,600);
  fill(191,8,8);
  triangle(800,600,550,600,700,400);
  fill(86,165,3);
  triangle(700,400,550,100,900,350);
  fill(167,167,166);
  triangle(900,350,1250,100,1100,400);
  endShape();

  // 绘制第二个星形
  beginShape();
  fill(139,19,191);
  triangle(800,350,900,100,1000,350);
  fill(86,165,3);
  triangle(1000,350,1250,350,1100,550);
  fill(167,167,166);
  triangle(700,550,550,350,800,350);
  fill(20,134,245);
  triangle(700,550,550,850,900,650);
  fill(191,8,8);
  triangle(1100,550,1250,850,900,650);
  endShape();

  // 绘制中心圆
  fill(0,0,0);
  // 注意:如果希望圆也跟着拖动,它的坐标也需要是相对于新原点的
  // 或者,更好的做法是,将所有图形的坐标都定义为相对于它们各自的中心点,
  // 然后通过 translate 移动它们的中心点。
  // 这里为了演示,我们先让它保持原始坐标,但这意味着它不会跟着拖动。
  // 为了让它跟着拖动,我们需要像星星一样处理它的坐标,或者将它绘制在相对坐标系中。
  // 假设星星的中心点是 (900, 500),那么圆的绘制也应该围绕这个点。
  ellipse(900,500,425,425); // Processing中圆的绘制函数是ellipse
}

// 鼠标拖动事件处理
void mouseDragged(){
  // 更新全局平移量,使画布原点跟随鼠标
  x = mouseX;
  y = mouseY;
}

// Processing中没有内置的circle函数,通常用ellipse代替
// 这里为了兼容原代码,提供一个简单的circle函数
void circle(float cx, float cy, float radius){
  ellipse(cx, cy, radius, radius);
}

注意事项: 上述代码中,如果希望所有图形都围绕鼠标位置拖动,那么所有图形的坐标都应该被修改为相对于一个共同的中心点,例如 (0,0)。然后,translate(x, y) 将这个中心点移动到鼠标位置。原始代码中的绝对坐标 (1000,600 等) 使得 translate(x,y) 后,图形的实际位置是 (1000+x, 600+y),这可能会导致图形在拖动时看起来不居中。

实现原地独立旋转:pushMatrix()/popMatrix()与相对坐标

要实现两个星形在原地独立旋转,且方向相反,我们需要引入更高级的变换概念:pushMatrix()、popMatrix() 和相对坐标。

  1. pushMatrix() 和 popMatrix(): Processing使用矩阵堆栈来管理变换。pushMatrix() 将当前的变换状态(包括平移、旋转、缩放)保存到堆栈中,而 popMatrix() 则从堆栈中恢复上一个保存的状态。这使得我们可以在不影响全局坐标系或其他图形的情况下,对局部图形应用独立的变换。

  2. 相对坐标: 为了让图形能够围绕自身的中心点旋转,而不是围绕屏幕原点 (0,0) 旋转,我们需要将图形的顶点坐标定义为相对于其自身中心点的坐标。例如,如果一个星形的中心是 (900,500),那么它的顶点应该定义为 (x - 900, y - 500) 的形式。这样,当我们将坐标系原点平移到 (900,500) 后再旋转,星形就会围绕其自身中心旋转。

  3. 实现步骤:

    • 首先,通过 translate(x, y) 将整个绘图上下文移动到鼠标位置,作为所有图形的共同基准点。
    • 对于每个要独立旋转的星形:
      • 调用 pushMatrix() 保存当前变换状态。
      • 应用 rotate() 函数,传入一个随时间变化的旋转角度(例如 frameCount * 0.01)。
      • 使用相对坐标绘制星形。这些坐标现在是相对于当前变换后的坐标系原点。
      • 调用 popMatrix() 恢复到 pushMatrix() 之前的变换状态,确保下一个图形的变换是独立的。

以下是实现鼠标拖动和独立原地旋转的代码示例:

float x = 0; // 全局X平移量
float y = 0; // 全局Y平移量

void setup(){
  size (1800,1000);
  // 将初始平移量设置为画布中心,使图形初始居中
  x = width / 2;
  y = height / 2;
}

void draw() {
  background (0);

  // 1. 应用全局平移,使整个场景跟随鼠标拖动
  // 这里的 (x,y) 是鼠标位置,作为新的坐标系原点
  translate(x, y);

  // 2. 绘制第一个星形,顺时针旋转
  pushMatrix(); // 保存当前变换状态
    // 旋转当前坐标系。frameCount * 0.01 使其随时间平滑旋转
    rotate(frameCount * 0.01);
    // 使用相对坐标绘制第一个星形。
    // 这里的 (0,0) 已经是经过 translate(x,y) 后的鼠标位置。
    // 因此,这些相对坐标将使星形围绕鼠标位置旋转。
    fill(139,19,191);
    triangle( 100,  100,    0,  350, -100,  100);
    fill(20,134,245);
    triangle( 200, -100,  350,  100,  100,  100);
    fill(191,8,8);
    triangle(-100,  100, -350,  100, -200, -100);
    fill(86,165,3);
    triangle(-200, -100, -350, -400,    0, -150);
    fill(167,167,166);
    triangle(   0, -150,  350, -400,  200, -100);
  popMatrix(); // 恢复到 pushMatrix() 之前的变换状态

  // 3. 绘制第二个星形,逆时针旋转
  pushMatrix(); // 保存当前变换状态
    // 逆时针旋转
    rotate(frameCount * -0.01);
    // 使用相对坐标绘制第二个星形
    fill(139,19,191);
    triangle(-100, -150,    0, -400,  100, -150);
    fill(86,165,3);
    triangle( 100, -150,  350, -150,  200,   50);
    fill(167,167,166);
    triangle(-200,   50, -350, -150, -100, -150);
    fill(20,134,245);
    triangle(-200,   50, -350,  350,    0,  150);
    fill(191,8,8);
    triangle( 200,  50, 350, 350,   0, 150);
  popMatrix(); // 恢复到 pushMatrix() 之前的变换状态

  // 4. 绘制中心圆(也使用相对坐标,使其位于鼠标拖动位置的中心)
  fill(0,0,0);
  ellipse(0, 0, 425, 425); // 绘制在当前坐标系原点(0,0)处
}

// 鼠标拖动事件处理
void mouseDragged(){
  // 更新全局平移量,使画布原点跟随鼠标
  x = mouseX;
  y = mouseY;
}

// Processing中没有内置的circle函数,通常用ellipse代替
// 这里为了兼容原代码,提供一个简单的circle函数
void circle(float cx, float cy, float radius){
  ellipse(cx, cy, radius, radius);
}

关键概念总结:

  • translate(x, y): 移动坐标系的原点。在draw()函数开头使用,可以实现整个场景的拖动。
  • rotate(angle): 旋转坐标系。它会围绕当前坐标系的原点进行旋转。
  • pushMatrix() / popMatrix(): 这对函数是实现独立变换的关键。pushMatrix() 保存当前的变换状态,popMatrix() 恢复到最近保存的状态。这允许你在 pushMatrix() 和 popMatrix() 之间应用局部变换,而不会影响外部的绘图。
  • 相对坐标: 当你希望一个对象围绕自身中心旋转时,最好将对象的顶点坐标定义为相对于其自身中心点。然后,通过 translate() 将坐标系的原点移动到对象的中心,再应用 rotate()。

进一步优化与注意事项

  1. 模块化绘图代码: 为了提高代码的可读性和复用性,可以将每个星形的绘制代码封装成独立的函数。

    void drawStar1() {
      fill(139,19,191);
      triangle( 100,  100,    0,  350, -100,  100);
      fill(20,134,245);
      triangle( 200, -100,  350,  100,  100,  100);
      // ... 更多三角形
    }
    
    void drawStar2() {
      fill(139,19,191);
      triangle(-100, -150,    0, -400,  100, -150);
      fill(86,165,3);
      triangle( 100, -150,  350, -150,  200,   50);
      // ... 更多三角形
    }

    然后在 draw() 中调用:

    pushMatrix();
      rotate(frameCount * 0.01);
      drawStar1();
    popMatrix();
    
    pushMatrix();
      rotate(frameCount * -0.01);
      drawStar2();
    popMatrix();
  2. 变换顺序: 变换的顺序非常重要。通常的顺序是:translate() -> rotate() -> scale()。如果你先旋转再平移,结果会与先平移再旋转大相径庭。

  3. 旋转速度控制:frameCount * 0.01 提供了一个简单的动画速度。你可以引入一个变量来控制旋转速度,例如 rotationSpeed = 0.01;,然后用 frameCount * rotationSpeed 来调整。

通过掌握 translate()、rotate() 以及 pushMatrix()/popMatrix() 的组合使用,并结合相对坐标的思维,你将能够在Processing中创建出更加复杂和富有交互性的2D动画效果。

热门AI工具

更多
讯飞绘文

讯飞绘文是一款由科大讯飞推出的一站式 AIGC 内容运营平台。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

相关专题

更多
css中float用法
css中float用法

css中float属性允许元素脱离文档流并沿其父元素边缘排列,用于创建并排列、对齐文本图像、浮动菜单边栏和重叠元素。想了解更多float的相关内容,可以阅读本专题下面的文章。

6147

2024.04.28

C++中int、float和double的区别
C++中int、float和double的区别

本专题整合了c++中int和double的区别,阅读专题下面的文章了解更多详细内容。

704

2025.10.23

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

5387

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2348

2023.08.10

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

5387

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2348

2023.08.10

PixTV官网入口地址合集
PixTV官网入口地址合集

本专题汇总了 PixTV AI 一站式视频创作平台的官方入口与使用教程。无需下载软件,浏览器直接访问即可使用。平台将剧本、图像、视频、声音与剪辑整合在“无限画布”中,接入 GPT Image 2.5、Seedance 2.5 等头部模型。本专题整理了从新建画布、角色锚定、分镜拆分到视频生成与导出的完整操作指南,助你快速上手 AI 短剧与漫剧创作。

20

2026.10.10

Kratos框架HTTP与gRPC服务开发教程
Kratos框架HTTP与gRPC服务开发教程

本专题围绕Kratos框架双协议服务开发,涵盖HTTP路由与处理器编写、参数获取、gRPC服务实现与客户端调用、metadata上下文传递、encoding编解码注册、统一响应封装、超时控制与流式响应实现方法。

20

2026.10.10

Kratos框架Protobuf接口定义与代码生成合集
Kratos框架Protobuf接口定义与代码生成合集

本专题讲解Kratos框架接口定义体系,涵盖proto编写规范、proto add/client/server生成命令、http注解路由、validate校验、OpenAPI文档生成、跨服务proto复用与兼容性设计。

0

2026.10.10

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
GDB Reference Card
GDB Reference Card

共0课时 | 0人学习

《Debugging with GDB》用户手册
《Debugging with GDB》用户手册

共0课时 | 0人学习

Valgrind FAQ
Valgrind FAQ

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn