你的浏览器不支持画布(Canvas).

我们可以通过新的HTML5画布(Canvas)标签添加一个画布到网页中,以下代码用于定义一个 800 x 600 大小的画布。



你的浏览器不支持画布(Canvas).
					

再通过以下 JavaScript 代码控制在画布中的绘图。

// 通过ID取画布元素
var mc				= document.getElementById("MyCanvas");
var ctx				= null;

var width			= mc.width;
var height			= mc.height;
var mouseX			= 0;
var mouseY			= 0;
var pmouseX			= 0;
var pmouseY			= 0;
var drawRate		= 6;
var frameCount		= 0;
var mousePressed	= false;

var PI				= Math.PI;
var TWO_PI			= Math.PI * 2;
var HALF_PI			= Math.PI * 0.5;

var worms;
var colors	= [];
colors[0]	= '#2cd9fe';
colors[1]	= '#2cfecf';
colors[2]	= '#373fdf';
colors[3]	= '#88fe1f';
colors[4]	= '#48d6ff';
colors[5]	= '#b3fcff';
colors[6]	= '#f76cad';
colors[7]	= '#505083';
colors[8]	= '#113a7e';
colors[9]	= '#014050';
colors[10]	= '#ccf3ef';
colors[11]	= '#009437';
colors[12]	= '#8fb300';

if(mc.getContext)
{
	// 创建 context 对象
	ctx = mc.getContext("2d");

	// 鼠标移动事件
	mc.addEventListener('mousemove', ev_mousemove, false);
	mc.addEventListener('mousedown', ev_mousedown, false);
	mc.addEventListener('mouseup', ev_mouseup, false);
	worms = new Array();
	drawInternal();
}

function ev_mousemove(ev)
{
	if (ev.layerX || ev.layerX == 0)
	{
		// 在Firefox中取鼠标坐标
		mouseX = ev.layerX;
		mouseY = ev.layerY;
	}
	else if (ev.offsetX || ev.offsetX == 0)
	{
		// 在Opera中取鼠标坐标
		mouseX = ev.offsetX;
		mouseY = ev.offsetY;
	}
}

function ev_mousedown(ev)
{
	mousePressed = true;
}

function ev_mouseup(ev)
{
	mousePressed = false;
}

function drawInternal()
{
	draw();
	setTimeout('drawInternal()', drawRate);
	pmouseX = mouseX;
	pmouseY = mouseY;
	frameCount++;
}

// 清画布
function clear()
{
	ctx.clearRect(0, 0, width, height);
}

// 圆形填充
function fillCircle(x, y, radius)
{
	if(radius <= 0)
		radius = 0;

	ctx.beginPath();
	ctx.arc(x, y, radius, 0, TWO_PI, true);
	ctx.closePath();
	ctx.fill();
}

// 伪随机数
function Marsaglia(i1, i2)
{
	var z=i1 || 362436069, w= i2 || 521288629;
	var nextInt = function()
	{
		z=(36969*(z&65535)+(z >> 16)) & 0xFFFFFFFF;
		w=(18000*(w&65535)+(w >> 16)) & 0xFFFFFFFF;
		return (((z&0xFFFF) << 16) | (w&0xFFFF)) & 0xFFFFFFFF;
	};

	this.nextDouble = function()
	{
		var i = nextInt() / 4294967296;
		return i < 0 ? 1 + i : i;
	};

	this.nextInt = nextInt;
}

Marsaglia.createRandomized = function()
{
	var now = new Date();
	return new Marsaglia((now / 60000) & 0xFFFFFFFF, now & 0xFFFFFFFF);
};

// Perlin Noise(由Ken Perlin发明的自然噪声生成算法)
function PerlinNoise(seed)
{
	var rnd = seed != undefined ? new Marsaglia(seed) : Marsaglia.createRandomized();
	var i, j;

	var p = new Array(512);

	for(i=0;i < 256;++i)
	{
		p[i] = i;
	}

	for(i=0;i < 256;++i)
	{
		var t = p[j = rnd.nextInt() & 0xFF];
		p[j] = p[i];
		p[i] = t;
	}

	for(i=0;i < 256;++i)
	{
		p[i + 256] = p[i];
	}

	function grad3d(i,x,y,z)
	{
		var h = i & 15;
		var u = h < 8 ? x : y, v = h < 4 ? y : h == 12||h == 14 ? x : z;
		return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
	}

	function grad2d(i,x,y)
	{
		var v = (i & 1) == 0 ? x : y;
		return (i&2) == 0 ? -v : v;
	}

	function grad1d(i,x)
	{
		return (i&1) == 0 ? -x : x;
	}

	function lerp(t,a,b)
	{
		return a + t * (b - a);
	}

	this.noise3d = function(x, y, z)
	{
		var X = Math.floor(x)&255, Y = Math.floor(y)&255, Z = Math.floor(z)&255;
		x -= Math.floor(x); y -= Math.floor(y); z -= Math.floor(z);
		var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y, fz = (3-2*z)*z*z;
		var p0 = p[X]+Y, p00 = p[p0] + Z, p01 = p[p0 + 1] + Z,
		p1 = p[X + 1] + Y, p10 = p[p1] + Z, p11 = p[p1 + 1] + Z;
		return lerp(fz,
		lerp(fy, lerp(fx, grad3d(p[p00], x, y, z), grad3d(p[p10], x-1, y, z)),
		lerp(fx, grad3d(p[p01], x, y-1, z), grad3d(p[p11], x-1, y-1,z))),
		lerp(fy, lerp(fx, grad3d(p[p00 + 1], x, y, z-1), grad3d(p[p10 + 1], x-1, y, z-1)),
		lerp(fx, grad3d(p[p01 + 1], x, y-1, z-1), grad3d(p[p11 + 1], x-1, y-1,z-1))));
	};

	this.noise2d = function(x, y)
	{
		var X = Math.floor(x)&255, Y = Math.floor(y)&255;
		x -= Math.floor(x); y -= Math.floor(y);
		var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y;
		var p0 = p[X]+Y, p1  = p[X + 1] + Y;
		return lerp(fy,
		lerp(fx, grad2d(p[p0], x, y), grad2d(p[p1], x-1, y)),
		lerp(fx, grad2d(p[p0 + 1], x, y-1), grad2d(p[p1 + 1], x-1, y-1)));
	};

	this.noise1d = function(x)
	{
		var X = Math.floor(x)&255;
		x -= Math.floor(x);
		var fx = (3-2*x)*x*x;
		return lerp(fx, grad1d(p[X], x), grad1d(p[X+1], x-1));
	};
}

var noiseProfile = { generator: undefined, octaves: 4, fallout: 0.5, seed: undefined};

function noise(x, y, z)
{
	if(noiseProfile.generator == undefined)
	{
		noiseProfile.generator = new PerlinNoise(noiseProfile.seed);
	}

	var generator = noiseProfile.generator;
	var t=1, k=1, sum=0;

	for(var i=0;i < noiseProfile.octaves;++i)
	{
		t *= noiseProfile.fallout;
		switch(arguments.length)
		{
		case 1:
			sum += t * (1 + generator.noise1d(k*x)) / 2;
			break;
		case 2:
			sum += t * (1 + generator.noise2d(k*x, k*y)) / 2;
			break;
		case 3:
			sum += t * (1 + generator.noise3d(k*x, k*y, k*z)) / 2;
			break;
		}

		k *= 2;
	}
	return sum;
}

function Worm(x,y)
{
	this.x = x;
	this.y = y;
	this.lx = x;
	this.ly = y;
	this.heading = Math.sin(frameCount*0.083) * PI;;
	this.rotation = Math.random() * (PI / (Math.random() * 70));
	this.rate = 0;
	this.maxLength = 15 + (noise(frameCount * 0.0025, frameCount*0.1) * 15);
	this.detail = 2;
	this.thickness = 3;
	this.thicknessTarget = 5 + Math.random() * 10;

	var cIndex = parseInt( Math.random() * colors.length );
	this.c = colors[cIndex];
	this.life = 30 + Math.random() * 120;
	this.segments = new Array();
	this.ooo = true;
	this.counter = noise(frameCount * 0.1, frameCount*.1);

	this.update = function()
	{
		this.life--;
		if(this.life > 0)
		{
			this.thickness += (this.thicknessTarget-this.thickness) * 0.1;
			this.thickness += 0.1;
			if(this.thickness > this.thicknessTarget)
				this.thickness = this.thicknessTarget;

			this.heading += this.rotation;
			this.rate = Math.cos( this.counter / 200.0) * (10 + noise(frameCount*0.05) * 10);
			this.rotation = Math.sin(this.counter/this.rate) * (this.segments.length+1) * 0.010;
			this.counter++;

			var speedMod = (this.segments.length * this.segments.length * this.segments.length)
			* 0.0015 * this.thickness / this.thicknessTarget;
			var totalSpeed = (this.detail + speedMod);
			var nx = Math.cos(this.heading) * totalSpeed;
			var ny = Math.sin(this.heading) * totalSpeed;
			this.walk(nx,ny);
		}
		else
		{
			if(this.segments.length > 1)
			{
				this.segments.pop();
			}
			else
			{
				this.thickness *= 0.95;
				this.thickness -= 0.2;
				if(this.thickness < 0.1)
					this.ooo = true;
				return;
			}
		}

		this.ooo = true;
		for(var i=0;i < this.segments.length;i++)
		{
			var segment = this.segments[i];
			if(segment.ooo() == false)
			{
				this.ooo = false;
				break;
			}
		}
	}

	this.walk = function(nx,ny)
	{
		this.lx = this.x;
		this.ly = this.y;
		this.x += nx;
		this.y += ny;
		var newSegment = new Segment(this.lx,this.ly,this.x,this.y,this.thickness);
		this.segments.unshift(newSegment);

		if(this.segments.length > 1)
		{
			this.segments[this.segments.length-1].smoothAgainst(this.segments[this.segments.length-2]);
		}

		if(this.segments.length >= this.maxLength)
		{
			this.segments.pop();
		}

	}
}

function Segment(x1,y1,x2,y2,thickness)
{
	this.x1 = x1;
	this.y1 = y1;
	this.x2 = x2;
	this.y2 = y2;
	this.thickness = thickness;

	var angle = Math.atan2(y2-y1,x2-x1);

	this.lAngle = angle - HALF_PI;
	var lDeltaX = Math.cos(this.lAngle) * thickness;
	var lDeltaY = Math.sin(this.lAngle) * thickness;
	this.leftX1 = x1 + lDeltaX;
	this.leftY1 = y1 + lDeltaY;
	this.leftX2 = x2 + lDeltaX;
	this.leftY2 = y2 + lDeltaY;

	this.rAngle = angle + HALF_PI;
	var rDeltaX = Math.cos(this.rAngle) * thickness;
	var rDeltaY = Math.sin(this.rAngle) * thickness;
	this.rightX1 = x1 + rDeltaX;
	this.rightY1 = y1 + rDeltaY;
	this.rightX2 = x2 + rDeltaX;
	this.rightY2 = y2 + rDeltaY;

	this.smoothAgainst = function(last)
	{
		this.leftX1 = last.leftX2 = (last.leftX2 + this.leftX1) * 0.5;
		this.leftY1 = last.leftY2 = (last.leftY2 + this.leftY1) * 0.5;
		this.rightX1 = last.rightX2 = (last.rightX2 + this.rightX1) * 0.5;
		this.rightY1 = last.rightY2 = (last.rightY2 + this.rightY1) * 0.5;
	}

	this.ooo = function()
	{
		if(this.x1 < 0 || this.y1 < 0 || this.x1 > width || this.y1 > height ||
		   this.x2 < 0 || this.y2 < 0 || this.x2 > width || this.y2 > height)
		  return true;
		else
		  return false;
	}
}

function draw()
{
	clear();

	for(var w=0; w < worms.length; w++)
	{
		var worm = worms[w];
		worm.update();
		ctx.fillStyle = worm.c;

		ctx.lineWidth = 2;
		ctx.strokeStyle = '#FFFFFF';
		if(worm.segments.length>1)
		{
			ctx.beginPath();
			ctx.moveTo(worm.segments[0].leftX1, worm.segments[0].leftY1);
			for(var i=0; i < worm.segments.length; i++)
			{
				var segment = worm.segments[i];
				ctx.lineTo(segment.leftX1,segment.leftY1);
			}
			ctx.lineTo(worm.segments[worm.segments.length-1].rightX1,
			worm.segments[worm.segments.length-1].rightY1);

			for(var i=worm.segments.length-1; i >= 0; i--)
			{
				var segment = worm.segments[i];
				ctx.lineTo(segment.rightX1,segment.rightY1);
			}
			ctx.closePath();
			ctx.fill();
			ctx.stroke();
		}

		if(worm.segments.length>=1)
		{
			var x = worm.segments[0].x1;
			var y = worm.segments[0].y1;
			var thickness = worm.segments[0].thickness;
			if(worm.thickness < thickness)
				thickness = worm.thickness;
			if(thickness <= 0)
				thickness = 0.000001;
			ctx.beginPath();
			ctx.arc(x, y, thickness, worm.segments[0].lAngle, worm.segments[0].rAngle, false);
			ctx.closePath();
			ctx.stroke();

			var xEnd = worm.segments[worm.segments.length-1].x1;
			var yEnd = worm.segments[worm.segments.length-1].y1;
			thickness = worm.segments[worm.segments.length-1].thickness;
			if(worm.thickness < thickness)
				thickness = worm.thickness;
			if(thickness <= 0)
				thickness = 0.000001;
			ctx.beginPath();
			ctx.arc(xEnd, yEnd, thickness, worm.segments[worm.segments.length-1].lAngle,
			worm.segments[worm.segments.length-1].rAngle, true);
			ctx.closePath();
			ctx.stroke();

			thickness = worm.thickness-0.6;
			if(worm.thickness < thickness)
				thickness = worm.thickness;
			fillCircle(worm.segments[0].x1,worm.segments[0].y1,thickness);
			thickness = worm.segments[worm.segments.length-1].thickness-0.6;
			if(worm.thickness < thickness)
				thickness = worm.thickness;
			fillCircle(worm.segments[worm.segments.length-1].x1,
			worm.segments[worm.segments.length-1].y1,thickness);

			if(worm.life > 0)
			{
				ctx.fillStyle = "#FFFFFF";
				fillCircle(worm.segments[0].x1,worm.segments[0].y1,thickness * 0.72);
			}
		}
	}

	for(var w=0; w < worms.length; w++)
	{
		var worm = worms[w];
		if(worm.ooo)
		{
			worms.splice(w,1);
			w--;
		}
	}

	if(frameCount %2 ==0 && mousePressed && worms.length < 50)
	{
		var direction = Math.atan2(pmouseY-mouseY, pmouseX-mouseX) + PI;
		var newWorm = new Worm(mouseX,mouseY);
		worms.push( newWorm );
		if(mouseX != pmouseX && mouseY != pmouseY)
		{
			newWorm.heading = direction;
		}
	}
}
					

返回目录