Canvas bezierCurve для тяжелого мерцания просто в Chrome. Хороший в Firefox и Edge - PullRequest
0 голосов
/ 07 марта 2019

Я применил демонстрацию Javascript 1k качающейся травы (http://labs.hyperandroid.com/js1k) для моего сайта. К сожалению, трава очень сильно мерцает только в Chrome. Ни в Firefox, ни в MS Edge.

Я уже изменил код для использования requestAnimationFrame , что не помогло.

Понятия не имею, что искать дальше. Любая помощь, чтобы избежать проблемы в Google Chrome, высоко ценится.

Комментарий сразу после первоначального сообщения : здесь, на stackoverflow, гораздо меньше мерцания с использованием Chrome, чем на JSFiddle (https://jsfiddle.net/4sqpL1b9/) или на моем сайте. Я не понимаю этого ...

Наконец, решение для моей ситуации : 1) добавить 2 пикселя в конец травинки (см. Переменную 'tuneWidth') и 2) увеличить холст на 2 и снова уменьшить масштаб с CSS до исходного размера основного объекта. Это уменьшает мерцание в Google Chrome практически завершено. По крайней мере, достаточно хорошо для меня :-). Я обновил фрагмент кода ниже.

// Original version http://labs.hyperandroid.com/js1k
// 2019 MD: Modified for being an transparent overlay on top of HTML id "thisOverlay"
// w, d, thisOverlay, thisDay, thisMonth, thisDoy are set from the initiating HTML/PHP file

var grassId = "grass";
var callbackId = null;
var canvas = null;
var ctx = null;
var garden = null;
var gradient;

var grassBaseColor;
var grassSizeFactor;

var allColors = {
	 60:"00FF00", 64:"07FD01", 69:"0EFB02", 73:"15F903", 77:"1CF704", 82:"22F506", 86:"29F307", 91:"2FF208",
	 95:"35F009", 99:"3BEE0A",104:"41EC0B",108:"47EA0C",112:"4CE80D",117:"52E70E",121:"57E50F",125:"5CE310",
	130:"61E111",134:"66DF12",138:"6ADD13",143:"6FDC14",147:"73DA15",152:"77D816",156:"7CD617",160:"80D418",
	165:"83D218",169:"87D019",173:"8BCF1A",178:"8ECD1B",182:"91CB1B",186:"94C91C",191:"97C71D",195:"9AC51E",
	200:"9DC41E",204:"A0C21F",208:"A2C020",213:"A5BE20",217:"A7BC21",221:"A9BA21",226:"ABB922",230:"ADB723",
	234:"AFB523",239:"B1B324",243:"B1B024",247:"AFAB25",252:"ADA625",256:"ACA126",261:"AA9C26",265:"A89826",
	269:"A69327",274:"A48E27",278:"A28A28",282:"A18628",287:"9F8128",291:"9D7D28",295:"9B7929",300:"997529",
	304:"977229",309:"966E29",313:"946A2A",317:"92672A",322:"90632A",326:"8E602A",330:"8C5D2A",335:"8B5A2B"
};

(function() {
	Grass = function() {
		return this;
	};

	Grass.prototype = {
			alto_hierba:          0,    // grass height
			maxAngle:             0,    // maximum grass rotation angle (wind movement)
			angle:                0,    // construction angle. thus, every grass is different to others
			coords:               null, // quadric bezier curves coordinates
			color:                null, // grass color. modified by ambient component
			offset_control_point: 3,    // grass base width. greater values, wider at the basement

		initialize : function(canvasWidth,canvasHeight,minHeight,maxHeight,angleMax,initialMaxAngle) {
			// grass start position
			var sx = Math.floor(Math.random()*canvasWidth);
			var sy = canvasHeight;
			// quadric curve middle control point. higher values means wider grass from base to peak
			// try offset_control_x = 10 for thicker grass. default = 1.5
			var offset_control_x = 1.5;
			this.alto_hierba = minHeight + Math.random() * maxHeight;
			this.maxAngle = 10 + Math.random() * angleMax;
			this.angle = Math.random() * initialMaxAngle * (Math.random() < 0.5 ? 1 : -1) * Math.PI / 180;
			// hand crafted value. modify offset_control_x to play with grass curvature slope
			var csx = sx-offset_control_x ;
			// grass curvature. greater values make grass bender. try with:
			//   var csy = sy-this.alto_hierba;   -> much more bended grass
			//   var csy = sy-1;                  -> totally unbended grass
			//   var csy = sy-this.alto_hierba/2; -> original, good looking grass
			var csy = Math.random() < 0.1 ? sy - this.alto_hierba : sy - this.alto_hierba / 2;
			// both bezier curves that conform each grass should have the same middle control point to be parallel
			// play with psx/psy by adding or removing values to slightly modify grass geometry
			var psx = csx;
			// changed var psy = csy; to
			var psy = csy - offset_control_x;
			// the bigger offset_control_point, the wider on its basement. default is 1.5
			this.offset_control_point = 1.5;
			var dx = sx + this.offset_control_point;
			var dy = sy;
			this.coords = [sx,sy,csx,csy,psx,psy,dx,dy];
			// make random grass color 
			this.color = [
				parseInt(grassBaseColor.slice(0,2),16) + Math.random()*20,
				parseInt(grassBaseColor.slice(2,4),16) + Math.random()*50,
				parseInt(grassBaseColor.slice(4,6),16) + Math.random()*20
			];
		},

		// paint every grass
		// ctx is the canvas2drendering context
		// time for grass animation
		// ambient to dim or brighten every grass
		// returns nothing
		paint : function(ctx,time,ambient) {
			// grass peak position. how much to rotate the peak
			// less values, will make as if there were a softer wind. default is 0.0005
			var inc_punta_hierba = Math.sin(time*0.0005);
			// rotate the point, so grass curves are modified accordingly. if just moved
			// horizontally, the curbe would end by being unstable with undesired visuals
			var ang = this.angle + Math.PI/2 + inc_punta_hierba * Math.PI/180 * (this.maxAngle * Math.cos(time*0.0002));
			var px = this.coords[0] + this.offset_control_point + this.alto_hierba * Math.cos(ang);
			var py = this.coords[1] - this.alto_hierba * Math.sin(ang);
			var c = this.coords;
			ctx.beginPath();
			ctx.moveTo(c[0],c[1]);
			// add some pixel to the end of the blade of grass to make
			// it thicker and therefore less flicker. default is 1
			var tuneWidth = 1;
			// draw it
			ctx.bezierCurveTo(c[0],c[1],c[2],c[3],px-tuneWidth,py);
			ctx.bezierCurveTo(px+tuneWidth,py,c[4],c[5],c[6],c[7]);
			ctx.fillStyle ='rgb(' +
				Math.floor(this.color[0]*ambient) + ',' +
				Math.floor(this.color[1]*ambient) + ',' +
				Math.floor(this.color[2]*ambient) + ')';
			ctx.fill();
		}
	};
})();

function getGrassBaseColor() {
	var doy = thisDoy; // 1..366 (from PHP)
	// get max doy (=max key in color array)
	var maxDoy = Math.max.apply(null,Object.keys(allColors));
	// loop until valid or max color index (=day of year)
	while (!allColors[doy] && doy < maxDoy) { doy++; }
	// just in case...
	doy = Math.min(doy,maxDoy);
	return allColors[doy];
}

function getGrassSizeFactor() {
	return 1;
	var day = thisDay; // 1..30 (from PHP)
	var month = thisMonth; // 1..12 (from PHP)
	if (month == 3) { return day/60; }                  // March: 0.0 - 0,5
	else if (month == 4) { return 0.5 + day/60; }       // April: 0.5 - 1.0
	else if (month >= 5 && month <= 9) { return 1; }    // May-September: 1.0
	else if (month == 10) { return 1 - day/60; }        // October: 1.0 - 0.5
	else if (month == 11) { return 0.5 - day/60; }      // November: 0.5 - 0.0
	return 1;                                           // default: 1.0
}

function clearCanvas() {
	ctx.clearRect(0,0,canvas.width,canvas.height);
}

(function() {
	Garden = function() {
		return this;
	};

	Garden.prototype = {
		grass:   null,
		ambient: 1,
		width:   0,
		height:  0,

		initialize : function(width,height,size) {
			this.width = width;
			this.height = height;
			this.grass = [];
			for(var i = 0; i < size; i++) {
				var thisGrass = new Grass();
				thisGrass.initialize(
					width,
					height,
					5,                      // min grass height. default 5
					height*grassSizeFactor, // max grass height
					20,                     // grass max initial random angle. default 20
					45                      // max random angle for animation. default 45
				);
				this.grass.push(thisGrass);
			}
		},

		paint : function(ctx,time) {
			clearCanvas();
			for(var i = 0; i < this.grass.length; i++) {
				this.grass[i].paint(ctx,time,this.ambient);
			}
		}
	};
})();

function paintGarden(timeStamp) {
	garden.paint(ctx,timeStamp);
	callbackId = requestAnimationFrame(paintGarden);
}

function initGrass() {
	var container = d.getElementById(thisOverlay);
	var thisWidth = container.clientWidth;
	var thisHeight = container.clientHeight;
	// clear current canvas area
	if (ctx) { clearCanvas(); }
	// create canvas only if first run of script, not on resize
	if (!canvas) {
		canvas = d.createElement("canvas");
		canvas.id = grassId;
		canvas.title = "Title";
		container.appendChild(canvas);
	}
	if (canvas)	{
		// 2 seems to avoid flickering in Chrome best
		var thisScale = 2;
		ctx = canvas.getContext("2d");
		// up-scale canvas and down-scale again with CSS to underlying object size
		// this - together with the above grass thickness tune - avoids almost complete flicker in Google Chrome
		ctx.canvas.width = thisScale * thisWidth;
		ctx.canvas.height = thisScale * thisHeight;
		ctx.scale(thisScale,thisScale);
		canvas.style.width = thisWidth + "px";
		canvas.style.height = thisHeight + "px";
		garden = new Garden();
		// 3rd parameter is grass density. default is 300
		garden.initialize(thisWidth,thisHeight,300);
		requestAnimationFrame(paintGarden);
	}
}

function resetGrass() {
	cancelAnimationFrame(callbackId);
	initGrass();
}

grassBaseColor = getGrassBaseColor();
grassSizeFactor = getGrassSizeFactor();
w.onresize = resetGrass;
w.onload = initGrass;
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'
  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
<head>
<meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
<style>
#header {
	height: 100px;
	margin: 0;
	padding: 0;
	background-color: black;
	position: relative; /* to allow canvas overlay */
}
#grass { /* to allow canvas overlay */
	left: 0;
	position: absolute;
	z-index: 50;
}
</style>
</head>
<body>
<div id='header'></div> 
<script type='text/javascript'>var w = window, d = document, thisOverlay = 'header', thisDay = 7, thisMonth = 3, thisDoy = 66;</script>
</body>
</html>
...