Javascript使图像旋转以始终查看鼠标光标?

我试图用箭头指向我的鼠标光标在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 = 'http://sofzh.miximages.com/javascript/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 }; 

“最佳实践”解决方案。

由于现有的 (Alnitak’s)答案存在一些问题。

  • 错误的登录计算,然后太多的调整,以纠正错误的标志。
  • 箭头未指向鼠标,因为鼠标坐标不正确。 尝试将鼠标移动到箭头的尖端( 接受 (Alnitak)的答案),你可以看到它只能在canvas上的两个点上运行。 需要针对canvas填充/偏移校正鼠标
  • canvas坐标需要包含页面滚动位置,因为鼠标事件pageXpageY属性是相对于页面的左上角,而不是整个文档。 如果滚动页面,则箭头将不再指向鼠标。 或者您可以使用鼠标事件clientX,clientY属性将鼠标坐标保持在客户端(整个)页面左上方,因此您无需更正滚动。
  • 使用保存和恢复效率很低。 使用setTransform
  • 不需要时渲染。 鼠标比屏幕刷新时间多了很多。 鼠标触发时的渲染将只会导致从未见过的渲染。 渲染在处理和电力使用方面都很昂贵。 无需渲染会很快耗尽设备的电池电量

这是一个“最佳实践”解决方案。

核心function绘制一个图像,看着一个lookx ,看起来像

 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来获取相对于canvas的鼠标位置。

左上角的计数器显示了不需要渲染的已触发的鼠标事件数。 非常缓慢地移动鼠标,计数器不会增加。 以正常速度移动鼠标,您将看到每隔几秒就可以生成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 = 'http://sofzh.miximages.com/javascript/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); 

在第一个例子中,摆脱转换为度 – Math.atan2ctx.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); }; 

请注意,在canvas上,Y轴指向下方(与正常的笛卡尔坐标相反),因此旋转最终顺时针而不是逆时针方向。

工作演示在https://jsfiddle.net/alnitak/5vp0syn5/