使用 ctx.getTransformation() 获取变换后的画布鼠标坐标
Posted
技术标签:
【中文标题】使用 ctx.getTransformation() 获取变换后的画布鼠标坐标【英文标题】:get canvas mouse coordinates after transformation using ctx.getTransformation() 【发布时间】:2020-04-02 08:08:43 【问题描述】:执行旋转后,我正在使用以下函数在画布上获取鼠标坐标。
function getWindowToCanvas(canvas, x, y)
const ctx = canvas.getContext("2d");
var transform = ctx.getTransform();
var rect = canvas.getBoundingClientRect();
var screenX = (x - rect.left) * (canvas.width / rect.width);
var screenY = (y - rect.top) * (canvas.height / rect.height);
if (transform.isIdentity)
return
x: screenX,
y: screenY
;
else
console.log(transform.invertSelf());
const invMat = transform.invertSelf();
return
x: Math.round(screenX * invMat.a + screenY * invMat.c + invMat.e),
y: Math.round(screenX * invMat.b + screenY * invMat.d + invMat.f)
;
阅读html5-canvas-transformation-algorithm和best-way-to-transform-mouse-coordinates-to-html5-canvass-transformed-context后我使用了逆变换矩阵
我让用户用鼠标绘制矩形,我需要在转换后获取鼠标的 x,y 坐标,但是一旦画布旋转(比如 90 度),矩形就不再跟随鼠标指针。
有谁知道我做错了什么?
【问题讨论】:
你画的部分在哪里?如果您实际上使坐标相对于整个窗口,则跟踪鼠标的方法不会发生意外行为 嗨 Youssef,我正在尝试找到一种更好的方法来制作相对于整个窗口的坐标,但我不知道比var rect = canvas.getBoundingClientRect();
然后var relativeToWindowX = (event.clientX - rect.left) * (canvas.width / rect.width)
更好的方法
event.clientX 来自于用户在画布上单击和拖动时画布的 onmousedown 和 onmousemove 事件。
一次做一步。查看jsfiddle.net/60map523 将鼠标点击转换为画布坐标。第二步是将画布坐标转换为转换后的坐标。
您“真正”想要做什么?你能建立一个完整的例子吗?听起来会有更简单的方法,例如在绘制之前将转换重置为身份,甚至将转换存储在每个对象中:jsfiddle.net/en8df6o0/2
【参考方案1】:
感谢@MarkusJarderot 和jsFiddle getting mouse coordinates from un-rotated canvas,我能够得到一个接近 完美的解决方案。我不太明白它,但它工作得更好。
function getWindowToCanvas(canvas, e)
//first calculate normal mouse coordinates
e = e || window.event;
var target = e.target || e.srcElement,
style = target.currentStyle || window.getComputedStyle(target, null),
borderLeftWidth = parseInt(style["borderLeftWidth"], 10),
borderTopWidth = parseInt(style["borderTopWidth"], 10),
rect = target.getBoundingClientRect(),
offsetX = e.clientX - borderLeftWidth - rect.left,
offsetY = e.clientY - borderTopWidth - rect.top;
let x = (offsetX * target.width) / target.clientWidth;
let y = (offsetY * target.height) / target.clientHeight;
//then adjust coordinates for the context's transformations
const ctx = canvas.getContext("2d");
var transform = ctx.getTransform();
const invMat = transform.invertSelf();
return
x: x * invMat.a + y * invMat.c + invMat.e,
y: x * invMat.b + y * invMat.d + invMat.f
;
剩下的唯一问题是,当旋转 45 度时,使用 ctx.rect() 绘制一个矩形会绘制一个与画布平行的矩形,而不是与窗口平行的矩形,因此即使最终矩形也是倾斜的在正确的地方。我想相对于窗口而不是画布绘制矩形。但是,这可能就是 ctx.rect() 的工作方式,我稍后需要更新。目前,这可以帮助其他人。
更新
找出原始错误。
由于我不明白为什么我的原始功能不起作用,因此使用上述解决方案开始对其进行故障排除。事实证明,上述代码不起作用的原因是因为我在调试时调用console.log(transform.invertSelf())
来查看转换。这改变了变换。所以,当我马上打电话给var invMat = transform.invertSelf()
时,我又把它倒过来了!我应该注意'invertSelf'中的'self'。
这个功能现在可以使用了
function getWindowToCanvas(canvas, x, y)
var rect = canvas.getBoundingClientRect();
var screenX = (x - rect.left) * (canvas.width / rect.width);
var screenY = (y - rect.top) * (canvas.height / rect.height);
const ctx = canvas.getContext("2d");
var transform = ctx.getTransform();
if (transform.isIdentity)
return
x: screenX,
y: screenY
;
else
// console.log(transform.invertSelf()); //don't invert twice!!
const invMat = transform.invertSelf();
return
x: Math.round(screenX * invMat.a + screenY * invMat.c + invMat.e),
y: Math.round(screenX * invMat.b + screenY * invMat.d + invMat.f)
;
【讨论】:
大声笑你没有在问题中提到它,但是js的问题之一是调试它,最好使用debugger关键字而不是console.log更有效,它会限制数量您应该清理的代码,而不是多个 console.log以上是关于使用 ctx.getTransformation() 获取变换后的画布鼠标坐标的主要内容,如果未能解决你的问题,请参考以下文章
在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?
Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)