Canvas Marching Squares Glitch / Scroll Integration

时间:2015-06-13 03:16:15

标签: javascript html css canvas

我试图教自己如何使用画布 - 而且我可能已经在脑海里了 - 但我想我是否有人解决这个问题我遇到过

作为一个教训,我决定尝试从这个元球的想法开始: http://codepen.io/ge1doot/pen/RNdwQB

并重新修改它以使元球静止,但随着向下滚动页面,以不同的速率向上移动。

我有点在这里工作 - 在小提琴上表现得比在下面好得多。 https://jsfiddle.net/L7cr46px/2/



function getScrollOffsets() {
    var doc = document, w = window;
    var x, y, docEl;
    
    if ( typeof w.pageYOffset === 'number' ) {
        x = w.pageXOffset;
        y = w.pageYOffset;
    } else {
        docEl = (doc.compatMode && doc.compatMode === 'CSS1Compat')?
                doc.documentElement: doc.body;
        x = docEl.scrollLeft;
        y = docEl.scrollTop;
    }
    return {x:x, y:y};
}

var lava, ballData;
ballData = new Array();

(function() {
	var metablobby = metablobby || {
		screen: {
			elem:     null,
			callback: null,
			ctx:      null,
			width:    0,
			height:   0,
			left:     0,
			top:      0,
			init: function (id, callback, initRes) {
				this.elem = document.getElementById(id);
				this.callback = callback || null;
				if (this.elem.tagName == "CANVAS") this.ctx = this.elem.getContext("2d");
				window.addEventListener('resize', function () {
					this.resize();
				}.bind(this), false);
				this.elem.onselectstart = function () { return false; }
				this.elem.ondrag        = function () { return false; }
				initRes && this.resize();
				return this;
			},
			resize: function () {
				var o = this.elem;
				this.width  = o.offsetWidth;
				this.height = o.offsetHeight;
				for (this.left = 0, this.top = 0; o != null; o = o.offsetParent) {
					this.left += o.offsetLeft;
					this.top  += o.offsetTop;
				}
				if (this.ctx) {
					this.elem.width  = this.width;
					this.elem.height = this.height;
				}
				this.callback && this.callback();
			},
			pointer: {
				screen:   null,
				elem:     null,
				callback: null,
				pos:   {x:0, y:0},
				mov:   {x:0, y:0},
				drag:  {x:0, y:0},
				start: {x:0, y:0},
				end:   {x:0, y:0},
				active: false,
				touch: false,
				move: function (e, touch) {
					//this.active = true;
					this.touch = touch;
					e.preventDefault();
					var pointer = touch ? e.touches[0] : e;
					this.mov.x = pointer.clientX - this.screen.left;
					this.mov.y = pointer.clientY - this.screen.top;
					if (this.active) {
						this.pos.x = this.mov.x;
						this.pos.y = this.mov.y;
						this.drag.x = this.end.x - (this.pos.x - this.start.x);
						this.drag.y = this.end.y - (this.pos.y - this.start.y);
						this.callback.move && this.callback.move();
					}
				},
				scroll: function(e, touch){
					run();	
				},
				init: function (callback) {
					this.screen = metablobby.screen;
					this.elem = this.screen.elem;
					this.callback = callback || {};
					if ('ontouchstart' in window) {
						// touch
						this.elem.ontouchstart  = function (e) { this.down(e, true); }.bind(this);
						this.elem.ontouchmove   = function (e) { this.move(e, true); }.bind(this);
						this.elem.ontouchend    = function (e) { this.up(e, true);   }.bind(this);
						this.elem.ontouchcancel = function (e) { this.up(e, true);   }.bind(this);
					}
					document.addEventListener("mousemove", function (e) { this.move(e, false); }.bind(this), true);
					document.addEventListener("scroll", function (e) { this.scroll(e, false); }.bind(this), true);
	
					return this;
				}
			},
		}
	}
	
	// ==== Point constructor ====
	var Point = function(x, y) {
		this.x = x;
		this.y = y;
		this.magnitude = x * x + y * y;
		this.computed = 0;
		this.force = 0;
	}
	Point.prototype.add = function(p) {
		return new Point(this.x + p.x, this.y + p.y);
	}
	// ==== Ball constructor ====
		var Ball = function(parent,i) {
			var x = Math.floor(Math.random() * window.innerWidth) + 1;
			var y = Math.floor(Math.random() * (window.innerHeight)*5) + 1;
			var radius = (Math.floor(Math.random() * 65) + 15)
			var drift = Math.random();
			
			ballData[i]=[x,y,radius, drift];
			
			this.vel = new Point(0,0);
			this.pos = new Point(x,y);
			this.size = radius;
			this.width = parent.width;
			this.height = parent.height;
		}
	// ==== move balls ====
		Ball.prototype.move = function(i) {
			// ---- interact with pointer ----
			if (pointer.active) {
					var dx = pointer.pos.x - this.pos.x;
					var dy = pointer.pos.y - this.pos.y;
					var a = Math.atan2(dy, dx);
					var v = -Math.min(
							10,
							500 / Math.sqrt(dx * dx + dy * dy)
					);
					this.pos = this.pos.add(
							new Point(
									Math.cos(a) * v,
									Math.sin(a) * v
							)
					);
			}
			
			var drift = ballData[i-1][3];
			var pageOffset = getScrollOffsets().y;
			this.pos.y = ballData[i-1][1] - (pageOffset*drift);
			this.vel.y = 0 - (pageOffset*drift);
			this.pos = this.pos.add(this.vel);
	
		}
		
	// ==== lavalamp constructor ====
		var LavaLamp = function(width, height, numBalls) {
			this.step = 4;
			this.width = width;
			this.height = height;
			this.wh = Math.min(width, height);
			this.sx = Math.floor(this.width / this.step);
			this.sy = Math.floor(this.height / this.step);
			this.paint = false;
			this.metaFill = '#000000';
			this.plx = [0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0];
			this.ply = [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1];
			this.mscases = [0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 0, 2, 1, 1, 0];
			this.ix = [1, 0, -1, 0, 0, 1, 0, -1, -1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1];
			this.grid = [];
			this.balls = [];
			this.iter = 0;
			this.sign = 1;
			// ---- init grid ----
				for (var i = 0; i < (this.sx + 2) * (this.sy + 2); i++) {
					this.grid[i] = new Point(
						(i % (this.sx + 2)) * this.step, (Math.floor(i / (this.sx + 2))) * this.step
					)
				}
			// ---- create metaballs ----
				for (var i = 0; i < 50; i++) {
					this.balls[i] = new Ball(this,i);
				}
		}
	// ==== compute cell force ====
		LavaLamp.prototype.computeForce = function(x, y, idx) {
			var force;
			var id = idx || x + y * (this.sx + 2);
			if (x === 0 || y === 0 || x === this.sx || y === this.sy) {
					var force = 0.6 * this.sign;
			} else {
					var cell = this.grid[id];
					var force = 0;
					var i = 0,
							ball;
					while (ball = this.balls[i++]) {
							force += ball.size * ball.size / (-2 * cell.x * ball.pos.x - 2 * cell.y * ball.pos.y + ball.pos.magnitude + cell.magnitude);
					}
					force *= this.sign
			}
			this.grid[id].force = force;
			return force;
		}
	// ---- compute cell ----
		LavaLamp.prototype.marchingSquares = function(next) {
			var x = next[0];
			var y = next[1];
			var pdir = next[2];
			var id = x + y * (this.sx + 2);
			if(typeof this.grid[id] !== "undefined"){
				if (this.grid[id].computed === this.iter) return false;
				var dir, mscase = 0;
				// ---- neighbors force ----
				for (var i = 0; i < 4; i++) {
						var idn = (x + this.ix[i + 12]) + (y + this.ix[i + 16]) * (this.sx + 2);
						var force = this.grid[idn].force;
						if ((force > 0 && this.sign < 0) || (force < 0 && this.sign > 0) || !force) {
								// ---- compute force if not in buffer ----
								force = this.computeForce(
										x + this.ix[i + 12],
										y + this.ix[i + 16],
										idn
								);
						}
						if (Math.abs(force) > 1) mscase += Math.pow(2, i);
				}
				if (mscase === 15) {
					// --- inside ---
					return [x, y - 1, false];
				} else {
					// ---- ambiguous cases ----
					if (mscase === 5) dir = (pdir === 2) ? 3 : 1;
					else if (mscase === 10) dir = (pdir === 3) ? 0 : 2;
					else {
						// ---- lookup ----
						dir = this.mscases[mscase];
						this.grid[id].computed = this.iter;
					}
					// ---- draw line ----
					var ix = this.step / (
						Math.abs(Math.abs(this.grid[(x + this.plx[4 * dir + 2]) + (y + this.ply[4 * dir + 2]) * (this.sx + 2)].force) - 1) /
						Math.abs(Math.abs(this.grid[(x + this.plx[4 * dir + 3]) + (y + this.ply[4 * dir + 3]) * (this.sx + 2)].force) - 1) + 1
					);
					ctx.lineTo(
							this.grid[(x + this.plx[4 * dir + 0]) + (y + this.ply[4 * dir + 0]) * (this.sx + 2)].x + this.ix[dir] * ix,
							this.grid[(x + this.plx[4 * dir + 1]) + (y + this.ply[4 * dir + 1]) * (this.sx + 2)].y + this.ix[dir + 4] * ix
					);
					this.paint = true;
					// ---- next ----
					return [
						x + this.ix[dir + 4],
						y + this.ix[dir + 8],
						dir
					];
				}
			}
		}
		LavaLamp.prototype.renderMetaballs = function() {
			var i = 0, ball;
			while (ball = this.balls[i++]) ball.move(i);

			// ---- reset grid ----
			this.iter++;
			this.sign = -this.sign;
			this.paint = false;
			ctx.fillStyle = '#FF0000';
			ctx.beginPath();
			// ---- compute metaballs ----
			i = 0;
			while (ball = this.balls[i++]) {
				// ---- first cell ----
				var next = [
						Math.round(ball.pos.x / this.step),
						Math.round(ball.pos.y / this.step), false
				];
				// ---- marching squares ----
				do {
						next = this.marchingSquares(next);
				} while (next);
				// ---- fill and close path ----
				if (this.paint) {
					ctx.fill();
					ctx.closePath();
					ctx.beginPath();
					this.paint = false;
				}
			}
		}
	
	// ==== main loop ====
		var run = function() {
			//requestAnimationFrame(run);
			ctx.clearRect(0, 0, screen.width, screen.height);
			lava.renderMetaballs();
		}
	// ---- canvas ----
		var screen = metablobby.screen.init("thedots", null, true),
		ctx = screen.ctx,
		pointer = screen.pointer.init();
		screen.resize();
	// ---- create LavaLamps ----
		lava = new LavaLamp(screen.width, screen.height, 10);
	// ---- start engine ----
		run();

})();
&#13;
html,body {
	height: 100%;
	width: 100%;
	background-color: white;
}
body {
	height: 500vh;
}
#thedots {
	position: fixed;
	top: 0;
	left: 0;
	width: 100vw;
	height: 100vh;
	z-index: 100;
	pointer-events: none;
}
&#13;
<canvas id="thedots" width="1203" height="363"></canvas>	
&#13;
&#13;
&#13;

但是我会看到疯狂的渲染错误。或者他们可能没有渲染错误,只是一些错误的代码。任何帮助将不胜感激。

谢谢!

0 个答案:

没有答案