使用鼠标旋转画布中的图像

2023-11-22

在我的代码中,我将图像加载到画布中。然后我需要调整大小、旋转和拖动它。我设法实现了拖动和调整大小。

如何在这段代码上使用鼠标实现旋转(沿着图像的中心)。

我的 HTML 页面:

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; padding:10px;}
    #canvas{border:1px solid red;}
</style>

<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    var canvasOffset=$("#canvas").offset();
    var offsetX=canvasOffset.left;
    var offsetY=canvasOffset.top;

    var startX;
    var startY;
    var isDown=false;


    var pi2=Math.PI*2;
    var resizerRadius=8;
    var rr=resizerRadius*resizerRadius;
    var draggingResizer={x:0,y:0};
    var imageX=50;
    var imageY=50;
    var imageWidth,imageHeight,imageRight,imageBottom;
    var draggingImage=false;
    var startX;
    var startY;



    var img=new Image();
    img.onload=function(){
        imageWidth=img.width;
        imageHeight=img.height;
        imageRight=imageX+imageWidth;
        imageBottom=imageY+imageHeight
        draw(true,false);
    }
    img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/facesSmall.png";


    function draw(withAnchors,withBorders){

        // clear the canvas
        ctx.clearRect(0,0,canvas.width,canvas.height);

        // draw the image
        ctx.drawImage(img,0,0,img.width,img.height,imageX,imageY,imageWidth,imageHeight);

        // optionally draw the draggable anchors
        if(withAnchors){
            drawDragAnchor(imageX,imageY);
            drawDragAnchor(imageRight,imageY);
            drawDragAnchor(imageRight,imageBottom);
            drawDragAnchor(imageX,imageBottom);
        }

        // optionally draw the connecting anchor lines
        if(withBorders){
            ctx.beginPath();
            ctx.moveTo(imageX,imageY);
            ctx.lineTo(imageRight,imageY);
            ctx.lineTo(imageRight,imageBottom);
            ctx.lineTo(imageX,imageBottom);
            ctx.closePath();
            ctx.stroke();
        }

    }

    function drawDragAnchor(x,y){
        ctx.beginPath();
        ctx.arc(x,y,resizerRadius,0,pi2,false);
        ctx.closePath();
        ctx.fill();
    }

    function anchorHitTest(x,y){

        var dx,dy;

        // top-left
        dx=x-imageX;
        dy=y-imageY;
        if(dx*dx+dy*dy<=rr){ return(0); }
        // top-right
        dx=x-imageRight;
        dy=y-imageY;
        if(dx*dx+dy*dy<=rr){ return(1); }
        // bottom-right
        dx=x-imageRight;
        dy=y-imageBottom;
        if(dx*dx+dy*dy<=rr){ return(2); }
        // bottom-left
        dx=x-imageX;
        dy=y-imageBottom;
        if(dx*dx+dy*dy<=rr){ return(3); }
        return(-1);

    }


    function hitImage(x,y){
        return(x>imageX && x<imageX+imageWidth && y>imageY && y<imageY+imageHeight);
    }


    function handleMouseDown(e){
      startX=parseInt(e.clientX-offsetX);
      startY=parseInt(e.clientY-offsetY);
      draggingResizer=anchorHitTest(startX,startY);
      draggingImage= draggingResizer<0 && hitImage(startX,startY);
    }

    function handleMouseUp(e){
      draggingResizer=-1;
      draggingImage=false;
      draw(true,false);
    }

    function handleMouseOut(e){
      handleMouseUp(e);
    }

    function handleMouseMove(e){

      if(draggingResizer>-1){

          mouseX=parseInt(e.clientX-offsetX);
          mouseY=parseInt(e.clientY-offsetY);

          // resize the image
          switch(draggingResizer){
              case 0: //top-left
                  imageX=mouseX;
                  imageWidth=imageRight-mouseX;
                  imageY=mouseY;
                  imageHeight=imageBottom-mouseY;
                  break;
              case 1: //top-right
                  imageY=mouseY;
                  imageWidth=mouseX-imageX;
                  imageHeight=imageBottom-mouseY;
                  break;
              case 2: //bottom-right
                  imageWidth=mouseX-imageX;
                  imageHeight=mouseY-imageY;
                  break;
              case 3: //bottom-left
                  imageX=mouseX;
                  imageWidth=imageRight-mouseX;
                  imageHeight=mouseY-imageY;
                  break;
          }

          // enforce minimum dimensions of 25x25
          if(imageWidth<25){imageWidth=25;}
          if(imageHeight<25){imageHeight=25;}

          // set the image right and bottom
          imageRight=imageX+imageWidth;
          imageBottom=imageY+imageHeight;

          // redraw the image with resizing anchors
          draw(true,true);

      }else if(draggingImage){

          imageClick=false;

          mouseX=parseInt(e.clientX-offsetX);
          mouseY=parseInt(e.clientY-offsetY);

          // move the image by the amount of the latest drag
          var dx=mouseX-startX;
          var dy=mouseY-startY;
          imageX+=dx;
          imageY+=dy;
          imageRight+=dx;
          imageBottom+=dy;
          // reset the startXY for next time
          startX=mouseX;
          startY=mouseY;

          // redraw the image with border
          draw(false,true);

      }


    }


    $("#canvas").mousedown(function(e){handleMouseDown(e);});
    $("#canvas").mousemove(function(e){handleMouseMove(e);});
    $("#canvas").mouseup(function(e){handleMouseUp(e);});
    $("#canvas").mouseout(function(e){handleMouseOut(e);});


}); // end $(function(){});
</script>

</head>

<body>
    <p>Resize the image using the 4 draggable corner anchors</p>
    <p>You can also drag the image</p>
    <canvas id="canvas" width=350 height=350></canvas>
</body>
</html>

以下是如何使用拖动手柄来旋转图像

enter image description hereenter image description here

mousedown 事件处理程序会测试用户是否开始拖动旋转手柄。

使用 context.isPointInPath(x,y) 可以更轻松地进行命中测试,它测试指定的 [x,y] 坐标是否位于最近绘制的路径内(方便的是,旋转手柄实际上是路径)。

所以 mousedown 会像这样激活拖动手柄:

  • 计算当前的mouseX和mouseY。
  • 重绘旋转手柄(必需的,因为 isPointInPath 仅命中测试最近的路径)
  • 如果用户确实单击了旋转手柄,则设置 isDown 标志。

mousedown 代码如下所示:

function handleMouseDown(e){
  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);
  drawRotationHandle(false);
  isDown=ctx.isPointInPath(mouseX,mouseY);
}

是的...我们可以简单地对旋转手柄末端的一个圆圈进行命中测试,但是使用 isPointInPath 将允许您绘制您想要的任何花哨的旋转手柄。

isPointInPath 还有另一个好处。当包含路径的上下文旋转时,isPointInPath 将命中测试旋转路径为你。这意味着您不必编写数学代码来取消旋转鼠标坐标来进行命中测试 - 它已经为您完成了!

mousemove 处理程序以旋转句柄指定的角度重新绘制可旋转图像:

  • 如果未设置 isDown 标志,则返回(用户没有拖动旋转手柄)。
  • 计算当前的mouseX和mouseY。
  • 计算旋转手柄的当前角度。
  • 以当前角度重新绘制可旋转图像。

鼠标移动代码如下所示:

function handleMouseMove(e){
  if(!isDown){return;}

  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);
  var dx=mouseX-cx;
  var dy=mouseY-cy;
  r=Math.atan2(dy,dx);
  draw();
}

使用上下文的变换方法以指定的旋转绘制图像

function drawRect(){
    ctx.save();
    ctx.translate(cx,cy);
    ctx.rotate(r);
    ctx.drawImage(img,0,0);
    ctx.restore();
}

最后,mouseup 和 mouseout 处理程序通过清除 isDown 标志来停止拖动操作。

function handleMouseUp(e){
  isDown=false;
}

function handleMouseOut(e){
  isDown=false;
}

这是代码和小提琴:http://jsfiddle.net/m1erickson/QqwKR/

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    #canvas{border:1px solid red;}
</style>

<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    var canvasOffset=$("#canvas").offset();
    var offsetX=canvasOffset.left;
    var offsetY=canvasOffset.top;

    var isDown=false;

    var cx=canvas.width/2;
    var cy=canvas.height/2;
    var w;
    var h;
    var r=0;

    var img=new Image();
    img.onload=function(){
        w=img.width/2;
        h=img.height/2;
        draw();
    }
    img.src="facesSmall.png";


    function draw(){
        ctx.clearRect(0,0,canvas.width,canvas.height);
        drawRotationHandle(true);
        drawRect();
    }

    function drawRect(){
        ctx.save();
        ctx.translate(cx,cy);
        ctx.rotate(r);
        ctx.drawImage(img,0,0,img.width,img.height,-w/2,-h/2,w,h);
        ctx.restore();
    }

    function drawRotationHandle(withFill){
        ctx.save();
        ctx.translate(cx,cy);
        ctx.rotate(r);
        ctx.beginPath();
        ctx.moveTo(0,-1);
        ctx.lineTo(w/2+20,-1);
        ctx.lineTo(w/2+20,-7);
        ctx.lineTo(w/2+30,-7);
        ctx.lineTo(w/2+30,7);
        ctx.lineTo(w/2+20,7);
        ctx.lineTo(w/2+20,1);
        ctx.lineTo(0,1);
        ctx.closePath();
        if(withFill){
            ctx.fillStyle="blue";
            ctx.fill();
        }
        ctx.restore();
    }

    function handleMouseDown(e){
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);
      drawRotationHandle(false);
      isDown=ctx.isPointInPath(mouseX,mouseY);
      console.log(isDown);
    }

    function handleMouseUp(e){
      isDown=false;
    }

    function handleMouseOut(e){
      isDown=false;
    }

    function handleMouseMove(e){
      if(!isDown){return;}

      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);
      var dx=mouseX-cx;
      var dy=mouseY-cy;
      r=Math.atan2(dy,dx);
      draw();
    }

    $("#canvas").mousedown(function(e){handleMouseDown(e);});
    $("#canvas").mousemove(function(e){handleMouseMove(e);});
    $("#canvas").mouseup(function(e){handleMouseUp(e);});
    $("#canvas").mouseout(function(e){handleMouseOut(e);});

}); // end $(function(){});
</script>

</head>

<body>
    <p>Rotate by dragging blue rotation handle</p>
    <canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用鼠标旋转画布中的图像 的相关文章

  • React-native:将场景绑定到导航栏

    我正在整理这个提问 回答应用程序 并遇到了这个障碍 我想从导航栏触发场景中的功能 与登录应用程序类似 我在导航栏中有一个用于提交答案的按钮 RightButton route navigator index navState if rout
  • 如何更改传单中功能集的样式?

    我正在看等值区域的例子 https leafletjs com examples choropleth https leafletjs com examples choropleth 这是他们使用的数据源 type Feature prop
  • 访问sendBeacon发送的数据

    文档表明sendBeacon通过发送其数据HTTP POST request 但在 PHP 中 POST变量似乎是一个空数组 这是我的 JavaScript 代码 navigator sendBeacon beacon log php My
  • 用隐藏单元格补充 colspanned 表格有什么不好吗?

    我一直在表格上开发一些排序和选择功能 我发现在具有跨单元格的表格中定位非常困难 我只是添加了跨区单元格并将其隐藏 它看起来不错 它与我的 js 一起工作 非常适合索引 但我想知道这是否是合法的方法 stuffing display none
  • 消息“在 jest.setTimeout 指定的 5000 毫秒超时内未调用异步回调”

    我正在使用 Puppeteer 和 Jest 来运行一些前端测试 我的测试如下 describe Profile Tab Exists and Clickable settings user gt test Assert that you
  • jquery 验证错误位置

    这看起来很简单 但我无法弄清楚 我正在使用 jquery 验证插件 我验证所有文件 但我想要的是在输入文本行中显示验证消息警报 例如在电子邮件输入中 请填写电子邮件地址 但现在它出现在所有字段下 在我的html中
  • 如何使用canvas.toDataURL()将画布保存为图像?

    我目前正在构建一个 HTML5 Web 应用程序 Phonegap 本机应用程序 我似乎不知道如何将画布保存为图像canvas toDataURL 有人可以帮我吗 这是代码 有什么问题吗 我的画布被命名为 canvasSignature J
  • 如何使用 jest 模拟第三方库

    我正在开发一个node js应用程序使用nestjs我有一堂课叫LoggerService如下 export class LoggerService private logger Rollbar constructor this logge
  • Ember.js 处理 View 事件后转换到路由

    Setup 我有一个 Ember 应用程序 支持使用 Imgur API 上传图像 我已经有一个工作路线和模板来处理任何 Imgur ID 但我想在上传新图像后转换到此路线 使用返回的 Imgur ID 这是该应用程序的相关部分 http
  • jquery window.open 在 ajax 成功中被阻止

    尝试在我的 ajax 成功调用中打开一个新的浏览器窗口 但是 它被阻止为弹出窗口 我做了一些搜索 发现用户事件需要绑定到 window open 才能避免这种情况发生 我还找到了这个解决方案 您可以在 ajax 之前打开一个空白窗口 然后在
  • 仅一页 JavaScript 应用程序

    您是否尝试过单页 Web 应用程序 即浏览器仅从服务器 获取 一页 其余部分由客户端 JavaScript 代码处理 此类 应用程序页面 的一个很好的例子是 Gmail 对于更简单的应用程序 例如博客和 CMS 使用这种方法有哪些优点和缺点
  • HTML2canvas 和 Canvas2image,下载的屏幕截图不显示我的 HTML 图像

    我一直在开发一个 HTML 页面 我想将其转换为图像 我一直在使用 html2canvas 和 canvas2image 脚本并采用此代码http jsfiddle net 8ypxW 3 http jsfiddle net 8ypxW 3
  • JavaScript 中数组的 HTML 数据列表值

    我有一个简单的程序 它必须从服务器上的文本文件中获取值 然后将数据列表填充为输入文本字段中的选择 为此 我想要采取的第一步是我想知道如何动态地将 JavaScript 数组用作数据列表选项 我的代码是
  • 如果链接包含特定文本,jQuery 将类添加到 href

    我的网站上的列表中有一些动态填充的链接 这些链接链接到文件 是否可以使用 jQuery 查看文件名是否以 pdf 结尾 并在 href 或类似的链接文本以 mp3 结尾时添加一个类 例如 我的列表中有以下链接 文件1 pdf 歌曲1 mp3
  • JS用正则表达式替换数字

    我有元素的标识符 如下所示 form book 1 2 3 我想要的是用其他值替换该标识符中的第二个数字 我将函数 match 与以下正则表达式一起使用 var regexp d d d 但它返回我包含的数组 1 2 3 2 因此 当我尝试
  • 淡出和循环一组 div 的最佳方式

    假设我有以下 div div class a You are funny div div class b You are smart div div class c You are cool div 最好的展示方式是什么div a持续 5
  • 当用户单击链接时,如何记录 MixPanel 事件?

    当用户单击某种类型的链接时 我试图在 MixPanel 中记录一个事件 我正在使用 JQuery 不引人注意地完成此操作 据我所知 我需要添加一个回调函数 以便在记录事件后将用户带到 URL 这是我正在使用的代码 不幸的是
  • JavaScript 代码在不使用 ActiveX 的情况下截取网站屏幕截图

    我有一个用户与之交互的 JavaScript 应用程序 我需要保存当前界面的外观 裁剪出我需要的部分 或者通过指定div只拍摄我需要的部分 然后发送回服务器 显然任何外部服务都无法做到这一点 我需要一个 JavaScript 或Flash
  • 单击引导分页链接时调用 jquery 函数

    我想在单击引导分页链接时调用 jquery 函数 假设我想从第1页遍历到第2页 应该调用一个jquery函数 我正在使用以下代码 但它不起作用 ul pagination on click li function alert page ch
  • 需要有关 React Js 的帮助

    我是 React Js 新手 我的代码无法正常工作 请看下面 这是我的脚本文件Main jsx 该文件由 React 编译 输出放置在 dist 文件夹下的 main js 文件中 var react require react react

随机推荐

  • 如何使用 Visual Studio 将 Unicode 打印到 C 语言的输出控制台?

    正如问题所说 我必须做什么才能将 Unicode 字符打印到输出控制台 我必须使用什么设置 现在我有这个代码 wchar t text L the 来 wprintf L Text is s n text return EXIT SUCCE
  • 我可以使用 Google Maps API 显示自定义地图吗?

    我所说的自定义地图是指自定义地图图块 例如绘制另一个星球的地图 我环顾四周 但找不到任何资源来指导如何在 Google 或任何网站上执行此操作 我知道这是可能的 因为我以前在网站上看到过这样做 但不幸的是 具有该网站链接的网站已关闭 因此我
  • 虚函数和性能 C++

    在您对重复的标题感到畏缩之前 另一个问题不适合我在这里问的问题 IMO 所以 我真的很想在我的应用程序中使用虚拟函数 让事情变得简单一百倍 这不是 OOP 的全部内容吗 但我在某处读到它们是以性能成本为代价的 除了过早优化的同样老套的炒作之
  • 如何在 C# 中从数据表动态构建插入命令

    我在从 C 中的 dataTable 对象动态创建 SQL 插入语句时遇到一些问题 我想知道实现它的最佳实践 这是我的代码片段 到目前为止我已经尝试过 String sqlCommandInsert INSERT INTO dbo RAW
  • 如何从java启动chrome浏览器

    有没有什么聪明的方法可以从 java 类启动 chrome 浏览器 我问这个问题是因为我想要一种智能方式来启动一个应用程序 该应用程序需要在以 Internet Explorer 作为默认浏览器并安装了 java 1 4 2 的计算机上使用
  • PHP排序最近的坐标

    我在 PHP Web 服务中有一个包含经度和纬度的 MySQL 表 我只想向用户发送 5 个最接近的坐标 我编写了计算从坐标到用户在 POST 请求中发送的坐标的距离的方法 但我不确定如何对其进行排序 并且只发回一些坐标 这是距离方法 fu
  • ASIHTTPRequest 将 json 发送到 php 服务器

    在过去的 5 个小时里 我一直在尝试将 json 对象发布到 PHP 脚本 我已阅读所有文档 看起来代码应该可以工作 但事实并非如此 请求已发出并接收正常 但我无法访问发布的 json 数据 或者甚至不知道它是否已正确发送 我不断收到空的
  • 在 Sitecore 中,当向模板添加字段时,有一个名为“共享”的复选框。这是为了什么?

    这似乎是一个愚蠢的问题 但我在我拥有的 sitecore 文档 在线和离线 中可以找到的最接近的答案是共享字段是 跨语言共享 我认为这意味着共享字段的内容在 sitecore 中该项目的所有翻译中都是相同的 那是对的吗 是的 这是正确的 项
  • 有没有办法将 css 文件附加到 jEditorPane?

    足够简单的问题 我有一个包含 HTML 的字符串 该字符串正在传递给 JEditorPane 供用户使用 我可以附加 CSS 文件 或包含 CSS 规则的字符串 以允许更具体的文本样式吗 The HTMLEditorKit默认情况下查找文件
  • 如何检测行的开头,或者:“当前上下文中不存在名称‘getCharPositionInLine’”

    我正在尝试创建一个行开头标记 lexer grammar ScriptLexer BOL getCharPositionInLine 0 Beginning Of Line token 但上面会发出错误 The name getCharPo
  • 根据jstl中的索引获取arraylist的元素[重复]

    这个问题在这里已经有答案了 这也许是一件相对简单的事情 但由于某种原因我似乎没有做对 如何根据索引从jstl中的arrayList中获取元素 在纯java中 假设我有这个数组列表 ArrayList lt String gt colors
  • 将 MATLAB 图形保存为 PDF,质量为 300 DPI,居中

    我想将 MATLAB 图另存为 PDF 质量为 300DPI 并居中 到目前为止 我设法保存它 但图像似乎被裁剪了 我将页面类型更改为A3并解决了问题 但我正在寻找更优雅的东西 我是通过 GUI 执行此操作 但也许在 MATLAB 中使用命
  • Python 按键和按键释放监听器

    我正在使用 python 代码控制一辆遥控玩具车 截至目前 代码如下 def getkey fd sys stdin fileno old termios tcgetattr fd new termios tcgetattr fd new
  • 开源.NET富文本编辑器UserControl与GUI全部实现[关闭]

    Closed 这个问题不符合堆栈溢出指南 目前不接受答案 我正在编写一个数据输入程序 其中最后一个字段将是一个 RichTextBox 供用户输入备注 并格式化为打印 是否有免费或开源的 NET WinForms 富文本编辑器 并且所有 G
  • 重定向系统中的 cookie 令人困惑

    我从事 PHP 工作 我想在登录后将页面重定向到我想访问的最后一个页面 但 5 小时后我仍然堆在这里 但我仍然没有成功 这是架构 我有 3 个 php 文件 newest php before login signin php before
  • 运行时检查失败 #2 - 变量周围的堆栈已损坏

    我已经在 stackoverflow 上看到了一些问题 但没有一个能解决我的问题 我在 C 中有该代码 include
  • Switch Case 语句中出现重复 const 声明错误

    我有以下代码 但收到错误 重复声明 query url switch condition case complex const query url something break default const query url someth
  • 如何正确调用TFS中的GetWorkspace?

    目前当我打电话时GetWorkspace I get ItemNotMappedException例外 但是当我手动迭代工作区时 我可以让我的代码正常工作 这太奇怪了 我想知道我是否应该在调用之前调用一些刷新或其他东西GetWorkspac
  • 使用Python请求时如何获取底层socket

    我有一个 Python 脚本 它使用以下命令创建许多短暂的并发连接requests图书馆 我特别需要找出每个连接使用的源端口 并且我认为我需要为此访问底层套接字 有没有办法通过响应对象来获取它 对于流连接 使用stream True参数 您
  • 使用鼠标旋转画布中的图像

    在我的代码中 我将图像加载到画布中 然后我需要调整大小 旋转和拖动它 我设法实现了拖动和调整大小 如何在这段代码上使用鼠标实现旋转 沿着图像的中心 我的 HTML 页面