如何检测鼠标是否在顺时针方向移动

How to detect if mouse is moving in clockwise direction

我想在鼠标移动时在 canvas 上旋转一个形状,我想检测鼠标是否在 clockwise direction 内移动,但我不知道该怎么做。这是我的代码:

var canvas = document.getElementById('canvas');
var img = document.getElementById('photo');
var ctx = canvas.getContext('2d');

var annotation_rect = canvas.getBoundingClientRect();
rect = {
      startX : 150,
      startY : 50,
      w : 250,
      h : 150,
      endX : 0,
      endY : 0,
      rotate: 0
    };
var drag = false;
var rotating = false;
var update = true; // when true updates canvas
var rotate_angle = 5; // in degrees - for rotating blurred part
var angle = rotate_angle * (Math.PI / 180);
var original_source = img.src;
img.src = original_source;

function rotateRight(){
 rect.rotate += angle;
  update = true;
}

function rotateLeft(){
 rect.rotate -= angle;
  update = true;
}

function init() {
    img.addEventListener('load', function(){
        canvas.width = img.width;
        canvas.height = img.height;
        canvas.addEventListener('mousedown', mouseDown, false);
        canvas.addEventListener('mouseup', mouseUp, false);
        canvas.addEventListener('mousemove', mouseMove, false);
    });
    
    // start the rendering loop
    requestAnimationFrame(updateCanvas);
}

// main render loop only updates if update is true
function updateCanvas(){
  if(update){
      drawCanvas();
      update = false;
  }

  requestAnimationFrame(updateCanvas);
}

// draws a rectangle with rotation 
function drawRect(){
  ctx.setTransform(1,0,0,1,rect.startX + rect.w / 2, rect.startY + rect.h / 2);
    ctx.rotate(rect.rotate);
    ctx.beginPath();
    ctx.shadowBlur = 5;
    ctx.filter = 'blur(10px)';
    ctx.rect(-rect.w/2, -rect.h/2, rect.w, rect.h);
    ctx.lineWidth = 3;
    ctx.strokeStyle = "#fff";
    ctx.fillStyle = "#fff";
    ctx.fill();
    ctx.stroke();
}

// clears canvas sets filters and draws rectangles
function drawCanvas(){    
  ctx.setTransform(1,0,0,1,0,0);
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = 'rgba(0, 0, 0, 0.6)';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    drawRect()
}

// create new rect add to array 
function mouseDown(e) {
    drag = true;
}

function mouseUp() { drag = false; update = true; }



function startRotation(e){
    rotating = true;
}

function stopRotation(e){
    rotating = false;
}

function onShapeRotating(e){
    if(rotating){
         rotateRight();
    }
} 



function mouseMove(e) {
  var mouseX = e.offsetX - annotation_rect.left,
      mouseY = e.offsetY - annotation_rect.top,
        endX = rect.startX + rect.w,
      endY = rect.startY + rect.h
    
  var cursorOnShape = mouseX >= rect.startX && mouseX <= endX && mouseY >= rect.startY && mouseY <= endY;
    
    
    if(cursorOnShape){
        canvas.style.cursor = "pointer"
        
        canvas.addEventListener('mousedown', startRotation, false);
        canvas.addEventListener('mouseup', stopRotation, false);
        canvas.addEventListener('mousemove', onShapeRotating, false);
    }else{    
        canvas.style.cursor = "default"
        canvas.removeEventListener('mousedown', startRotation, false);
        canvas.removeEventListener('mouseup', stopRotation, false);
        canvas.removeEventListener('mousemove', onShapeRotating, false);
    }
}

init();
canvas{
  position: absolute;
  left: 0; 
  right: 0; 
  top: 0; 
  bottom: 0; 
  display:inline-block;
  background:rgba(0,0,0,0.3);
}
<div style="position: relative; overflow: hidden;display:inline-block;">
    <img id="photo" src="https://carsales.pxcrush.net/carsales/car/cil/cc5166737225893351785.jpg?width=600&height=300&overlay&aspect=FitWithIn&watermark=1439104668"/>
    <canvas id="canvas"></canvas>
</div>

因此我在 canvas 上绘制了一个矩形,我检测鼠标是否在该矩形上,如果是,我调用函数 rotateShape 并调用函数 rotateRight.那是有效的。如果你用鼠标在矩形上,你按下它并旋转它会旋转调用函数 rotateRight.

的矩形

但我希望能够检查鼠标是否顺时针方向移动。如果它顺时针方向移动,我会调用 rotateRight,如果不是,那么我会调用 rotateLeft.

知道怎么做吗?

Here is the fiddle.

好的,但不是合适的方式,抱歉:

var canvas = document.getElementById('canvas');
var img = document.getElementById('photo');
var ctx = canvas.getContext('2d');

var annotation_rect = canvas.getBoundingClientRect();
rect = {
      startX : 150,
      startY : 50,
      w : 250,
      h : 150,
      endX : 0,
      endY : 0,
      rotate: 0
    };
var drag = false;
var rotating = false;
var update = true; // when true updates canvas
var rotate_angle = 5; // in degrees - for rotating blurred part
var angle = rotate_angle * (Math.PI / 180);
var original_source = img.src;
var bLeft = true;
img.src = original_source;

function rotateRight(){
 rect.rotate += angle;
  update = true;
}

function rotateLeft(){
 rect.rotate -= angle;
  update = true;
}

function init() {
    img.addEventListener('load', function(){
        canvas.width = img.width;
        canvas.height = img.height;
        canvas.addEventListener('mousedown', mouseDown, false);
        canvas.addEventListener('mouseup', mouseUp, false);
        canvas.addEventListener('mousemove', mouseMove, false);
    });
    
    // start the rendering loop
    requestAnimationFrame(updateCanvas);
}

// main render loop only updates if update is true
function updateCanvas(){
  if(update){
      drawCanvas();
      update = false;
  }

  requestAnimationFrame(updateCanvas);
}

// draws a rectangle with rotation 
function drawRect(){
  ctx.setTransform(1,0,0,1,rect.startX + rect.w / 2, rect.startY + rect.h / 2);
    ctx.rotate(rect.rotate);
    ctx.beginPath();
    ctx.shadowBlur = 5;
    ctx.filter = 'blur(10px)';
    ctx.rect(-rect.w/2, -rect.h/2, rect.w, rect.h);
    ctx.lineWidth = 3;
    ctx.strokeStyle = "#fff";
    ctx.fillStyle = "#fff";
    ctx.fill();
    ctx.stroke();
}

// clears canvas sets filters and draws rectangles
function drawCanvas(){    
  ctx.setTransform(1,0,0,1,0,0);
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = 'rgba(0, 0, 0, 0.6)';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    drawRect()
}

// create new rect add to array 
function mouseDown(e) {
    drag = true;
}

function mouseUp() { drag = false; update = true; }



function startRotation(e){
    rotating = true;
}

function stopRotation(e){
    rotating = false;
}

function onShapeRotating(e){
    if(rotating && !bLeft){
         rotateRight();
    } else if(rotating && bLeft){
         rotateLeft();
    }
} 



function mouseMove(e) {
  var mouseX = e.offsetX - annotation_rect.left,
      mouseY = e.offsetY - annotation_rect.top,
        endX = rect.startX + rect.w,
      endY = rect.startY + rect.h
    
  var cursorOnShape = mouseX >= rect.startX && mouseX <= endX && mouseY >= rect.startY && mouseY <= endY;
    
    bLeft = ( rect.startX + rect.w/2 - mouseX ) > 0 ? true:false;
    if(cursorOnShape){
        canvas.style.cursor = "pointer"
        
        canvas.addEventListener('mousedown', startRotation, false);
        canvas.addEventListener('mouseup', stopRotation, false);
        canvas.addEventListener('mousemove', onShapeRotating, false);
    }else{    
        canvas.style.cursor = "default"
        canvas.removeEventListener('mousedown', startRotation, false);
        canvas.removeEventListener('mouseup', stopRotation, false);
        canvas.removeEventListener('mousemove', onShapeRotating, false);
    }
}

init();
canvas{
  position: absolute;
  left: 0; 
  right: 0; 
  top: 0; 
  bottom: 0; 
  display:inline-block;
  background:rgba(0,0,0,0.3);
}
<div style="position: relative; overflow: hidden;display:inline-block;">
    <img id="photo" src="https://carsales.pxcrush.net/carsales/car/cil/cc5166737225893351785.jpg?width=600&height=300&overlay&aspect=FitWithIn&watermark=1439104668"/>
    <canvas id="canvas"></canvas>
</div>

不是按固定值旋转,而是按最后已知角度与新角度之间的差值旋转。

要获得这些角度,您可以使用

var angle = Math.atan2(
  e.clientY - (rect.startY + rect.h /2),
  e.clientX - (rect.startX + rect.w /2)
);

var canvas = document.getElementById('canvas');
var img = document.getElementById('photo');
var ctx = canvas.getContext('2d');

var annotation_rect = canvas.getBoundingClientRect();
rect = {
  startX: 150,
  startY: 50,
  w: 250,
  h: 150,
  endX: 0,
  endY: 0,
  rotate: 0
};
var drag = false;
var rotating = false;
var update = true; // when true updates canvas
var original_source = img.src;
img.src = original_source;

// keep track of the last angle
var prevAngle = null;
// a single function
function rotate(angle) {
  rect.rotate += angle;
  update = true;
}
// called on mousemove when dragging
function onShapeRotating(e) {
  if (rotating) {
    var angle = Math.atan2(
      e.clientY - (rect.startY + rect.h / 2),
      e.clientX - (rect.startX + rect.w / 2)
    );
    if (prevAngle !== null)
      rotate(angle - prevAngle)
    prevAngle = angle;
  }
}

function init() {
  img.addEventListener('load', function() {
    canvas.width = img.width;
    canvas.height = img.height;
    canvas.addEventListener('mousedown', mouseDown, false);
    canvas.addEventListener('mouseup', mouseUp, false);
    canvas.addEventListener('mousemove', mouseMove, false);
  });

  // start the rendering loop
  requestAnimationFrame(updateCanvas);
}

// main render loop only updates if update is true
function updateCanvas() {
  if (update) {
    drawCanvas();
    update = false;
  }

  requestAnimationFrame(updateCanvas);
}

// draws a rectangle with rotation 
function drawRect() {
  ctx.setTransform(1, 0, 0, 1, rect.startX + rect.w / 2, rect.startY + rect.h / 2);
  ctx.rotate(rect.rotate);
  ctx.beginPath();
  ctx.shadowBlur = 5;
  ctx.filter = 'blur(10px)';
  ctx.rect(-rect.w / 2, -rect.h / 2, rect.w, rect.h);
  ctx.lineWidth = 3;
  ctx.strokeStyle = "#fff";
  ctx.fillStyle = "#fff";
  ctx.fill();
  ctx.stroke();
}

// clears canvas sets filters and draws rectangles
function drawCanvas() {
  ctx.setTransform(1, 0, 0, 1, 0, 0);
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = 'rgba(0, 0, 0, 0.6)';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  drawRect()
}

// create new rect add to array 
function mouseDown(e) {
  drag = true;
}

function mouseUp() {
  prevAngle = null;
  drag = false;
  update = true;
}



function startRotation(e) {
  rotating = true;
}

function stopRotation(e) {
  rotating = false;
}


function mouseMove(e) {
  var mouseX = e.offsetX - annotation_rect.left,
    mouseY = e.offsetY - annotation_rect.top,
    endX = rect.startX + rect.w,
    endY = rect.startY + rect.h

  var cursorOnShape = mouseX >= rect.startX && mouseX <= endX && mouseY >= rect.startY && mouseY <= endY;


  if (cursorOnShape) {
    canvas.style.cursor = "pointer"

    canvas.addEventListener('mousedown', startRotation, false);
    canvas.addEventListener('mouseup', stopRotation, false);
    canvas.addEventListener('mousemove', onShapeRotating, false);
  } else {
    canvas.style.cursor = "default"
    canvas.removeEventListener('mousedown', startRotation, false);
    canvas.removeEventListener('mouseup', stopRotation, false);
    canvas.removeEventListener('mousemove', onShapeRotating, false);
  }
}

init();
canvas {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  display: inline-block;
  background: rgba(0, 0, 0, 0.3);
}
<div style="position: relative; overflow: hidden;display:inline-block;">
  <img id="photo" src="https://carsales.pxcrush.net/carsales/car/cil/cc5166737225893351785.jpg?width=600&height=300&overlay&aspect=FitWithIn&watermark=1439104668" />
  <canvas id="canvas"></canvas>
</div>


现在,您的代码有很多问题需要修复,但我会交给您。

你最好在整体上有单个事件 canvas/document 而不是有选择地添加。
您的 cursorOnShape 算法没有考虑矩形的旋转。

计算角度变化

使用 Math.atan2() 的问题是 Math.atan2(1,-10)Math.atan2(-1,-10) 之间的差异是 6.084 而不是 0.199 这意味着当你累积差异的总和时,你永远不会得到超过的值Math.PI-Math.PI

以下

换句话说,你不能直接追踪你转了多少圈,也不容易知道你是顺时针转还是逆时针转。

叉积

计算鼠标围绕一个点旋转的方向和数量的最佳方法是使用从中心到鼠标位置和旧位置的两个向量的叉积

// cx,cy center of rotation
// ox,oy old position of mouse
// mx,my new position of mouse.
function getAngle(cx, cy, ox, oy, mx, my){
    var x1 = ox - cx;
    var y1 = oy - cy;
    var x2 = mx - cx;
    var y2 = my - cy;
    var d1 = Math.sqrt(x1 * x1 + y1 * y1);
    var d2 = Math.sqrt(x2 * x2 + y2 * y2);

    return Math.asin((x1 / d1) * (y2 / d2) - (y1 / d1) * (x2 / d2));
}

函数将 return 逆时针方向角度负变化,顺时针角度正方向变化。

例子

示例显示上述函数用于跟踪总旋转,而不是像其他答案那样跟踪绝对方向。

const mouse  = {x : 0, y : 0, ox : 0, oy : 0, down : false};
["down","up","move"].forEach(name => document.addEventListener("mouse" + name,mouseEvents));
const ctx = canvas.getContext("2d");

function mouseEvents(e) {
  mouse.x = e.pageX; mouse.y = e.pageY;
  mouse.down = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.down;
}

function getAngleBetween(cx, cy, ox, oy, mx, my) {
  var x1 = ox - cx;
  var y1 = oy - cy;
  var x2 = mx - cx;
  var y2 = my - cy;
  // max to prevent div by zero
  var d1 = Math.max(0.001, Math.sqrt(x1 * x1 + y1 * y1));
  var d2 = Math.max(0.001, Math.sqrt(x2 * x2 + y2 * y2));
  return Math.asin((x1 / d1) * (y2 / d2) - (y1 / d1) * (x2 / d2));
}
var w, h, cw, ch;
var angle = 0;
function update() {
  ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
  if (w !== innerWidth || h !== innerHeight) {
    cw = (w = canvas.width = innerWidth) / 2;
    ch = (h = canvas.height = innerHeight) / 2;
  } else {
    ctx.clearRect(0, 0, w, h);
  }
  var change = 0;
  if (mouse.down) {
    change = getAngleBetween(cw, ch, mouse.ox, mouse.oy, mouse.x, mouse.y);
    angle += change;
  }

  ctx.setTransform(1, 0, 0, 1, cw, ch);
  ctx.rotate(angle);
  ctx.fillRect(-100, -25, 200, 50);


  ctx.setTransform(1, 0, 0, 1, 0, 0);
  ctx.font = "28px arial";
  ctx.textAlign = "center";
  ctx.fillText("Total rotation : " + ((angle * 180 / Math.PI) | 0), cw, 30);
  ctx.font = "14px arial";
  ctx.fillText("Change : " + (change * 180 / Math.PI).toFixed(2), cw, 48);

  ctx.fillText("Click drag to rotate box.", cw, h - 20);

  mouse.ox = mouse.x;
  mouse.oy = mouse.y
  requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas {
  position: absolute;
  top: 0px;
  left: 0px;
}
<canvas id="canvas"></canvas>