获得更好阻力效果的正确偏移量是多少?

What is the right offset for better drag effect?

我正在尝试使我的云可拖动。它有效,但从我的示例中可以看出,云总是从中心剪到鼠标位置。

这是我当前的设置。

$(document).ready(function () {

    var canvas = document.getElementById("background-canvas");
    canvas.width = $(window).width();
    canvas.height = $(window).height();
    canvas.style.zIndex = -1;
    var ctx = canvas.getContext("2d");
 
 var mousePosition = new Vector2d(0,0);
 
    var background = new Background(ctx, canvas.width, canvas.height, new Color(224,247,250,0.8));
 var cloud = new Cloud(background, 300, 100, new Vector2d(10,10), 20, 1000);
 img=new Image();
    img.src="https://i.imgur.com/hIVsoho.png";

 background.addCloud(cloud);
 
 for (var i = 0; i < background.allClouds.length; i++){
  var selectedCloud = background.allClouds[i];
  for (var j = 0; j < selectedCloud.maxNumberofPixels; j++){
   var pixel = cloud.createPixel();  // TODO: cloud shall not define pixel.
   //new Pixel(2, 4, getRandomLocationWithinParent(selectedCloud), new Vector2d(0,5), new Vector2d(0,0), new Color(0,0,128,1));
   cloud.addPixel(pixel);
  }
 }
 /*
 * Input listeners
 */
 document.addEventListener("mousemove", function (evt) {
        mousePosition = getMousePos(canvas, evt);
    }, false);
 document.addEventListener("mousedown", function (evt){
  console.log(mousePosition);
  for(var i = 0; i < background.allClouds.length; i++)
   if(background.allClouds[i].hover(mousePosition))
    background.allClouds[i].isClicked = true;
 }, false)
 document.addEventListener("mouseup", function (evt){
  for(var i = 0; i < background.allClouds.length; i++)
   if(background.allClouds[i].hover(mousePosition))
    background.allClouds[i].isClicked = false;
 }, false)
 
 
 setInterval(updateBackground, 20);  
 
 function updateBackground() {
  // paint background color.
  ctx.fillStyle = background.color.getColorString();
  ctx.fillRect(0,0,background.width, background.height);
  
  // paint clouds
  for(var i = 0; i < background.allClouds.length; i++){
   var selectedCloud = background.allClouds[i];
   //ctx.fillStyle = selectedCloud.color.getColorString();    
   //ctx.fillRect(0, 0, selectedCloud.width, selectedCloud.height); rectangle view of cloud.
   
   // paint rain
   var deadPixelContainer = [];
   for (var j = 0; j < selectedCloud.allPixels.length; j++){
    var selectedPixel = selectedCloud.allPixels[j];
    ctx.fillStyle = selectedPixel.color.getColorString();
    ctx.save();
                ctx.translate(selectedPixel.location.x, selectedPixel.location.y);
                ctx.fillRect(-selectedPixel.width / 2, -selectedPixel.height / 2, selectedPixel.width, selectedPixel.height);
                ctx.restore();
    if(!selectedPixel.alive){
     deadPixelContainer.push(selectedPixel);
     continue;
    }
    selectedPixel.update();
    selectedPixel.checkEdges(background);
    
   }
   if(deadPixelContainer.length > 0){
    selectedCloud.removePixels(deadPixelContainer);
   }
   ctx.save();
   ctx.translate(selectedCloud.location.x, selectedCloud.location.y);
   ctx.drawImage(img,0,0,img.width,img.height,-25, -10,350,100);
   ctx.restore();
   cloud.update(mousePosition);
  }
  
 }
 // TODO: Create object for mouse
 function getMousePos(canvas, evt) {
        var rect = canvas.getBoundingClientRect();
        return new Vector2d(evt.clientX - rect.left, evt.clientY - rect.top);
    }
 
});


function Cloud(background, width, height, location, startNumberOfPixels, maxNumberofPixels){
 this.width = width;
 this.height = height;
 this.location = location;
 this.allPixels = [];
 this.maxNumberofPixels = maxNumberofPixels;
 this.color = new Color(255,255,255,0.5);
 this.isClicked = false;
 this.rainStrength = 5;  // how often cloud spawns new pixels per update cycle.
 this.addPixel = function(pixel){
  if(this.allPixels.length <= startNumberOfPixels)
   this.allPixels.push(pixel);
 }
 this.update = function(mousePosition){
  // make cloud draggable
  if(this.isClicked){
   var offsetX = mousePosition.x - this.location.x;
   var offsetY = mousePosition.y - this.location.y;
   
   this.location = new Vector2d(this.location.x + offsetX - this.width/2, this.location.y + offsetY - this.height/2);
  }
  // add more pixels overtime.
  if(this.allPixels.length <= this.maxNumberofPixels)
   for(var i = 0; i < this.rainStrength; i++)
    this.allPixels.push(this.createPixel());
 }
 this.hover = function(mousePosition){
  if(mousePosition.x > this.location.x 
  && mousePosition.x < this.location.x + this.width
  && mousePosition.y > this.location.y
  && mousePosition.y < this.location.y + this.height)
   return true;
  return false;
 }
 this.createPixel = function(){
  return new Pixel(2, 4, this.getRandomLocation(), new Vector2d(0,7), new Vector2d(0,0.05), new Color(0,0,128,1));
 }
 this.removePixels = function(deadPixelContainer){
  for(var i = 0; i < deadPixelContainer.length; i++){
   try{
    var pixelContainer = this.allPixels.slice();
    pixelContainer.splice(this.allPixels.findIndex(v => v === deadPixelContainer[i]), 1).slice();
    this.allPixels = pixelContainer.slice();
   }catch(e){
    console.log(e);
   }
  }
 }
 this.getRandomLocation = function(){
  var minWidth = this.location.x;
  var maxWidth = this.location.x + this.width;
  var minHeight = this.location.y + this.height/2; // don't count upper part of cloud. Rain forms at the bottom.
  var maxHeight = this.location.y + this.height;
  var randomWidthLocation = Math.random() * (maxWidth - minWidth + 1)+minWidth;
  var randomHeightLocation = Math.random() * (maxHeight - minHeight + 1) + minHeight;
  return new Vector2d(randomWidthLocation, randomHeightLocation); 
 }
}

function Background(ctx, width, height, color){
    this.width = width;
    this.height = height;
    this.color = color;  //"#191919"
    this.isPaused = false;
    this.allPixels = []; // might need to be removed. 
 this.allClouds = [];
    this.pixelCount = 150;
 this.addCloud = function(cloud){
  this.allClouds.push(cloud);
 };
    this.refreshCanvas = function(){
        this.width = $(window).width();
        this.height = $(window).height();
    };
    this.addPixelOn = function(pixelWidht, pixelHeight, location, velocity, acceleration, color) { // might need to be removed. 
        var pixel = new Pixel(pixelWidht, pixelHeight, location, velocity, acceleration, color);
        this.allPixels.push(pixel);
    };
    this.addPixel = function(pixelWidht, pixelHeight, velocity, acceleration, color) { // might need to be removed. 
        var location = new Vector2d(Math.random() * this.width, Math.random() * this.height);
        this.addPixelOn(pixelWidht, pixelHeight, location, velocity, acceleration, color);
    };
}

function Pixel(widht, height, location, velocity, acceleration, color) {
    this.height = height;
    this.width = widht;
    this.color = color;   //"#00CC33"
    this.location = location;
    this.velocity = velocity;
    this.acceleration = acceleration;
 this.alive = true;
    this.update = function () {
        this.velocity.add(this.acceleration);
        //this.velocity.limit(topspeed);
        this.location.add(this.velocity);
    };
    this.checkEdges = function (background) {
        if (this.location.y > background.height) {
            this.alive = false;
        }
    };
    this.setColor = function(color){
        this.color = color;
    };
    this.setHeight = function(height){
        this.height = height;
    };
    this.setWidth = function(width){
        this.width = width;
    }
}

function Color(r,g,b,o){
    this.red = r;
    this.green = g;
    this.blue = b;
    this.opacity = o;
    this.getColorString = function(){
        return "rgba("+this.red+","+this.green+","+this.blue+","+this.opacity+")";
    }
}

function Vector2d(x, y) {
    this.x = x;
    this.y = y;
    this.add = function (vector2d) {
        this.x += vector2d.x;
        this.y += vector2d.y;
    };
    this.sub = function (vector2d) {
        this.x -= vector2d.x;
        this.y -= vector2d.y;
    };
    this.mult = function (mult) {
        this.x *= mult;
        this.y *= mult;
    };
    this.div = function (div) {
        this.x /= div;
        this.y /= div;
    };
    this.mag = function () {
        return Math.sqrt(this.x * this.x, this.y * this.y);
    };
    this.norm = function () {
        var m = this.mag();
        if (m !== 0) {
            this.div(m);
        }
    }
}
#background-canvas {
  position: fixed;
  width: 100%;
  height: 100%
  background-color:red;
  top:0;
  left:0;
}
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<canvas id="background-canvas" />
</body>
</html>

拖动时设置云的位置的代码是这样的:

// make cloud draggable
if(this.isClicked){
    var offsetX = mousePosition.x - this.location.x;
    var offsetY = mousePosition.y - this.location.y;

    this.location = new Vector2d(this.location.x + offsetX - this.width/2, this.location.y + offsetY - this.height/2);
}

这不能正常工作。我不希望它夹在中间。

谁能帮我解决这个问题?

如果不完全清楚问题是什么,或者您需要任何额外信息,请询问。

您需要记住云本地坐标系中的点击位置:

if(background.allClouds[i].hover(mousePosition)) {
    background.allClouds[i].isClicked = true;
    background.allClouds[i].clickLocalPosition = new Vector2d(mousePosition.x, mousePosition.y);
    background.allClouds[i].clickLocalPosition.sub(background.allClouds[i].location);
}

那么,更新的时候,你根据点击位置计算出新的位置:

this.location.x = mousePosition.x - this.clickLocalPosition.x;
this.location.y = mousePosition.y - this.clickLocalPosition.y;  

$(document).ready(function () {

    var canvas = document.getElementById("background-canvas");
    canvas.width = $(window).width();
    canvas.height = $(window).height();
    canvas.style.zIndex = -1;
    var ctx = canvas.getContext("2d");
 
 var mousePosition = new Vector2d(0,0);
 
    var background = new Background(ctx, canvas.width, canvas.height, new Color(224,247,250,0.8));
 var cloud = new Cloud(background, 300, 100, new Vector2d(10,10), 20, 1000);
 img=new Image();
    img.src="https://i.imgur.com/hIVsoho.png";

 background.addCloud(cloud);
 
 for (var i = 0; i < background.allClouds.length; i++){
  var selectedCloud = background.allClouds[i];
  for (var j = 0; j < selectedCloud.maxNumberofPixels; j++){
   var pixel = cloud.createPixel();  // TODO: cloud shall not define pixel.
   //new Pixel(2, 4, getRandomLocationWithinParent(selectedCloud), new Vector2d(0,5), new Vector2d(0,0), new Color(0,0,128,1));
   cloud.addPixel(pixel);
  }
 }
 /*
 * Input listeners
 */
 document.addEventListener("mousemove", function (evt) {
        mousePosition = getMousePos(canvas, evt);
    }, false);
 document.addEventListener("mousedown", function (evt){
  console.log(mousePosition);
  for(var i = 0; i < background.allClouds.length; i++)
   if(background.allClouds[i].hover(mousePosition)) {
    background.allClouds[i].isClicked = true;
        background.allClouds[i].clickLocalPosition = new Vector2d(mousePosition.x, mousePosition.y);
        background.allClouds[i].clickLocalPosition.sub(background.allClouds[i].location);
      }
 }, false)
 document.addEventListener("mouseup", function (evt){
  for(var i = 0; i < background.allClouds.length; i++)
   if(background.allClouds[i].hover(mousePosition))
    background.allClouds[i].isClicked = false;
 }, false)
 
 
 setInterval(updateBackground, 20);  
 
 function updateBackground() {
  // paint background color.
  ctx.fillStyle = background.color.getColorString();
  ctx.fillRect(0,0,background.width, background.height);
  
  // paint clouds
  for(var i = 0; i < background.allClouds.length; i++){
   var selectedCloud = background.allClouds[i];
   //ctx.fillStyle = selectedCloud.color.getColorString();    
   //ctx.fillRect(0, 0, selectedCloud.width, selectedCloud.height); rectangle view of cloud.
   
   // paint rain
   var deadPixelContainer = [];
   for (var j = 0; j < selectedCloud.allPixels.length; j++){
    var selectedPixel = selectedCloud.allPixels[j];
    ctx.fillStyle = selectedPixel.color.getColorString();
    ctx.save();
                ctx.translate(selectedPixel.location.x, selectedPixel.location.y);
                ctx.fillRect(-selectedPixel.width / 2, -selectedPixel.height / 2, selectedPixel.width, selectedPixel.height);
                ctx.restore();
    if(!selectedPixel.alive){
     deadPixelContainer.push(selectedPixel);
     continue;
    }
    selectedPixel.update();
    selectedPixel.checkEdges(background);
    
   }
   if(deadPixelContainer.length > 0){
    selectedCloud.removePixels(deadPixelContainer);
   }
   ctx.save();
   ctx.translate(selectedCloud.location.x, selectedCloud.location.y);
   ctx.drawImage(img,0,0,img.width,img.height,-25, -10,350,100);
   ctx.restore();
   cloud.update(mousePosition);
  }
  
 }
 // TODO: Create object for mouse
 function getMousePos(canvas, evt) {
        var rect = canvas.getBoundingClientRect();
        return new Vector2d(evt.clientX - rect.left, evt.clientY - rect.top);
    }
 
});


function Cloud(background, width, height, location, startNumberOfPixels, maxNumberofPixels){
 this.width = width;
 this.height = height;
 this.location = location;
 this.allPixels = [];
 this.maxNumberofPixels = maxNumberofPixels;
 this.color = new Color(255,255,255,0.5);
 this.isClicked = false;
 this.rainStrength = 5;  // how often cloud spawns new pixels per update cycle.
 this.addPixel = function(pixel){
  if(this.allPixels.length <= startNumberOfPixels)
   this.allPixels.push(pixel);
 }
 this.update = function(mousePosition){
  // make cloud draggable
  if(this.isClicked){
      this.location.x = mousePosition.x - this.clickLocalPosition.x;
      this.location.y = mousePosition.y - this.clickLocalPosition.y;   
  }
  // add more pixels overtime.
  if(this.allPixels.length <= this.maxNumberofPixels)
   for(var i = 0; i < this.rainStrength; i++)
    this.allPixels.push(this.createPixel());
 }
 this.hover = function(mousePosition){
  if(mousePosition.x > this.location.x 
  && mousePosition.x < this.location.x + this.width
  && mousePosition.y > this.location.y
  && mousePosition.y < this.location.y + this.height)
   return true;
  return false;
 }
 this.createPixel = function(){
  return new Pixel(2, 4, this.getRandomLocation(), new Vector2d(0,7), new Vector2d(0,0.05), new Color(0,0,128,1));
 }
 this.removePixels = function(deadPixelContainer){
  for(var i = 0; i < deadPixelContainer.length; i++){
   try{
    var pixelContainer = this.allPixels.slice();
    pixelContainer.splice(this.allPixels.findIndex(v => v === deadPixelContainer[i]), 1).slice();
    this.allPixels = pixelContainer.slice();
   }catch(e){
    console.log(e);
   }
  }
 }
 this.getRandomLocation = function(){
  var minWidth = this.location.x;
  var maxWidth = this.location.x + this.width;
  var minHeight = this.location.y + this.height/2; // don't count upper part of cloud. Rain forms at the bottom.
  var maxHeight = this.location.y + this.height;
  var randomWidthLocation = Math.random() * (maxWidth - minWidth + 1)+minWidth;
  var randomHeightLocation = Math.random() * (maxHeight - minHeight + 1) + minHeight;
  return new Vector2d(randomWidthLocation, randomHeightLocation); 
 }
}

function Background(ctx, width, height, color){
    this.width = width;
    this.height = height;
    this.color = color;  //"#191919"
    this.isPaused = false;
    this.allPixels = []; // might need to be removed. 
 this.allClouds = [];
    this.pixelCount = 150;
 this.addCloud = function(cloud){
  this.allClouds.push(cloud);
 };
    this.refreshCanvas = function(){
        this.width = $(window).width();
        this.height = $(window).height();
    };
    this.addPixelOn = function(pixelWidht, pixelHeight, location, velocity, acceleration, color) { // might need to be removed. 
        var pixel = new Pixel(pixelWidht, pixelHeight, location, velocity, acceleration, color);
        this.allPixels.push(pixel);
    };
    this.addPixel = function(pixelWidht, pixelHeight, velocity, acceleration, color) { // might need to be removed. 
        var location = new Vector2d(Math.random() * this.width, Math.random() * this.height);
        this.addPixelOn(pixelWidht, pixelHeight, location, velocity, acceleration, color);
    };
}

function Pixel(widht, height, location, velocity, acceleration, color) {
    this.height = height;
    this.width = widht;
    this.color = color;   //"#00CC33"
    this.location = location;
    this.velocity = velocity;
    this.acceleration = acceleration;
 this.alive = true;
    this.update = function () {
        this.velocity.add(this.acceleration);
        //this.velocity.limit(topspeed);
        this.location.add(this.velocity);
    };
    this.checkEdges = function (background) {
        if (this.location.y > background.height) {
            this.alive = false;
        }
    };
    this.setColor = function(color){
        this.color = color;
    };
    this.setHeight = function(height){
        this.height = height;
    };
    this.setWidth = function(width){
        this.width = width;
    }
}

function Color(r,g,b,o){
    this.red = r;
    this.green = g;
    this.blue = b;
    this.opacity = o;
    this.getColorString = function(){
        return "rgba("+this.red+","+this.green+","+this.blue+","+this.opacity+")";
    }
}

function Vector2d(x, y) {
    this.x = x;
    this.y = y;
    this.add = function (vector2d) {
        this.x += vector2d.x;
        this.y += vector2d.y;
    };
    this.sub = function (vector2d) {
        this.x -= vector2d.x;
        this.y -= vector2d.y;
    };
    this.mult = function (mult) {
        this.x *= mult;
        this.y *= mult;
    };
    this.div = function (div) {
        this.x /= div;
        this.y /= div;
    };
    this.mag = function () {
        return Math.sqrt(this.x * this.x, this.y * this.y);
    };
    this.norm = function () {
        var m = this.mag();
        if (m !== 0) {
            this.div(m);
        }
    }
}
#background-canvas {
  position: fixed;
  width: 100%;
  height: 100%
  background-color:red;
  top:0;
  left:0;
}
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<canvas id="background-canvas" />
</body>
</html>