首页 > 其他分享 >关于矩形旋转,拖拽,拉伸

关于矩形旋转,拖拽,拉伸

时间:2024-12-01 18:55:29浏览次数:5  
标签:拉伸 const index prevPoint 旋转 矩形 拖拽 Math

总结:用svg和<rect/> 无法同时实现三个效果,如果不实现拖拽效果,只实现旋转和拉伸可以采用和transform实现

因为,拖拽会导致拖拽中心的偏移导致无法计算新的旋转中心。

如果要同时实现这三个效果只能使用<polygon/>在旋转时候,直接根据旋转角度计算四个点的位置。在拉伸时候,计算拉伸的点在邻边的投影。保证矩形。

在拖拽后,直接根据四个点计算新的旋转中心

其实不使用svg来进行实现,更简单 

以下是我得出结果的过程

1、旋转中心点是以矩形中心旋转。

目前有一个项目。已经实现了矩形的拉伸和拖拽。只要求加上个旋转

从代码上看,项目是采用svg 的rect元素实现的。但是有几个问题

1、rect的元素。是以(x,y)为元素的左上角位置。width 和height 决定元素。这个就导致rect元素本身无法实现旋转角度的。首先想的用css实现

2、普通元素的旋转是默认几何中心的,svg内的元素默认的旋转是以<svg/>的左上角。svg有一个 transform="rotate(90, 150 120)",第一个参数是

角度,后面是旋转中心坐标。所以以矩形的x,y和宽高,去计算旋转中心。

 这样的话,确实能实现要求的以中心旋转的问题。

根据鼠标移动的位置和初始位置的角度差值,计算旋转角度得到transform的第一个参数,后面两个参数,直接计算矩形重心

计算鼠标角度的方法:

mouseX,mouseY是鼠标相对于视图的位置, 所以centerX,centerY是旋转中心相对于视图的位置所以可以用getBoundingClientRect()

    getAngle(mouseX, mouseY) {       const { centerX, centerY } = this.rotateCenter       const angle = Math.atan2(mouseY - centerY, mouseX - centerX) * (180 / Math.PI);       return angle < 0 ? 360 + angle : angle     },

但是在旋转的时候,很有可能会出界。这个限制范围要修改。

旋转矩形是否出界,不需要去计算四个角的位置,其实只要判断矩形的外接矩形是否出界。关于矩形的外接矩形使用getBBox()方法。

 

其实到这里,三个功能都已经有了。但是连续进行测试的时候有问题

如果矩形经过transform该方法返回的也是未旋转前的属性。在未进行旋转变化的时候,计算的位置确实是重心,但是在进行拖拽后。

有旋转的偏移量,但是这个时候重新计算,算出来的结果就不对了。导致了旋转后进行拖拽,矩形会突然跳位置,跳位置后再旋转就没问题了。

我自己其实到这里的时候也没想过换<polygon/>。新的旋转重心肯定跟老的旋转角度有一定关系,我尝试使用点旋转后位置去计算,但是还是不对。

也是因为这个旋转重心的问题,我才想用<polygon/>.因为这个元素本身根据四个点可以直接实现矩形旋转的效果,而且四个点的位置可以直接定旋转重心。

 

polygon实现矩形。

用polygon旋转中心的问题后,有其他问题

1、根据旋转角度去计算点关于角度偏移的位置

2、其中一个点拉伸后,为了保证矩形,其他点的计算(拉伸点在相邻两个边的投影就是拉伸后的坐标,即点在直线上的投影)

3、出界问题(既然能拿到四个点,我是直接计算的,用外接矩形方式也可)

图上123数字是点在拉伸方法中计算index的位置。

 

/**
     * 根据旋转中心和旋转角度计算点旋转后的位置
     * @param {*} centerpPoint  旋转中心点
     * @param {*} angle 旋转角度
     * @param {*} point 旋转点
     * @returns 新的点坐标
     */
    rotatePoint(centerpPoint, angle, point) {
      const { cx, cy } = centerpPoint
      const { px, py } = point
      let radians = (Math.PI / 180) * angle;
      let cos = Math.cos(radians);
      let sin = Math.sin(radians);

      let nx = (cos * (px - cx)) + (sin * (py - cy)) + cx;
      let ny = (cos * (py - cy)) - (sin * (px - cx)) + cy;

      return { x: nx, y: ny };
    },
 /**
     * 计算四个角拉伸后的矩形四个点
     * @param {Array} points // 未拉伸前矩形四个点坐标数组[{x,y}]
     * @param {Number} pointIndex // 当前点在矩形8个点中的index 0,1,2,3,4,5,6,7,8 左上角开始计算
     * @param {Number} dx //鼠标横向偏移量
     * @param {Number} dy //鼠标纵向偏移量
     * @returns 
     */
    fixRectPoint(points, pointIndex, dx, dy) {
      let orgPoints = JSON.parse(JSON.stringify(points)) //深拷贝
      let result = []
      const index = pointIndex / 2
      let nowPoint = orgPoints[index]
      nowPoint.x += dx;
      nowPoint.y += dy;
      const prevPoint_index = (index - 1) < 0 ? 3 : index - 1 // 上一个点 的 index
      const nextPoint_index = (index + 1) % 4 // 下一个点 的 index
      const diagonalPoint_index = (index + 2) % 4 // 对角点 的 index
      const prevPoint = orgPoints[prevPoint_index]
      const nextPoint = orgPoints[nextPoint_index]
      const diagonalPoint = orgPoints[diagonalPoint_index]
      const point_prev = this.fixPointOntoLinePoint(nowPoint, diagonalPoint, prevPoint)
      const point_next = this.fixPointOntoLinePoint(nowPoint, diagonalPoint, nextPoint)
      result[index] = nowPoint
      result[prevPoint_index] = point_prev
      result[nextPoint_index] = point_next
      result[diagonalPoint_index] = diagonalPoint
      return result
    },

 

 /**
     * 计算矩形四个边中间点拉伸后的矩形四个点
     * @param {Array} points // 未拉伸前矩形四个点坐标数组[{x,y}]
     * @param {*} index  // 当前点在矩形8个点中的index 0,1,2,3,4,5,6,7,8 左上角开始计算
     * @param {*} nowPoint //当前点未拉伸前的坐标{x,y}
     * @param {*} dx  //鼠标横向偏移量
     * @param {*} dy //鼠标纵向偏移量
     * @returns 
     */
    fixCenterPoint(points, pointIndex, nowPoint, dx, dy) {
      let result = JSON.parse(JSON.stringify(points)) //深拷贝
      nowPoint.x += dx;
      nowPoint.y += dy;
      const prevPoint_index = ((pointIndex - 1) < 0 ? 7 : pointIndex - 1) / 2 // 上一个点 的 index
      const nextPoint_index = ((pointIndex + 1) % 8) / 2 // 下一个点 的 index
      const prev_prevPoint_index = prevPoint_index - 1 < 0 ? 3 : prevPoint_index - 1 // 下一个点 的 index
      const next_nextPoint_index = (nextPoint_index + 1) % 4
      const prev_prevPoint = points[prev_prevPoint_index]
      const prevPoint = points[prevPoint_index]
      const nextPoint = points[nextPoint_index]
      const next_nextPoint = points[next_nextPoint_index]
      const point_prev = this.fixPointOntoLinePoint(nowPoint, prev_prevPoint, prevPoint)
      const point_next = this.fixPointOntoLinePoint(nowPoint, next_nextPoint, nextPoint)
      result[prevPoint_index] = point_prev
      result[nextPoint_index] = point_next
      return result
    },
 /**
     * 计算p0在两点p1和p2所构成的直线方程的投影坐标
     */
    fixPointOntoLinePoint(p0, p1, p2) {
      const { x: x0, y: y0 } = p0 // 投影点
      const { x: x1, y: y1 } = p1 //所在直线上点1
      const { x: x2, y: y2 } = p2 // 所在直线上点2
      // (y - y1) / (y2 - y1) = (x - x1) / (x2 - x1) 两点直线方程化简可得
      // (y2 - y1)x - (x2 - x1)y + (x2 - x1)y1 - (y2 - y1)x1 = 0 ax + by + c = 0
      const a = (y2 - y1)
      const b = - (x2 - x1)
      const c = (x2 - x1) * y1 - (y2 - y1) * x1
      // 点 Q(x,y)是直线L(ax + by + c = 0)上离点P(x0,y0)最近的点,于是向量PQ与L垂直,
      // 则它们的点积为0  (x-x0)(a)+(y-y0)(b)=0 
      // const x = (b * b * x0 - a * b * y0 - a * c) / (a * a + b * b)
      // const y = (a * a * y0 - a * b * x0 - b * c) / (a * a + b * b)
      const x = (Math.pow(b, 2) * x0 - a * b * y0 - a * c) / (Math.pow(a, 2) + Math.pow(b, 2))
      const y = (Math.pow(a, 2) * y0 - a * b * x0 - b * c) / (Math.pow(a, 2) + Math.pow(b, 2))
      return { x, y }
    },
/**
     * 判断矩形元素是否出界
     * @param {*} element  html dom结构
     * @param {Boolean} isNeedData  是否出界数据 false 不需要 出界数据负数代表出界
     * @returns {Boolean||Object} isNeedData=true 返回Object 
     */
    isOutRange(points, isNeedData = false) {
      let left = 0, right = 0, top = 0, bottom = 0;
      for (let i = 0; i < points.length; i++) {
        const item = points[i]
        if (i === 0) {
          left = item.x
          right = item.x
          top = item.y
          bottom = item.y
        } else {
          left = Math.min(left, item.x)
          right = Math.max(right, item.x)
          top = Math.min(top, item.y)
          bottom = Math.max(bottom, item.y)
        }
      }
      const imgDom = document.getElementById("mark-img")
      const totalWidth = parseFloat(imgDom.getAttribute("width"))
      const totalHeight = parseFloat(imgDom.getAttribute("height"))
      let outRangeData = { left: 0, right: 0, top: 0, bottom: 0, width: 0, height: 0 }
      let outRangeFlag = false
      if (left < 0) { // 左边出界
        outRangeData.left = left
        outRangeFlag = true
      }
      if (top < 0) { //上边出界
        outRangeData.top = top
        outRangeFlag = true
      }
      if (right > totalWidth) { //右边出界,
        outRangeData.right = (totalWidth - right)
        outRangeFlag = true
      }
      if (bottom > totalHeight) { //下边出界,
        outRangeData.bottom = (totalHeight - bottom)
        outRangeFlag = true
      }
      if (right - left > totalWidth) { //宽度出界
        outRangeData.width = totalWidth - (right - left)
      }
      if (bottom - top > totalHeight) { //高度出界
        outRangeData.height = totalHeight - (bottom - top)
      }
      return isNeedData ? { outRangeData, outRangeFlag } : outRangeFlag
    },

 具体代码实现,后续整理放出

 

关于getBoundingClientRect()和getBBox()区别

 

标签:拉伸,const,index,prevPoint,旋转,矩形,拖拽,Math
From: https://www.cnblogs.com/cbb-web/p/18580072

相关文章

  • 怎么使用纯css实现左右拉伸拖动?
    可以使用纯CSS实现左右拉伸拖动,但功能有限,真正的拖动行为通常需要JavaScript。纯CSS方法主要依赖于resize属性,它可以使元素具有可调整大小的行为。以下两种方法可以实现类似左右拉伸的效果:1.使用<textarea>或<inputtype="text">:这两种元素默认就带有resize属性......
  • 洛谷P1719 最大加权矩形 (最大子数组和 加强版)
    P1719最大加权矩形先给一个 n×n 矩阵,1<=n<=127。要求矩阵中最大加权矩形,即矩阵的每一个元素都有一权值,权值定义在整数集上。从中找一矩形,矩形大小无限制,是其中包含的所有元素的和最大。矩阵的每个元素属于 [-127,127],例如0–2–7092–62-41–41......
  • 【论术】基于t-table的表格拖拽
    在老之将至的这几年,监听员千万遍问自己:这就是我的一生吗?他又千万次回答:是的,这就是你的一生。-刘慈欣《三体》项目需要:表格中须支持拖拽功能,如图:​此功能参照了fastgpt的知识库文件对象管理,以公司主流的t-design为基础进行开发,t-table的api仅提供了dragSortAPI,即拖拽排序,无法......
  • 拖拽文件-初级程序-极语言教程
    //窗体代码:整数窗体,列表;程序资源24,"清单.xml";程序段加载窗体整数左=(桌面.宽-417)>>1,上=(桌面.高-321)>>1;窗体=创建窗口($10,程序.名称,"窗口标题",$10C80000,左,上,417,321,0,0,0,0);列表=创建窗口($200,"listbox","",$50210081,10,20,380,260,窗体,1,9,0);窗......
  • vxe-modal 实现窗口拖拽调整宽高
    vxe-modal实现窗口拖拽调整宽高官网:https://vxeui.com<template><div><vxe-buttoncontent="点击弹出"@click="showPopup=true"></vxe-button><vxe-modalv-model="showPopup"title="标题1":width=&......
  • vxe-modal 窗口组件开启拖拽移动位置功能
    官网:https://vxeui.com<template><div><vxe-buttoncontent="点击弹出"@click="showPopup=true"></vxe-button><vxe-modalv-model="showPopup":width="600":height="400">......
  • OJ题目详解——1.5~42:画矩形
    描述根据参数,画出矩形。输入输入一行,包括四个参数:前两个参数为整数,依次代表矩形的高和宽(高不少于3行不多于10行,宽不少于5列不多于10列);第三个参数是一个字符,表示用来画图的矩形符号;第四个参数为1或0,0代表空心,1代表实心。输出输出画出的图形。#include<stdio.h>intmain()......
  • Qt开发技巧(十九):定时器的调用问题,控件的透明问题,慎用事件过滤器,依赖库的路径链接,对话框
    继续讲一些Qt开发中的技巧操作:1.定时器的调用问题有一个场景经常遇到,那就是在符合某个条件下,延时一段时间去执行一段代码,如果短时间内触发多次又不需要频繁执行,只需要执行一次就行。如果选择用QTimer::singleShot无法终止已经触发的,这个时候就要主动实例化一个单次定时器,......
  • 矩形面积并 - 扫描线模板
    扫描线模板(矩形面积并)首先离散化的思想,将各个线段细分到单位长,于是就是动态求当前值域内tag\(>1\)的数量。以下是参考代码,十分优美intn,cnt;llxx[N];structScanline{lly;lllx,rx;intinout;booloperator<(constScanline&t)const{......
  • 【Unity】(2D)物体拖拽
    在2D场景中,实现将框中的物体拖拽之符合条件的物体中;应用场景:排序、物品栏、背包等;成果展示Demo中实现的效果是画面中存在4个图片,需要按照喜好程度对图片进行排序,将上面的1-2-3-4序号拖拽至对应的图片旁边。Scene部分其中Target中存放4张猫咪图片,对应的Content中的1-2-3-4是......