任务需求
鼠标点击,对应path出现边框,通过鼠标旋转path。(因为最后需要记录所有点的坐标,所以不能只使用transform。)
基本思路
- 找出path的旋转中心的坐标(x,y)
- 获取鼠标关于旋转中心移动的角度
- 通过tansform旋转path(未改变pathlist中的坐标)
- 鼠标松开之后记录当下的角度,然后对当前path进行矩阵变化(改变pathlist中坐标)
预备知识
- 如何计算path的旋转中心?
使用getBBox()可以获取元素的边界,会返回一个对象{x,y,width,height},旋转中心就是该矩阵的中心(对角线交叉的中心)
let svg = document.getElementsByTagName('svg')[0];//父元素
let path1 = document.getElementById('path');//获取子元素
let svgBox = path1.getBBox();//计算边界 会返回一个对象 x y width height
let rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");//创建一个rect
//设置相应属性
rect.setAttribute('x',svgBox.x);
rect.setAttribute('y',svgBox.y);
rect.setAttribute('width',svgBox.width);
rect.setAttribute('height',svgBox.height);
rect.setAttribute('style','fill:none;stroke:pink;stroke-width:3;stroke-dasharray:5');
//增加到页面上
svg.appendChild(rect);
//计算旋转中心
let x = svgBox.x + (svgBox.width >> 1);
let y = svgBox.y + (svgBox.height >> 1);
cirx = x;
ciry = y;
let point = document.createElementNS("http://www.w3.org/2000/svg","circle");
point.setAttribute('cx',x);
point.setAttribute('cy',y);
point.setAttribute('r',2);
point.setAttribute('style','fill:pink');
svg.appendChild(point);
- 计算鼠标移动的角度?
可以获得三个点 旋转中心(cirx,ciry) 鼠标初始点(prex,prey) 鼠标当前点(nowx,nowy)
//计算向量之间的夹角
const getAngle = ({ x: x1, y: y1 }, { x: x2, y: y2 }) => {
const dot = x1 * x2 + y1 * y2
const det = x1 * y2 - y1 * x2
const angle = Math.atan2(det, dot) / Math.PI * 180
return (angle + 360) % 360
}
let angle = getAngle({
x: prex - cirx,
y: prey - ciry,
}, {
x: nowx - cirx,
y: nowy - ciry,
});
- 获取transform变化后的坐标
方法1:
//获取变换矩阵,并且把原路径的每个点都变化了
let ctm = path1.getScreenCTM();
var rootCTM = svg.getScreenCTM();
var pt1 = svg.createSVGPoint();
pt1.x = 100;
pt1.y = 100;
//pt1是旧的 pt2是新的
let pt2 = pt1.matrixTransform(rootCTM.inverse().multiply(ctm));
方法2:
let l = ((angle * Math.PI) / 180);
let cosv = Math.cos(l);
let sinv = Math.sin(l);
let pt3 = {x:-1,y:-1};
pt3.x = ((pt1.x - cirx) * cosv - (pt1.y - ciry) * sinv + cirx);
pt3.y = ((pt1.x - cirx) * sinv + (pt1.y - ciry) * cosv + ciry);
console.log("手动计算"+pt3.x+" "+pt3.y);
一些小的坑点
通过getBBox()获取的边界并不会随着path的旋转而旋转,所以需要在border和path上同时加上rotate,在鼠标松开的时候,计算path的真实坐标,然后把rotate置为0,如果这个时候还沿用之前getBBox获得边界那么,边界就会恢复成原来没有旋转的样子(因为rotate为0)。
所以不使用rect来表示边界,而是使用子一个自定义d的path来描绘边界,在鼠标松开时同时计算边界旋转后的新坐标,这样就可以保持在点击下一个path之前,边框也保持正常。
现在的版本还有些Bug,就是当path的strokeWidth变化时,边框不会变化,所以可能会有部分path在边框之外。这个问题等之后想好方法之后在解决吧。。。。