我正在开发用于在谷歌地图上定位图像的用户界面。 我从http://overlay-tiler.googlecode.com/svn/trunk/upload.html开始,这非常接近我想要的。
但是我想要一个旋转工具,一个缩放工具和一个翻译工具(后者存在),而不是3个接触点。
我尝试添加旋转工具,但它无法正常工作:
我在左下角放了一个控制旋转的点(围绕图像中心)。鼠标拖动控制点,我计算其他3个点。
我的代码基于移动器对象,但我更改了onMouseMove函数:
overlaytiler.Rotater.prototype.rotateDot_ = function(dot, theta, origin) {
dot.x = ((dot.x - origin.x) * Math.cos(theta) - (dot.y - origin.y) * Math.sin(theta)) + origin.x;
dot.y = ((dot.x - origin.x) * Math.sin(theta) + (dot.y - origin.y) * Math.cos(theta)) + origin.y;
dot.render();
};
overlaytiler.Rotater.prototype.onMouseMove_ = function(e) {
var dots = this.controlDots_;
var center = overlaytiler.Rotater.prototype.getCenter_(dots);
// Diagonal length
var r = Math.sqrt(Math.pow(this.x - center.x, 2) + Math.pow(this.y - center.y, 2));
var old = {
x: this.x,
y: this.y
};
// Real position
var newPos = {
x: this.x + e.clientX - this.cx,
y: this.y + e.clientY - this.cy
}
var newR = Math.sqrt(Math.pow(newPos.x - center.x, 2) + Math.pow(newPos.y - center.y, 2));
var theta = - Math.acos((2 * r * r - (Math.pow(newPos.x - old.x, 2) + Math.pow(newPos.y - old.y, 2))) / (2 * r * r));
// Fixed distance position
this.x = (newPos.x - center.x) * (r / newR) + center.x;
this.y = (newPos.y - center.y) * (r / newR) + center.y;
dots[1].x = center.x + (center.x - this.x);
dots[1].y = center.y + (center.y - this.y);
dots[1].render();
overlaytiler.Rotater.prototype.rotateDot_(dots[2], theta, center);
overlaytiler.Rotater.prototype.rotateDot_(dots[0], theta, center);
// Render
this.render();
this.cx = e.clientX;
this.cy = e.clientY;
};
不幸的是,精度和角度符号存在问题。
几次旋转后,图像高度失真,仅在一个方向上支持旋转。
我想知道如何才能达到很高的精度并且没有任何失真。
也许我的方法在这里没用(尝试在正确的坐标处移动角落),我试图用画布旋转图像,但我的尝试都没有成功。
修改:完整工作版本:http://jsbin.com/iQEbIzo/7/
答案 0 :(得分:2)
仅在一个方向支持旋转
这是由于你如何计算两个向量之间的角度。 无论鼠标是否正确,它总能为您提供相同的矢量。我在german math board找到了一个解决方案(很遗憾,我无法使用Google的缓存访问该网站:cached version)。
请注意,在此示例中,角度α在两侧相同,而不是您在第二个角度中所期望的-α。要确定向量a是否始终位于向量b的“同一侧”,您可以使用此公式。
ax*by - ay*bx
这是积极的还是消极的。您只需将角度符号更改为α * -1
。
我修改了代码的某些部分。
overlaytiler.Rotater.prototype.rotateDot_ = function(dot, theta, origin) {
// translate to origin
dot.x -= origin.x ;
dot.y -= origin.y ;
// perform rotation
newPos = {
x: dot.x*Math.cos(theta) - dot.y*Math.sin(theta),
y: dot.x*Math.sin(theta) + dot.y*Math.cos(theta)
} ;
dot.x = newPos.x ;
dot.y = newPos.y ;
// translate back to center
dot.x += origin.x ;
dot.y += origin.y ;
dot.render();
};
如果您想了解我如何轮换积分,请参考this site和this one。
overlaytiler.Rotater.prototype.onMouseMove_ = function(e) {
var dots = this.controlDots_;
var center = overlaytiler.Rotater.prototype.getCenter_(dots);
// get the location of the canvas relative to the screen
var rect = new Array() ;
rect[0] = dots[0].canvas_.getBoundingClientRect() ;
rect[1] = dots[1].canvas_.getBoundingClientRect() ;
rect[2] = dots[2].canvas_.getBoundingClientRect() ;
// calculate the relative center of the image
var relCenter = {
x: (rect[0].left + rect[2].left) / 2,
y: (rect[0].top + rect[2].top) / 2
} ;
// calculate a vector from the center to the bottom left of the image
dotCorner = {
x: rect[1].left - (rect[1].left - relCenter.x) * 2 - relCenter.x,
y: rect[1].top - (rect[1].top - relCenter.y) * 2 - relCenter.y
} ;
// calculate a vector from the center to the mouse position
mousePos = {
x: e.clientX - relCenter.x,
y: e.clientY - relCenter.y
} ;
// calculate the angle between the two vector
theta = calculateAngle(dotCorner, mousePos) ;
// is the mouse-vector left of the dot-vector -> refer to the german math board
if(dotCorner.y*mousePos.x - dotCorner.x*mousePos.y > 0) {
theta *= -1 ;
}
// calculate new position of the dots and render them
overlaytiler.Rotater.prototype.rotateDot_(dots[2], theta, center);
overlaytiler.Rotater.prototype.rotateDot_(dots[1], theta, center);
overlaytiler.Rotater.prototype.rotateDot_(dots[0], theta, center);
// Render
this.render();
this.cx = e.clientX;
this.cy = e.clientY;
};
你可以看到我为矢量计算编写了一些函数(只是为了让代码更具可读性):
function calculateScalarProduct(v1,v2)
{
return (v1.x * v2.x + v1.y * v2.y) ;
}
function calculateLength(v1)
{
return (Math.sqrt(v1.x*v1.x + v1.y*v1.y)) ;
}
function calculateAngle(v1, v2)
{
return (Math.acos(calculateScalarProduct(v1,v2) / (calculateLength(v1)*calculateLength(v2)))) ;
}
这是我的工作解决方案。评论如果你不理解某些东西,那么我可以使我的答案更全面。
工作示例:JSBin
哇,这是一个艰难的。答案 1 :(得分:2)
这是我的版本。 @efux和@Ben的答案要完整得多,设计得很好,但放大/缩小时地图不会缩放。叠加很可能需要这样做,因为它们用于在现有地图上放置“第二张地图”或照片。
这是JSFiddle:http://jsfiddle.net/adelriosantiago/3tzzwmsx/4/
执行绘图的代码如下:
DebugOverlay.prototype.draw = function() {
var overlayProjection = this.getProjection();
var sw = overlayProjection.fromLatLngToDivPixel(this.bounds_.getSouthWest());
var ne = overlayProjection.fromLatLngToDivPixel(this.bounds_.getNorthEast());
var div = this.div_;
div.style.left = sw.x + 'px';
div.style.top = ne.y + 'px';
div.style.width = (ne.x - sw.x) + 'px';
div.style.height = (sw.y - ne.y) + 'px';
div.style.transform = 'rotate(' + rot + 'deg)';
};
当然,如果需要,可以在efux和Ben代码上实现此代码,但我还没有尝试过。
请注意,当旋转标记移动时,框标记不会更新其位置...