为了获得更好的拖动效果,正确的偏移量是多少?

时间:2018-11-19 16:58:43

标签: javascript jquery html math draggable

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

这是我当前的设置。

$(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);
}

这不能正常工作。我希望它不会夹到中心。

有人可以帮我吗?

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

1 个答案:

答案 0 :(得分:1)

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

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>