【问题标题】:How to detect if mouse is moving in clockwise direction如何检测鼠标是否沿顺时针方向移动
【发布时间】:2018-03-07 08:28:16
【问题描述】:

我想在鼠标移动时在画布上旋转一个形状,我想检测鼠标是否在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>

因此我在画布上绘制了一个矩形,我检测鼠标是否在该矩形上,如果是,我调用函数rotateShape,然后调用函数rotateRight。这是有效的。如果您使用鼠标在矩形上并按下并旋转它会旋转矩形调用函数rotateRight

但我希望能够检查鼠标是否顺时针方向移动。如果它顺时针方向移动,我会打电话给rotateRight,如果不是,我会打电话给rotateLeft

知道怎么做吗?

Here is the fiddle.

【问题讨论】:

  • var 弧度 = Math.atan2(mouse_x - center_x, mouse_y - center_y);我的意思是使用你的对象的这个中心。
  • @nullqube 你能把它应用在小提琴里吗?
  • @nullqube 这不是真的。小提琴工作正常。我有更多的代码,但我试图让小提琴更简单。显然你没看懂。

标签: javascript canvas rotation html5-canvas


【解决方案1】:

计算角度变化

使用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));
}

如果逆时针,该函数将返回一个负的角度变化,如果顺时针,该函数将返回一个正值。

示例

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

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;
}
&lt;canvas id="canvas"&gt;&lt;/canvas&gt;

【讨论】:

    【解决方案2】:

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

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

    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>

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

    最好在整个画布/文档上设置单个事件,而不是选择性地添加事件。
    您的 cursorOnShape 算法没有考虑矩形的旋转。

    【讨论】:

    【解决方案3】:

    好的,但不是合适的方式,对不起:

    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>

    【讨论】:

    • 因为你做了状态机代码,所以我只是添加了一个 bLeft 变量来跟踪方向。但重点是计算鼠标位置与您尝试旋转的形状中心之间的差异。
    猜你喜欢
    • 2010-10-13
    • 1970-01-01
    • 1970-01-01
    • 2013-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多