我试图在javascript中使用箭头指向我的鼠标光标。现在它只是猛烈旋转,而不是指向光标。
以下是我的代码的小提琴:https://jsfiddle.net/pk1w095s/
以下是自己的代码:
var cv = document.createElement('canvas');
cv.width = 1224;
cv.height = 768;
document.body.appendChild(cv);
var rotA = 0;
var ctx = cv.getContext('2d');
var arrow = new Image();
var cache;
arrow.onload = function() {
cache = this;
ctx.drawImage(arrow, cache.width/2, cache.height/2);
};
arrow.src = 'https://d30y9cdsu7xlg0.cloudfront.net/png/35-200.png';
var cursorX;
var cursorY;
document.onmousemove = function(e) {
cursorX = e.pageX;
cursorY = e.pageY;
ctx.save(); //saves the state of canvas
ctx.clearRect(0, 0, cv.width, cv.height); //clear the canvas
ctx.translate(cache.width, cache.height); //let's translate
var centerX = cache.x + cache.width / 2;
var centerY = cache.y + cache.height / 2;
var angle = Math.atan2(e.pageX - centerX, -(e.pageY - centerY)) * (180 / Math.PI);
ctx.rotate(angle);
ctx.drawImage(arrow, -cache.width / 2, -cache.height / 2, cache.width, cache.height); //draw the image
ctx.restore(); //restore the state of canvas
};
答案 0 :(得分:7)
在第一个例子中,摆脱转换为度数 - Math.atan2
和ctx.rotate
函数都采用弧度。
修复了疯狂旋转 - 你仍然有一些数学错误,最容易通过从数学中分割出绘图来整理出来。
下面的函数绘制旋转给定角度的箭头:
// NB: canvas rotations go clockwise
function drawArrow(angle) {
ctx.clearRect(0, 0, cv.width, cv.height);
ctx.save();
ctx.translate(centerX, centerY);
ctx.rotate(-Math.PI / 2); // correction for image starting position
ctx.rotate(angle);
ctx.drawImage(arrow, -arrow.width / 2, -arrow.height / 2);
ctx.restore();
}
并且onmove
处理程序只是指出方向。
document.onmousemove = function(e) {
var dx = e.pageX - centerX;
var dy = e.pageY - centerY;
var theta = Math.atan2(dy, dx);
drawArrow(theta);
};
请注意,在画布上,Y轴指向下方(与正常的笛卡尔坐标相反),因此旋转最终顺时针方向而不是逆时针方向。
的工作演示答案 1 :(得分:3)
作为,现有的(Alnitak's)答案存在一些问题。
pageX
,pageY
属性相对于页面的左上角而不是整个文档。如果滚动页面,则箭头将不再指向鼠标。或者您可以使用鼠标事件clientX,clientY属性将鼠标坐标保持在客户端(整个)页面左上方,因此您无需更正滚动。这是一个“最佳实践”解决方案。
核心功能绘制图像,查看点lookx
,looky
var drawImageLookat(img, x, y, lookx, looky){
ctx.setTransform(1, 0, 0, 1, x, y); // set scale and origin
ctx.rotate(Math.atan2(looky - y, lookx - x)); // set angle
ctx.drawImage(img,-img.width / 2, -img.height / 2); // draw image
ctx.setTransform(1, 0, 0, 1, 0, 0); // restore default not needed if you use setTransform for other rendering operations
}
该演示展示如何使用requestAnimationFrame
确保仅在DOM准备好渲染时进行渲染,使用getBoundingClientRect
获取相对于画布的鼠标位置。
左上方的计数器显示已触发的不需要渲染的鼠标事件数。非常慢地移动鼠标,计数器不会增加。以正常速度移动鼠标,您将看到每隔几秒就可以生成100个不需要的渲染事件。第二个数字是以1/1000秒保存的大致时间,%是随时间保存的比率时间。
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width = 512;
canvas.height = 512;
canvas.style.border = "1px solid black";
document.body.appendChild(canvas);
var renderSaveCount = 0; // Counts the number of mouse events that we did not have to render the whole scene
var arrow = {
x : 256,
y : 156,
image : new Image()
};
var mouse = {
x : null,
y : null,
changed : false,
changeCount : 0,
}
arrow.image.src = 'https://d30y9cdsu7xlg0.cloudfront.net/png/35-200.png';
function drawImageLookat(img, x, y, lookx, looky){
ctx.setTransform(1, 0, 0, 1, x, y);
ctx.rotate(Math.atan2(looky - y, lookx - x) - Math.PI / 2); // Adjust image 90 degree anti clockwise (PI/2) because the image is pointing in the wrong direction.
ctx.drawImage(img, -img.width / 2, -img.height / 2);
ctx.setTransform(1, 0, 0, 1, 0, 0); // restore default not needed if you use setTransform for other rendering operations
}
function drawCrossHair(x,y,color){
ctx.strokeStyle = color;
ctx.beginPath();
ctx.moveTo(x - 10, y);
ctx.lineTo(x + 10, y);
ctx.moveTo(x, y - 10);
ctx.lineTo(x, y + 10);
ctx.stroke();
}
function mouseEvent(e) { // get the mouse coordinates relative to the canvas top left
var bounds = canvas.getBoundingClientRect();
mouse.x = e.pageX - bounds.left;
mouse.y = e.pageY - bounds.top;
mouse.cx = e.clientX - bounds.left; // to compare the difference between client and page coordinates
mouse.cy = e.clienY - bounds.top;
mouse.changed = true;
mouse.changeCount += 1;
}
document.addEventListener("mousemove",mouseEvent);
var renderTimeTotal = 0;
var renderCount = 0;
ctx.font = "18px arial";
ctx.lineWidth = 1;
// only render when the DOM is ready to display the mouse position
function update(){
if(arrow.image.complete && mouse.changed){ // only render when image ready and mouse moved
var now = performance.now();
mouse.changed = false; // flag that the mouse coords have been rendered
ctx.clearRect(0, 0, canvas.width, canvas.height);
// get mouse canvas coordinate correcting for page scroll
var x = mouse.x - scrollX;
var y = mouse.y - scrollY;
drawImageLookat(arrow.image, arrow.x, arrow.y, x ,y);
// Draw mouse at its canvas position
drawCrossHair(x,y,"black");
// draw mouse event client coordinates on canvas
drawCrossHair(mouse.cx,mouse.cy,"rgba(255,100,100,0.2)");
// draw line from arrow center to mouse to check alignment is perfect
ctx.strokeStyle = "black";
ctx.beginPath();
ctx.globalAlpha = 0.2;
ctx.moveTo(arrow.x, arrow.y);
ctx.lineTo(x, y);
ctx.stroke();
ctx.globalAlpha = 1;
// Display how many renders that were not drawn and approx how much time saved (excludes DOM time to present canvas to display)
renderSaveCount += mouse.changeCount -1;
mouse.changeCount = 0;
var timeSaved = ((renderTimeTotal / renderCount) * renderSaveCount);
var timeRatio = ((timeSaved / renderTimeTotal) * 100).toFixed(0);
ctx.fillText("Avoided "+ renderSaveCount + " needless renders. Saving ~" + timeSaved.toFixed(0) +"ms " + timeRatio + "% .",10,20);
// get approx render time per frame
renderTimeTotal += performance.now()-now;
renderCount += 1;
}
requestAnimationFrame(update);
}
requestAnimationFrame(update);