Jello Text

Size
12,971 Kb
Views
30,360

How do I make an jello text?

Text bounces around like Jello using Verlet Integration on a triangulated grid.. What is a jello text? How do you make a jello text? This script and codes were developed by Sakri Rosenstrom on 13 September 2022, Tuesday.

Jello Text Previews

Jello Text - Script Codes HTML Codes

<!DOCTYPE html>
<html >
<head> <meta charset="UTF-8"> <title>Jello Text</title> <link rel="stylesheet" href="css/style.css">
</head>
<body> <div id="canvasContainer"></div> <script src="js/index.js"></script>
</body>
</html>

Jello Text - Script Codes CSS Codes

html, body{ margin : 0px; width : 100%; height : 100%; overflow: hidden;
}
#canvasContainer{ position: absolute; margin : 0px; width : 100%; height : 100%;
}
#controls{ position: absolute;
}

Jello Text - Script Codes JS Codes

/* * * @author Sakri Rosenstrom * http://www.sakri.net * https://twitter.com/sakri * http://www.devstate.net * Sources for this can be found at: * https://github.com/sakri/sakriNetCommonJS */
(function (window){ var Sakri = window.Sakri || {}; window.Sakri = window.Sakri || Sakri;	Sakri.MathUtil = {};	//used for radiansToDegrees and degreesToRadians	Sakri.MathUtil.PI_180 = Math.PI/180;	Sakri.MathUtil.ONE80_PI = 180/Math.PI;	//precalculations for values of 90, 270 and 360 in radians	Sakri.MathUtil.PI2 = Math.PI*2;	Sakri.MathUtil.HALF_PI = Math.PI/2;	Sakri.MathUtil.PI_AND_HALF = Math.PI+ Math.PI/2;	Sakri.MathUtil.NEGATIVE_HALF_PI = -Math.PI/2; //keep degrees between 0 and 360 Sakri.MathUtil.constrainDegreeTo360 = function(degree){ return (360 + degree % 360) % 360;//hmmm... looks a bit weird?! }; Sakri.MathUtil.constrainRadianTo2PI = function(rad){ return (Sakri.MathUtil.PI2 + rad % Sakri.MathUtil.PI2) % Sakri.MathUtil.PI2;//equally so... }; Sakri.MathUtil.radiansToDegrees = function(rad){ return rad*Sakri.MathUtil.ONE80_PI; }; Sakri.MathUtil.degreesToRadians = function(degree){ return degree * Sakri.MathUtil.PI_180; };	//return number between 1 and 0	Sakri.MathUtil.normalize = function(value, minimum, maximum){	return (value - minimum) / (maximum - minimum);	};	//map normalized number to values	Sakri.MathUtil.interpolate = function(normValue, minimum, maximum){	return minimum + (maximum - minimum) * normValue;	};	//map a value from one set to another	Sakri.MathUtil.map = function(value, min1, max1, min2, max2){	return Sakri.MathUtil.interpolate( Sakri.MathUtil.normalize(value, min1, max1), min2, max2);	}; Sakri.MathUtil.clamp = function(min,max,value){ if(value < min){ return min; } if(value > max){ return max; } return value; }; Sakri.MathUtil.clampRGB = function(value){ return Sakri.MathUtil.clamp(0, 255, value); };	Sakri.MathUtil.getRandomNumberInRange = function(min, max){	return min + Math.random() * (max - min);	};	Sakri.MathUtil.getRandomIntegerInRange = function(min, max){	return Math.round(Sakri.MathUtil.getRandomNumberInRange(min, max));	}; //Move to geom?	Sakri.MathUtil.getCircumferenceOfEllipse = function(width,height){	return ((Math.sqrt(.5 * ((width * width) + (height * height)))) * (Math.PI * 2)) / 2;	}; //from : http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb Sakri.MathUtil.rgbToHex = function(r, g, b) { return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); } Sakri.MathUtil.hexToRgb = function(hex) { // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; hex = hex.replace(shorthandRegex, function(m, r, g, b) { return r + r + g + g + b + b; }); var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null; }
}(window));
//has a dependency on Sakri.MathUtil
(function (window){ var Sakri = window.Sakri || {}; window.Sakri = window.Sakri || Sakri;	Sakri.Geom = {}; //================================================== //=====================::POINT::==================== //================================================== Sakri.Geom.Point = function (x,y){ this.x = isNaN(x) ? 0 : x; this.y = isNaN(y) ? 0 : y; }; Sakri.Geom.Point.prototype.clone = function(){ return new Sakri.Geom.Point(this.x,this.y); }; Sakri.Geom.Point.prototype.update = function(x, y){ this.x = isNaN(x) ? this.x : x; this.y = isNaN(y) ? this.y : y; }; Sakri.Geom.Point.prototype.add = function(x, y){ this.x += isNaN(x) ? 0 : x; this.y += isNaN(y) ? 0 : y; }; Sakri.Geom.Point.prototype.equals = function(point){ return this.x==point.x && this.y==point.y; }; Sakri.Geom.Point.prototype.toString = function(){ return "{x:"+this.x+" , y:"+this.y+"}"; }; Sakri.Geom.Point.interpolate = function(pointA, pointB, normal){ return new Sakri.Geom.Point(Sakri.MathUtil.interpolate(normal, pointA.x, pointB.x) , Sakri.MathUtil.interpolate(normal, pointA.y, pointB.y)); }; Sakri.Geom.Point.distanceBetweenTwoPoints = function( point1, point2 ){ //console.log("Math.pow(point2.x - point1.x,2) : ",Math.pow(point2.x - point1.x,2)); return Math.sqrt( Math.pow(point2.x - point1.x,2) + Math.pow(point2.y - point1.y,2) ); }; Sakri.Geom.Point.angleBetweenTwoPoints = function(p1,p2){ return Math.atan2(p1.y-p2.y, p1.x-p2.x); }; Sakri.Geom.mirrorPointInRectangle = function(point,rect){ return new Sakri.Geom.Point(rect.width-point.x,rect.height-point.y); }; Sakri.Geom.randomizePoint = function(point,randomValue){ return new Sakri.Geom.Point(-randomValue+Math.random()*randomValue+point.x,-randomValue+Math.random()*randomValue+point.y); }; //================================================== //=====================::LINE::==================== //================================================== Sakri.Geom.Line = function(pointA, pointB){ this.pointA = pointA ? pointA : new Sakri.Geom.Point(); this.pointB = pointB ? pointB : new Sakri.Geom.Point(); this.updateLineProperties(); }; //=================:: Static methods //first check for horizontal+vertical lines? no division by 0?! Sakri.Geom.Line.getSlope = function(pointA,pointB){ return (pointA.y - pointB.y) / (pointA.x - pointB.x); }; Sakri.Geom.Line.getYIntercept = function(point,slope){ //slope intercept : y = mx + b return point.y - point.x * slope; }; //checks for parallel lines Sakri.Geom.Line.intersects = function(line1, line2){ if(line1.isVertical() && line2.isVertical()){ //if(line1.this.pointB.equals(line2.this.pointA))return line1.this.pointB; //throw new Error("Line.getIntersection() ERROR Two vertical lines cannot intersect"); console.log("Line.getIntersection() ERROR Two vertical lines cannot intersect"); return false; } if(line1.isHorizontal() && line2.isHorizontal()){ //if(line1.this.pointB.equals(line2.this.pointA))return line1.this.pointB; //throw new Error("Line.getIntersection() ERROR Two horizontal lines cannot intersect"); console.log("Line.getIntersection() ERROR Two horizontal lines cannot intersect"); return false; } if(line1.slope == line2.slope){ //throw new Error("Line.getIntersection() ERROR Two Parallel lines cannot intersect"); console.log("Line.getIntersection() ERROR Two Parallel lines cannot intersect"); return false; } return true; }; //returns a Sakri.Geom.Point or null //TODO : create alternative where new Points are not instantiated on every call, maybe pass a Point as an arg? Sakri.Geom.Line.getIntersection = function(line1, line2){ //slope intercept : y = mx + b //m1 * x + b1 - y = m2 * x + b2 - y //m1 * x + b1 - b2 = m2 * x; //m1 * x - m2 * x = b2 - b1; //x * (m1 - m2) = b2 - b1 if(!Sakri.Geom.Line.intersects(line1, line2)){ return null; } //PERPENDICULAR LINES if(line1.isHorizontal() && line2.isVertical()){ return new Sakri.Geom.Point(line2.pointA.x, line1.pointA.y); } if(line2.isHorizontal() && line1.isVertical()){ return new Sakri.Geom.Point(line1.pointA.x, line2.pointA.y); } //ONE HORIZONTAL OR VERTICAL if(line1.isHorizontal()){ return new Sakri.Geom.Point(line2.getXatY(line1.pointA.y), line1.pointA.y); } if(line1.isVertical()){ return new Sakri.Geom.Point(line1.pointA.x, line2.getYatX(line1.pointA.x)); } if(line2.isHorizontal()){ return new Sakri.Geom.Point(line1.getXatY(line2.pointA.y),line2.pointA.y); } if(line2.isVertical()){ return new Sakri.Geom.Point(line2.pointA.x,line1.getYatX(line2.pointA.x)); } var p = new Sakri.Geom.Point(); //console.log("line1.slope : "+line1.slope); //console.log("line2.slope : "+line2.slope); p.x = (line2.yIntercept - line1.yIntercept) / (line1.slope - line2.slope); p.y = line1.getYatX(p.x); return p; }; //=================:: public methods Sakri.Geom.Line.prototype.isHorizontal = function(){ return this.pointA.y == this.pointB.y; }; Sakri.Geom.Line.prototype.isVertical = function(){ return this.pointA.x == this.pointB.x; }; Sakri.Geom.Line.prototype.getYatX = function(x){ if(this.isHorizontal()){ return this.pointA.y; } if(this.isVertical()){ return NaN; }//throw error? return this.slope * x + this.yIntercept; }; Sakri.Geom.Line.prototype.getXatY = function(y){ if(this.isVertical()){ return this.pointA.x; } if(this.isHorizontal()){ return NaN; }//throw error? return (y - this.yIntercept)/this.slope; }; Sakri.Geom.Line.prototype.update = function(pointA, pointB){ if(pointA){ this.pointA.x = isNaN(pointA.x) ? this.pointA.x : pointA.x; this.pointA.y = isNaN(pointA.y) ? this.pointA.y : pointA.y; } if(pointB){ this.pointB.x = isNaN(pointB.x) ? this.pointB.x : pointB.x; this.pointB.y = isNaN(pointB.y) ? this.pointB.y : pointB.y; } this.updateLineProperties(); }; Sakri.Geom.Line.prototype.updateLineProperties = function(){ this.slope = Sakri.Geom.Line.getSlope(this.pointA, this.pointB); this.yIntercept = Sakri.Geom.Line.getYIntercept(this.pointA, this.slope); }; Sakri.Geom.Line.prototype.toString = function(){ return "Line{a : "+this.pointA.toString()+" , b : "+this.pointB.toString()+" , slope : "+this.slope+" , yIntercept : "+this.yIntercept+"}"; }; // ================================================== //=====================::TRIANGLE::==================== //================================================== Sakri.Geom.Triangle = function (a,b,c){ this.a = a ? a : new Sakri.Geom.Point(0,0); this.b = b ? b : new Sakri.Geom.Point(0,0); this.c = c ? c : new Sakri.Geom.Point(0,0); }; Sakri.Geom.Triangle.prototype.equals = function(triangle){ return this.a.equals(triangle.a) && this.b.equals(triangle.b) && this.c.equals(triangle.c); }; Sakri.Geom.Triangle.prototype.clone = function(){ return new Sakri.Geom.Triangle(new Sakri.Geom.Point(this.a.x,this.a.y),new Sakri.Geom.Point(this.b.x,this.b.y),new Sakri.Geom.Point(this.c.x,this.c.y)); }; Sakri.Geom.Triangle.prototype.getSmallestX = function(){ return Math.min(this.a.x,this.b.x,this.c.x); }; Sakri.Geom.Triangle.prototype.getSmallestY = function(){ return Math.min(this.a.y,this.b.y,this.c.y); }; Sakri.Geom.Triangle.prototype.getBiggestX = function(){ return Math.max(this.a.x,this.b.x,this.c.x); }; Sakri.Geom.Triangle.prototype.getBiggestY = function(){ return Math.max(this.a.y,this.b.y,this.c.y); }; Sakri.Geom.Triangle.prototype.containsVertex = function(point){ //console.log("Sakri.Geom.Triangle.containsVertex",this.toString(),point.toString()); return (this.a.x==point.x && this.a.y==point.y) || (this.b.x==point.x && this.b.y==point.y) || (this.c.x==point.x && this.c.y==point.y); }; Sakri.Geom.Triangle.prototype.toString = function(){ return "toString() Triangle{a:"+this.a+" , b:"+this.b+" , c:"+this.c+"}"; }; Sakri.Geom.Triangle.prototype.containsVertex = function(point){ return (this.a.x==point.x && this.a.y==point.y) || (this.b.x==point.x && this.b.y==point.y) || (this.c.x==point.x && this.c.y==point.y); }; Sakri.Geom.Triangle.prototype.sharesEdge = function(triangle){ //console.log("Sakri.Geom.Triangle.sharesEdge",this.toString(),triangle.toString()); var sharedPoints=0; if(this.containsVertex(triangle.a)){ sharedPoints++; } if(this.containsVertex(triangle.b)){ sharedPoints++; } if(this.containsVertex(triangle.c)){ sharedPoints++; } //console.log("sharesEdge()",sharedPoints); return sharedPoints==2; }; Sakri.Geom.Triangle.createRandomTriangleInRect = function(rect){ var a = new Sakri.Geom.Point(rect.x + Math.random() * rect.width, rect.y + Math.random() * rect.height); var b = new Sakri.Geom.Point(rect.x + Math.random() * rect.width, rect.y + Math.random() * rect.height); var c = new Sakri.Geom.Point(rect.x + Math.random() * rect.width, rect.y + Math.random() * rect.height); return new Sakri.Geom.Triangle(a,b,c); } Sakri.Geom.Triangle.mirrorTriangleInRectangle = function(triangle, rect){ //console.log("mirrorTriangleInRectangle() ",width,height); //console.log("\ttriangle : ",triangle.toString()); var a = Sakri.Geom.Triangle.mirrorPointInRectangle(triangle.a, rect); var b = Sakri.Geom.Triangle.mirrorPointInRectangle(triangle.b, rect); var c = Sakri.Geom.Triangle.mirrorPointInRectangle(triangle.c, rect); return new Sakri.Geom.Triangle.Triangle(a, b, c); //console.log("\ttriangle : ",triangle.toString()); } Sakri.Geom.Triangle.offsetTriangle = function(triangle, offset){ triangle.a.x += offset; triangle.a.y += offset; triangle.b.x += offset; triangle.b.y += offset; triangle.c.x += offset; triangle.c.y += offset; }	//==================================================	//===================::RECTANGLE::==================	//==================================================	Sakri.Geom.Rectangle = function (x, y, width, height){	this.update(x, y, width, height);	};	Sakri.Geom.Rectangle.prototype.update = function(x, y, width, height){	this.x = isNaN(x) ? 0 : x;	this.y = isNaN(y) ? 0 : y;	this.width = isNaN(width) ? 0 : width;	this.height = isNaN(height) ? 0 : height;	}; //TODO : doesn't work Sakri.Geom.Rectangle.prototype.inflate = function(x, y){ this.x -= isNaN(x) ? 0 : x; this.y -= isNaN(y) ? 0 : y; this.width += isNaN(x) ? 0 : x * 2; this.height += isNaN(y) ? 0 : y * 2; };	Sakri.Geom.Rectangle.prototype.updateToRect = function(rect){	this.x = rect.x;	this.y = rect.y;	this.width = rect.width;	this.height = rect.height;	};	Sakri.Geom.Rectangle.prototype.scaleX = function(scaleBy){	this.width *= scaleBy;	};	Sakri.Geom.Rectangle.prototype.scaleY = function(scaleBy){	this.height *= scaleBy;	};	Sakri.Geom.Rectangle.prototype.scale = function(scaleBy){	this.scaleX(scaleBy);	this.scaleY(scaleBy);	};	Sakri.Geom.Rectangle.prototype.getRight = function(){	return this.x + this.width;	};	Sakri.Geom.Rectangle.prototype.getBottom = function(){	return this.y + this.height;	}; Sakri.Geom.Rectangle.prototype.getCenter = function(){ return new Sakri.Geom.Point(this.getCenterX(), this.getCenterY()); }; Sakri.Geom.Rectangle.prototype.getCenterX = function(){ return this.x + this.width/2; }; Sakri.Geom.Rectangle.prototype.getCenterY=function(){ return this.y + this.height/2; }; Sakri.Geom.Rectangle.prototype.containsPoint = function(x, y){ return x >= this.x && y >= this.y && x <= this.getRight() && y <= this.getBottom(); }; Sakri.Geom.Rectangle.prototype.containsRect = function(rect){ return this.containsPoint(rect.x, rect.y) && this.containsPoint(rect.getRight(), rect.getBottom()); };	Sakri.Geom.Rectangle.prototype.isSquare = function(){	return this.width == this.height;	};	Sakri.Geom.Rectangle.prototype.isLandscape = function(){	return this.width > this.height;	};	Sakri.Geom.Rectangle.prototype.isPortrait = function(){	return this.width < this.height;	};	Sakri.Geom.Rectangle.prototype.getSmallerSide = function(){	return Math.min(this.width, this.height);	};	Sakri.Geom.Rectangle.prototype.getBiggerSide = function(){	return Math.max(this.width,this.height);	};	Sakri.Geom.Rectangle.prototype.getArea = function(){	return this.width * this.height;	};	Sakri.Geom.Rectangle.prototype.floor = function(){	this.x = Math.floor(this.x);	this.y = Math.floor(this.y);	this.width = Math.floor(this.width);	this.height = Math.floor(this.height);	};	Sakri.Geom.Rectangle.prototype.ceil = function(){	this.x = Math.ceil(this.x);	this.y = Math.ceil(this.y);	this.width = Math.ceil(this.width);	this.height = Math.ceil(this.height);	};	Sakri.Geom.Rectangle.prototype.round = function(){	this.x=Math.round(this.x);	this.y=Math.round(this.y);	this.width=Math.round(this.width);	this.height=Math.round(this.height);	};	Sakri.Geom.Rectangle.prototype.roundIn = function(){	this.x = Math.ceil(this.x);	this.y = Math.ceil(this.y);	this.width = Math.floor(this.width);	this.height = Math.floor(this.height);	};	Sakri.Geom.Rectangle.prototype.roundOut = function(){	this.x = Math.floor(this.x);	this.y = Math.floor(this.y);	this.width = Math.ceil(this.width);	this.height = Math.ceil(this.height);	};	Sakri.Geom.Rectangle.prototype.clone = function(){	return new Sakri.Geom.Rectangle(this.x, this.y, this.width, this.height);	};	Sakri.Geom.Rectangle.prototype.toString = function(){	return "Rectangle{x:"+this.x+" , y:"+this.y+" , width:"+this.width+" , height:"+this.height+"}";	};	//==================================================	//===========::ROUNDED RECTANGLE::==================	//==================================================	Sakri.Geom.RoundedRectangle = function (x, y , width, height, radius){ Sakri.Geom.Rectangle.call(this, x, y, width, height);	this.radius = isNaN(radius) ? 5 : radius;	}; //subclass extends superclass Sakri.Geom.RoundedRectangle.prototype = Object.create(Sakri.Geom.Rectangle.prototype); Sakri.Geom.RoundedRectangle.prototype.constructor = Sakri.Geom.Rectangle; Sakri.Geom.RoundedRectangle.prototype.drawPathToContext = function(context){ context.beginPath(); context.moveTo(this.x, this.y+this.radius); context.arc(this.x+this.radius, this.y+this.radius, this.radius, Math.PI,Math.PI+SakriMathUtil.HALF_PI); context.lineTo(this.getRight()-this.radius, this.y); context.arc(this.getRight()-this.radius, this.y+this.radius, this.radius, Math.PI+SakriMathUtil.HALF_PI, SakriMathUtil.PI2 ); context.lineTo(this.getRight(), this.getBottom()-this.radius); context.arc(this.getRight()-this.radius, this.getBottom()-this.radius, this.radius, 0, SakriMathUtil.HALF_PI ); context.lineTo(this.x+this.radius, this.getBottom()); context.arc(this.x+this.radius, this.getBottom()-this.radius, this.radius, SakriMathUtil.HALF_PI, Math.PI ); context.lineTo(this.x, this.y+this.radius); context.closePath(); }	Sakri.Geom.RoundedRectangle.prototype.toString = function(){	return "RoundedRectangle{x:"+this.x+" , y:"+this.y+" , width:"+this.width+" , height:"+this.height+" , radius:"+this.radius+"}";	};	Sakri.Geom.RoundedRectangle.prototype.clone = function(){	return new Sakri.Geom.RoundedRectangle(this.x,this.y,this.width,this.height,this.radius);	}; //================================================== //=====================::CIRCLE::=================== //================================================== Sakri.Geom.Circle = function (x,y,radius){ Sakri.Geom.Point.call(this,x,y); //call super constructor. this.radius = isNaN(radius) ? 10 : radius;// not sure if this should just be 0? }; //subclass extends superclass Sakri.Geom.Circle.prototype = Object.create(Sakri.Geom.Point.prototype); Sakri.Geom.Circle.prototype.constructor = Sakri.Geom.Point; Sakri.Geom.Circle.prototype.getRandomPointInCircle = function(){ var radius = Math.random() * this.radius; var radian = Math.random() * SimpleGeometry.PI2; var x = this.x + Math.cos(radian) * radius; var y = this.y + Math.sin(radian) * radius; return new Sakri.Geom.Point(x, y); }; Sakri.Geom.Circle.prototype.update = function(x,y,radius){ this.x = isNaN(x) ? this.x : x; this.y = isNaN(y) ? this.y : y; this.radius = isNaN(radius) ? this.radius : radius; } Sakri.Geom.Circle.prototype.clone = function(){ return new Sakri.Geom.Circle(this.x,this.y,this.radius); }; Sakri.Geom.Circle.prototype.containsPoint = function(point){ return Sakri.Geom.Point.distanceBetweenTwoPoints(point, this) <= this.radius; }; Sakri.Geom.Circle.prototype.toString = function(){ return "Circle{x : "+this.x+" , y : "+this.y+" , radius : "+this.radius+"}"; }; //================================================== //=====================::TRANSFORM::=================== //================================================== Sakri.Geom.setIdentityMatrixToContext = function(context){ context.setTransform(1,0,0,1,0,0); }; //(1,0,0,1,0,0); Sakri.Geom.Transform = function (scaleX, skewX, skewY, scaleY, tx, ty){ this.update(isNaN(scaleX) ? 1 : scaleX, isNaN(skewX) ? 0 : skewX, isNaN(skewY) ? 0 : skewY, isNaN(scaleY) ? 1 : scaleY, isNaN( tx) ? 0 : tx, isNaN(ty) ? 0 : ty); }; Sakri.Geom.Transform.prototype.update = function (scaleX, skewX, skewY, scaleY, tx, ty){ this.scaleX = scaleX; this.skewX = skewX; this.skewY = skewY; this.scaleY = scaleY; this.tx = tx; this.ty = ty; }; Sakri.Geom.Transform.prototype.toString = function() { return "SimpleGeometry.Transform{scaleX:"+this.scaleX+" ,skewX:"+this.skewX+" ,skewY:"+this.skewY+" ,scaleY:"+this.scaleY+" ,tx:"+this.tx+" ,ty:"+this.ty+"}"; };
}(window));
/** * Created by sakri on 27-1-14. */
(function (window){ var Sakri = window.Sakri || {}; window.Sakri = window.Sakri || Sakri; Sakri.CanvasTextUtil = {}; //this method renders text into a canvas, then resizes the image by shrinkPercent //loops through the non transparent pixels of the resized image and returns those as an array //fontProperties should be an object of type Sakri.CanvasTextProperties Sakri.CanvasTextUtil.createTextParticles = function(text, shrinkPercent, fontProps){ var renderCanvas = document.createElement('canvas'); var renderContext = renderCanvas.getContext('2d'); var fontString = fontProperties.getFontString(); //console.log(fontString); renderContext.font = fontString; renderContext.textBaseline = "top"; //console.log(renderContext.measureText(text).width); renderCanvas.width = renderContext.measureText(text).width; renderCanvas.height = fontProps.fontSize + 10;//TODO : Need to implement getFirstNonTransparentPixel() //after a resize of a canvas, we have to reset these properties renderContext.font = fontString; renderContext.textBaseline = "top"; //console.log(renderCanvas.width, renderCanvas.height); renderContext.fillStyle = "#FF0000"; renderContext.fillText(text, 0, 0); var shrunkenCanvas = document.createElement('canvas'); shrunkenCanvas.width = Math.round(renderCanvas.width*shrinkPercent); shrunkenCanvas.height = Math.round(renderCanvas.height*shrinkPercent); var shrunkenContext = shrunkenCanvas.getContext('2d'); shrunkenContext.drawImage(renderCanvas, 0, 0, shrunkenCanvas.width , shrunkenCanvas.height ); var pixels = shrunkenContext.getImageData(0, 0, shrunkenCanvas.width, shrunkenCanvas.height); var data = pixels.data; var particles = []; var i, x, y; for(i = 0; i < data.length; i += 4) { if(data[i]>200){ x = ((i/4)%shrunkenCanvas.width)/shrinkPercent; y = Math.floor((i/4)/shrunkenCanvas.width)/shrinkPercent; particles.push(new Sakri.Geom.Point(x, y)); } } delete renderCanvas; delete shrunkenCanvas; return particles; }; Sakri.CanvasTextUtil.createImagesFromString = function(string, fillStyle, strokeStyle, strokeWidth, fontProps){ var fontString = fontProps.getFontString(); var characters = string.split(""); var images = []; var canvas, context, image, metrics, i,character; canvas = document.createElement("canvas"); for(i=0; i<characters.length; i++){ character = characters[i]; context = canvas.getContext("2d"); context.textBaseline = "top"; context.font = fontString; metrics = context.measureText(character); canvas.width = metrics.width; canvas.height = fontProps.fontSize;// TODO : use getFirstNonTransparentPixel for dynamic sizing //these properties have to be set twice as they vanish after setting a canvas width and height context = canvas.getContext("2d"); context.textBaseline = "top"; context.font = fontString; image = new Image(); image.width = canvas.width; image.height = canvas.height; if(fillStyle){ context.fillStyle = fillStyle; context.fillText (character,0, 0); } if(strokeStyle){ context.strokeStyle = strokeStyle; context.lineWidth = strokeWidth; context.strokeText(character, 0, 0); } image.src = canvas.toDataURL(); images[i] = image; } delete canvas; return images; }; //========================================================================================= //==============::CANVAS TEXT PROPERTIES::==================================== //======================================================== Sakri.CanvasTextProperties = function(fontWeight, fontStyle, fontSize, fontFace){ this.setFontWeight(fontWeight); this.setFontStyle(fontStyle); this.setFontSize(fontSize); this.fontFace = fontFace ? fontFace : "sans-serif"; }; Sakri.CanvasTextProperties.NORMAL = "normal"; Sakri.CanvasTextProperties.BOLD = "bold"; Sakri.CanvasTextProperties.BOLDER = "bolder"; Sakri.CanvasTextProperties.LIGHTER = "lighter"; Sakri.CanvasTextProperties.ITALIC = "italic"; Sakri.CanvasTextProperties.OBLIQUE = "oblique"; Sakri.CanvasTextProperties.prototype.setFontWeight = function(fontWeight){ switch (fontWeight){ case Sakri.CanvasTextProperties.NORMAL: case Sakri.CanvasTextProperties.BOLD: case Sakri.CanvasTextProperties.BOLDER: case Sakri.CanvasTextProperties.LIGHTER: this.fontWeight = fontWeight; break; default: this.fontWeight = Sakri.CanvasTextProperties.NORMAL; } }; Sakri.CanvasTextProperties.prototype.setFontStyle = function(fontStyle){ switch (fontStyle){ case Sakri.CanvasTextProperties.NORMAL: case Sakri.CanvasTextProperties.ITALIC: case Sakri.CanvasTextProperties.OBLIQUE: this.fontStyle = fontStyle; break; default: this.fontStyle = Sakri.CanvasTextProperties.NORMAL; } }; Sakri.CanvasTextProperties.prototype.setFontSize = function(fontSize){ if(fontSize && fontSize.indexOf && fontSize.indexOf("px")>-1){ var size = fontSize.split("px")[0]; fontProperites.fontSize = isNaN(size) ? 24 : size;//24 is just an arbitrary number return; } this.fontSize = isNaN(fontSize) ? 24 : fontSize;//24 is just an arbitrary number }; Sakri.CanvasTextProperties.prototype.getFontString = function(){ return this.fontWeight + " " + this.fontStyle + " " + this.fontSize + "px " + this.fontFace; };
}(window));
/** * Created by sakri on 27-1-14. */
(function (window){ var Sakri = window.Sakri || {}; window.Sakri = window.Sakri || Sakri; Sakri.BitmapUtil = {}; //TODO : rename "canvas" to "source", if it's an img, create a canvas and draw the img into it Sakri.BitmapUtil.getFirstNonTransparentPixelTopDown = function(canvas){ var context = canvas.getContext("2d"); var y, i, rowData; for(y=0; y<canvas.height; y++){ rowData = context.getImageData(0, y, canvas.width, 1).data; for(i=0; i<rowData.length; i+=4){ if(rowData[i+0] + rowData[i+1] + rowData[i+2] + rowData[i+3] > 0){ return new Sakri.Geom.Point(i/4, y); } } } return null; }; Sakri.BitmapUtil.getFirstNonTransparentPixelBottomUp = function(canvas){ var context = canvas.getContext("2d"); var y, i, rowData; for(y = canvas.height-1; y>-1; y--){ rowData = context.getImageData(0, y, canvas.width, 1).data; for(i=0; i<rowData.length; i+=4){ if(rowData[i+0] + rowData[i+1] + rowData[i+2] + rowData[i+3] > 0){ return new Sakri.Geom.Point(i/4, y); } } } return null; }; Sakri.BitmapUtil.getFirstNonTransparentPixelLeftToRight = function(canvas){ var context = canvas.getContext("2d"); var x, i, colData; for(x = 0; x < canvas.width; x++){ colData = context.getImageData(x, 0, 1, canvas.height).data; for(i=0; i<colData.length; i+=4){ if(colData[i+0] + colData[i+1] + colData[i+2] + colData[i+3] > 0){ return new Sakri.Geom.Point(x, i/4); } } } return null; }; Sakri.BitmapUtil.getFirstNonTransparentPixelRightToLeft = function(canvas){ var context = canvas.getContext("2d"); var x, i, colData; for(x = canvas.width-1; x >-1; x--){ colData = context.getImageData(x, 0, 1, canvas.height).data; for(i=0; i<colData.length; i+=4){ if(colData[i+0] + colData[i+1] + colData[i+2] + colData[i+3] > 0){ return new Sakri.Geom.Point(x, i/4); } } } return null; }; //cuts out rows and columns of pixels without color data from the top, bottom, left and right Sakri.BitmapUtil.trimImage = function(image){ var trimCanvas = Sakri.BitmapUtil.createTrimmedCanvas(image); image.src = trimCanvas.toDataURL(); }; Sakri.BitmapUtil.trimCanvas = function(canvas){ console.log("trimCanvas()", canvas.width, canvas.height); var trimCanvas = Sakri.BitmapUtil.createTrimmedCanvas(canvas); canvas.width = trimCanvas.width; canvas.height = trimCanvas.height; console.log("\t=>" , canvas.width, canvas.height); var context = canvas.getContext("2d"); context.drawImage(trimCanvas, 0, 0); }; Sakri.BitmapUtil.getCanvasTrimRectangle = function(canvas){ var rect = new Sakri.Geom.Rectangle(); rect.x = Sakri.BitmapUtil.getFirstNonTransparentPixelLeftToRight(canvas).x; rect.y = Sakri.BitmapUtil.getFirstNonTransparentPixelTopDown(canvas).y; rect.width = Sakri.BitmapUtil.getFirstNonTransparentPixelRightToLeft(canvas).x - rect.x + 1; rect.height = Sakri.BitmapUtil.getFirstNonTransparentPixelBottomUp(canvas).y - rect.y + 1; return rect; } Sakri.BitmapUtil.createTrimmedCanvas = function(imageOrCanvas){ var trimCanvas = document.createElement("canvas"); var trimContext = trimCanvas.getContext("2d"); trimCanvas.width = imageOrCanvas.width; trimCanvas.height = imageOrCanvas.height; trimContext.drawImage(imageOrCanvas, 0, 0); var rect = Sakri.BitmapUtil.getCanvasTrimRectangle(trimCanvas); //console.log("createTrimmedCanvas() ", rect.toString()); trimCanvas.width = rect.width; trimCanvas.height = rect.height; trimContext = trimCanvas.getContext("2d"); trimContext.drawImage(imageOrCanvas, rect.x, rect.y, rect.width, rect.height, 0, 0, rect.width, rect.height); return trimCanvas; }; //capture rect is the content on canvas to be reflected, border defines the space between the original content and the reflection //captureRect must contain the properties x, y, width, height //For more interesting results add a gradient on top of the reflection Sakri.BitmapUtil.renderReflection = function(canvas, captureRect, border){ if(!border){ border = 5; } var context = canvas.getContext("2d"); context.save(); //move and flip vertically context.translate(captureRect.x, captureRect.y + captureRect.height*2 + border); context.scale(1, -1); context.drawImage(	canvas, captureRect.x, captureRect.y, captureRect.width, captureRect.height, 0, 0, captureRect.width, captureRect.height);//img,sx,sy,swidth,sheight,x,y,width,height context.restore(); };
}(window));
/** * Created by sakri on 2-2-14. */
(function (window){ var Sakri = window.Sakri || {}; window.Sakri = window.Sakri || Sakri; //================================================== //======================::VERLET POINT::============ //================================================== // VerletPoint extends Sakri.Geom.Point // constraint must be of type Sakri.Geom.Rectangle Sakri.VerletPoint = function(x, y, constraint) { //instanceof //console.log("Sakri.VerletPoint constructor ",x,y,constraint.toString()); this.constraint = constraint; this.oldPoint = new Sakri.Geom.Point(x, y); this.tempPoint = new Sakri.Geom.Point(); this.constraint = constraint; Sakri.Geom.Point.call(this, x, y); //call super constructor. }; //subclass extends superclass Sakri.VerletPoint.prototype = Object.create(Sakri.Geom.Point.prototype); Sakri.VerletPoint.prototype.constructor = Sakri.Geom.Point; Sakri.VerletPoint.prototype.update = function(){ this.tempPoint.x = this.x; this.tempPoint.y = this.y; this.x += (this.x - this.oldPoint.x); this.y += (this.y - this.oldPoint.y); this.oldPoint.x = this.tempPoint.x; this.oldPoint.y = this.tempPoint.y; this.constrain(); }; Sakri.VerletPoint.prototype.getMoveDistance = function(){ return Math.max( Math.abs(this.x - this.oldPoint.x), Math.abs(this.y - this.oldPoint.y) ); }; Sakri.VerletPoint.prototype.constrain = function(){ if(!this.constraint.containsPoint(this)){ if(this.x < this.constraint.x)this.x = this.constraint.x; if(this.x > this.constraint.getRight())this.x = this.constraint.getRight(); if(this.y < this.constraint.y)this.y = this.constraint.y; if(this.y > this.constraint.getBottom())this.y = this.constraint.getBottom(); } }; Sakri.VerletPoint.prototype.applyForce = function(x, y){ this.x += x; this.y += y; }; Sakri.VerletPoint.prototype.toString = function(){ return "VerletPoint{x:" + this.x + " , y:" + this.y + "}"; }; //================================================== //======================::VERLET STICK::============ //================================================== //a and b must be of type :VerletPoint instanceof Sakri.VerletStick = function(a, b){ this.a = a; this.b = b; this.restLength = Sakri.Geom.Point.distanceBetweenTwoPoints(this.a, this.b); this.dampen = 0.1;//seems this can't be much bigger than .2 }; Sakri.VerletStick.prototype.update = function(){ //console.log("VerletStick.update"); this.a.update(); this.b.update(); this.xdelta = this.a.x - this.b.x; this.ydelta = this.a.y - this.b.y; var stickLength = Sakri.Geom.Point.distanceBetweenTwoPoints(this.a, this.b); var diff = (stickLength - this.restLength) / stickLength; var product = this.xdelta * this.dampen * diff; this.a.x -= product; this.b.x += product; product = this.ydelta * this.dampen * diff; this.a.y -= product; this.b.y += product; //console.log("\t",this.a.toString(),this.b.toString()); //return Math.max(this.a.getMoveDistance(),this.b.getMoveDistance()); }; Sakri.VerletStick.prototype.applyForce = function(x, y){ //console.log("VerletStick.applyForce",x,y); this.a.applyForce(x, y); this.b.applyForce(x, y); }; Sakri.VerletStick.prototype.applyForceToEnd = function(x,y){ this.pointB.applyForce(x, y); }; Sakri.VerletStick.prototype.equals = function(stick){ if(this.a.equals(stick.pointA)){ return this.b.equals(stick.pointB); } if(this.b.equals(stick.pointA)){ return this.a.equals(stick.pointB); } return false; }; Sakri.VerletStick.prototype.renderToContext = function(context2d){ context2d.moveTo(this.a.x, this.a.y); context2d.lineTo(this.b.x, this.b.y); }; Sakri.VerletStick.prototype.toString = function(){ return "VerletStick{a:"+this.a+" , b:"+this.b+"}"; }; //================================================== //======================::VERLET STAGE::============ //================================================== // arena must be of typeSakri.Geom.Rectangle Sakri.VerletStage = function(context2d, arena, updateItems, autoStart, renderGraphics) { //instanceof //console.log("Sakri.Verlet.VerletStage constructor "); this.gravity = .01; this.context2d = context2d; this.arena = arena; this.updateItems = updateItems; this.running = false; this.renderGraphics = renderGraphics; //if(autoStart == true)this.start(); }; Sakri.VerletStage.prototype.update = function(){ //console.log("Sakri.VerletStage.prototype.update" , this.renderGraphics); if(this.renderGraphics){ this.updateAndRender(); return; } for(var i = 0; i<this.updateItems.length; i++){ this.updateItems[i].applyForce(0, this.gravity); this.updateItems[i].update(); } }; Sakri.VerletStage.prototype.updateAndRender = function(){ //this.context2d.fillStyle = "#aaaaaa"; //this.context2d.fillRect(this.arena.x,this.arena.y,this.arena.width,this.arena.height); this.context2d.strokeStyle = "#FF0000"; this.context2d.beginPath(); for(var i = 0; i<this.updateItems.length; i++){ this.updateItems[i].applyForce(0, this.gravity); this.updateItems[i].update(); this.updateItems[i].renderToContext(this.context2d); } this.context2d.stroke(); this.context2d.closePath(); }; Sakri.VerletStage.prototype.start = function(){ //console.log("VerletStage.start()"); if(this.running){ return; } this.gameLoop(); this.running = true; }; Sakri.VerletStage.prototype.gameLoop = function() { //console.log("Sakri.Verlet.VerletStage.gameLoop() "); var scope = this; window.setTimeout(function(){scope.gameLoop();}, 20); this.update(); }; Sakri.VerletStage.prototype.stop = function(){ this.running = false; }; //================================================== //======================::VERLET GRID::============ //================================================== Sakri.VerletGrid = function(width,height,rows,columns,constrain){ if(!rows || !columns){ throw new Error("VerletSquare Error : a square cannot have 0 rows or columns"); } this.gridWidth = width; this.gridHeight = height; //one cell is considered as a row or column this.rows = rows + 1; this.columns = columns + 1; this.constrain = constrain; this.lockedPoint = null; this.createVerletPoints(); this.connectVerletSticks(); }; Sakri.VerletGrid.prototype.createVerletPoints = function(){ var rect = new Sakri.Geom.Rectangle(0, 0, this.gridWidth / (this.columns - 1), this.gridHeight / (this.rows - 1) ); var total = this.rows * this.columns; this.points = []; for(var i = 0;i<total;i++){ this.points[i] = new Sakri.VerletPoint( (i % this.columns) * rect.width,Math.floor(i / this.columns) * rect.height , this.constrain); //trace(points[i],i,i/rows,Math.floor(i/rows),Math.floor(i/columns)*rect.height); } }; Sakri.VerletGrid.prototype.connectVerletSticks = function(){ this.sticks = []; this.connectHorizontalVerletSticks(); this.connectVerticalVerletSticks(); this.connectDiagonalVerletSticks(); }; Sakri.VerletGrid.prototype.connectHorizontalVerletSticks = function(){ var total = this.points.length; var stick, pointB, i; var pointA = this.points[0]; for(i = 1; i<total; i++){ pointB = this.points[i]; if(i % this.columns>0){ this.sticks.push(new Sakri.VerletStick(pointA, pointB)); } pointA = pointB; } }; Sakri.VerletGrid.prototype.connectVerticalVerletSticks = function(){ var total = this.points.length - this.columns; var stick, pointA, pointB, i; for(i = 0; i < total; i++){ pointA = this.points[i]; pointB = this.points[i + this.columns]; this.sticks.push(new Sakri.VerletStick(pointA, pointB)); } }; Sakri.VerletGrid.prototype.connectDiagonalVerletSticks = function(){ var total = this.points.length-this.columns; var stick, pointA, pointB, i; var skip = this.columns - 1; for(i = 0; i<total ;i++){ if(i % this.columns == skip){ continue; } pointA = this.points[i]; pointB = this.points[i+ this.columns + 1]; this.sticks.push(new Sakri.VerletStick(pointA, pointB)); } };
}(window));
//========================
//general properties for demo set up
//========================
var canvas;
var context;
var canvasContainer;
var htmlBounds;
var bounds;
var minimumStageWidth = 250;
var minimumStageHeight = 250;
var maxStageWidth = 900;
var maxStageHeight = 600;
var resizeTimeoutId = -1;
function init(){ canvas = document.createElement('canvas'); canvas.style.position = "absolute"; context = canvas.getContext("2d"); canvasContainer = document.getElementById("canvasContainer"); canvasContainer.appendChild(canvas); window.onresize = resizeHandler; commitResize();
}
function getWidth( element ){return Math.max(element.scrollWidth,element.offsetWidth,element.clientWidth );}
function getHeight( element ){return Math.max(element.scrollHeight,element.offsetHeight,element.clientHeight );}
//avoid running resize scripts repeatedly if a browser window is being resized by dragging
function resizeHandler(){ context.clearRect(0,0,bounds.width, bounds.height); clearTimeoutsAndIntervals(); clearTimeout (resizeTimeoutId); resizeTimeoutId = setTimeout(function(){commitResize();}, 300 );
}
function commitResize(){ htmlBounds = new Sakri.Geom.Rectangle(0,0, getWidth(this.canvasContainer) , getHeight(canvasContainer)); if(htmlBounds.width >= maxStageWidth){ canvas.width = maxStageWidth; canvas.style.left = htmlBounds.getCenterX() - (maxStageWidth/2)+"px"; }else{ canvas.width = htmlBounds.width; canvas.style.left ="0px"; } if(htmlBounds.height > maxStageHeight){ canvas.height = maxStageHeight; canvas.style.top = htmlBounds.getCenterY() - (maxStageHeight/2)+"px"; }else{ canvas.height = htmlBounds.height; canvas.style.top ="0px"; } bounds = new Sakri.Geom.Rectangle(0,0, canvas.width, canvas.height); context = canvas.getContext("2d"); context.clearRect(0,0,bounds.width, bounds.height); if(bounds.width<minimumStageWidth || bounds.height<minimumStageHeight){ stageTooSmallHandler(); return; } startDemo();
}
function stageTooSmallHandler(){ var warning = "Sorry, bigger screen required :("; var props = new Sakri.CanvasTextProperties(null,null,24); context.font = props.getFontString(); context.fillStyle = "#000000"; context.fillText(warning, bounds.getCenterX() - context.measureText(warning).width/2, bounds.getCenterY()-12);
}
var readyStateCheckInterval = setInterval( function() { if (document.readyState === "complete") { clearInterval(readyStateCheckInterval); init(); }
}, 10);
//========================
//Demo specific properties
//========================
function clearTimeoutsAndIntervals(){ clearInterval(bounceIntervalId); animating = false;
}
var animating = false;
var bounceIntervalId = -1;
var sourceImage;
var sourceImageRect;
var fillPattern;
var characterCanvas;
var renderCanvas;
var triangleCanvas;
var triangleContext;
var words = ["SAKRI", "JELLO"];
var currentText;
var textColor = "#0c0d43";
var backgroundColor = "#0c0d43";
var strokeColor = "#000000";
var strokeWidth = 3;
var rows = 2;
var cols = 4;
var cellWidth;
var cellHeight;
var verletStage;
var verletGrid;
var triangles;
var verletTriangles;
var reverseVerletTriangles;
var fontProperties = new Sakri.CanvasTextProperties(Sakri.CanvasTextProperties.BOLD, null, 160);
var colors = ["#698384", "#841118", "#c26c2e", "#517c24", "#cca835", "#166586", "#9ba78c", "#b68679"];
function getBGColor(textColor){ var color = colors[Math.floor(Math.random()*colors.length)]; while(color==textColor){ color = colors[Math.floor(Math.random()*colors.length)]; } return color;
}
function startDemo(){ textColor = colors[Math.floor(Math.random()*colors.length)]; backgroundColor = getBGColor(textColor); currentText = words[Math.floor(Math.random()*words.length)]; createSourceImage();//image containing text cellWidth = sourceImage.width / cols; cellHeight = sourceImage.height / rows; createTriangleCanvas(); sourceImageRect = new Sakri.Geom.Rectangle(0, 0, sourceImage.width, sourceImage.height); setUpVerletGrid(); createImageTriangles(); verletGrid.sticks[0].applyForce(1 + Math.random() * 8, 0); bounceIntervalId = setInterval(bounce, 4000); animating = true; loop();
}
function loop(){ drawScreen(); if(animating){ window.requestAnimationFrame(loop, canvas); }
}
function bounce(){ for(var i = 0; i < verletGrid.sticks.length; i++){ verletGrid.sticks[i].applyForce(0 , -Math.random()); } verletGrid.sticks[0].applyForce((Math.random()>.5 ? 1 : -1) * Math.random() * 5, 0);//a bit of horizontal force as well
}
//The demo only works if the source image has no transparent pixels. Somhow they screw up context.createPattern()
//The text therefore first rendered with no bg
//then the white space is trimmed
//then it's rerendered with a background using the dimensions from the trimmed version.
function createSourceImage(){ var renderCanvas = document.createElement('canvas'); var renderContext = renderCanvas.getContext("2d"); renderContext.clearRect(0,0,renderCanvas.width, renderCanvas.height); renderContext.font = fontProperties.getFontString(); renderContext.textBaseline = "top"; renderCanvas.width = renderContext.measureText(currentText).width; renderCanvas.height = fontProperties.fontSize; renderContext = renderCanvas.getContext("2d"); renderContext.fillStyle = textColor; renderContext.font = fontProperties.getFontString(); renderContext.textBaseline = "top"; renderContext.fillText(currentText,0,0); renderContext.strokeStyle = "#000000"; renderContext.lineWidth = strokeWidth; renderContext.strokeText(currentText,0,0); //context.drawImage(renderCanvas,0,0); var rect = Sakri.BitmapUtil.getCanvasTrimRectangle(renderCanvas); sourceImage = document.createElement('canvas'); sourceImage.width = rect.width; sourceImage.height = rect.height; var sourceImageContext = sourceImage.getContext("2d"); sourceImageContext.fillStyle = backgroundColor; sourceImageContext.fillRect(0, 0, rect.width, rect.height); sourceImageContext.drawImage(renderCanvas, rect.x, rect.y, rect.width, rect.height, 0, 0, rect.width, rect.height);
}
function createTriangleCanvas(){ triangleCanvas = document.createElement("canvas"); triangleCanvas.width = cellWidth; triangleCanvas.height = cellHeight; triangleContext = triangleCanvas.getContext("2d");
}
function setUpVerletGrid(){ var constrain = new Sakri.Geom.Rectangle(bounds.x, bounds.y, bounds.width, bounds.height); verletGrid = new Sakri.VerletGrid(sourceImage.width, sourceImage.height, rows, cols, constrain); verletStage = new Sakri.VerletStage(context, constrain, verletGrid.sticks, false, false);
}
function getMatchingVerletPoint(point){ var verlet, i; for(i = 0; i < verletGrid.points.length; i++){ verlet = verletGrid.points[i]; if(verlet.x == point.x && verlet.y == point.y){ return verlet; } }
}
function matchVerletPointsToTriangle(triangle){ var match = new Sakri.Geom.Triangle(getMatchingVerletPoint(triangle.a), getMatchingVerletPoint(triangle.b), getMatchingVerletPoint(triangle.c)); //console.log(match.toString()); return match;
}
function createImageTriangles(){ var triangle, reverseTriangle, i, j; triangles = []; verletTriangles = []; reverseVerletTriangles = []; for(i = 0; i < rows; i++){ for(j = 0; j < cols; j++){ triangle = new Sakri.Geom.Triangle(new Sakri.Geom.Point(j * cellWidth, i * cellHeight), new Sakri.Geom.Point(j * cellWidth+cellWidth, i * cellHeight), new Sakri.Geom.Point(j * cellWidth, i * cellHeight + cellHeight) ); triangles.push(triangle); //console.log(triangle.toString()); reverseTriangle = new Sakri.Geom.Triangle(new Sakri.Geom.Point(triangle.b.x, triangle.c.y), new Sakri.Geom.Point(triangle.c.x, triangle.c.y), new Sakri.Geom.Point(triangle.b.x, triangle.b.y) ); verletTriangles.push(matchVerletPointsToTriangle(triangle)); reverseVerletTriangles.push(matchVerletPointsToTriangle(reverseTriangle)); } }
}
function drawScreen(){ //console.log("drawScreen()"); context.fillStyle = backgroundColor; context.fillRect(0, 0, bounds.width, bounds.height); renderTriangles(); verletStage.update();
}
function renderTriangles(){ //console.log("renderTriangles()", triangles.length); for(var i = 0; i < triangles.length; i++){ context.save(); renderTriangle(triangles[i], verletTriangles[i]); context.restore(); context.save(); renderReverseTriangle(triangles[i], reverseVerletTriangles[i]); context.restore(); }
}
/*function transformToString(t){ return "(scaleX:" + t.scaleX + " , skewX:" + t.skewX + " , skewY:" + t.skewY + " , scaleY:" + t.scaleY + " , " + t.tx + " , " + t.ty + ")";
}*/
//TOP TRIANGLE
function renderTriangle(sourceTriangle, targetTriangle){ //console.log("renderTriangle()", sourceTriangle.toString(), targetTriangle.toString()); //console.log("renderTriangle()", context, triangleCanvas); triangleContext.drawImage(sourceImage, -sourceTriangle.a.x, -sourceTriangle.a.y); fillPattern = context.createPattern(triangleCanvas, 'no-repeat'); context.fillStyle = fillPattern; //context.fillStyle = "#FF0000"; updateTriangleTransform(sourceTriangle, targetTriangle); context.setTransform(transform.scaleX, transform.skewX, transform.skewY, transform.scaleY, transform.tx, transform.ty); context.beginPath(); //+1s and -1s are adjustments to avoid empty space between rendered triangles context.moveTo(-1, -1); context.lineTo(triangles[0].b.x + 1, triangles[0].b.y - 1); context.lineTo(-1, triangles[0].c.y + 1); context.lineTo(-1, -1); context.closePath(); context.fill();
}
//BOTTOM TRIANGLE
function renderReverseTriangle(sourceTriangle, targetTriangle){ updateReflectedTriangle(targetTriangle); triangleContext.drawImage(sourceImage, -sourceTriangle.a.x, -sourceTriangle.a.y); fillPattern = context.createPattern(triangleCanvas, 'no-repeat'); context.fillStyle = fillPattern; //context.fillStyle = "#00FF00"; updateTriangleTransform(sourceTriangle, reflectedTriangle); //console.log("transform : ",transformToString(transform)); context.setTransform(transform.scaleX, transform.skewX, transform.skewY, transform.scaleY, transform.tx, transform.ty); context.beginPath(); //+1s and -1s are adjustments to avoid empty space between rendered triangles context.moveTo(triangles[0].b.x, triangles[0].b.y - 1); context.lineTo(triangles[0].b.x, triangles[0].c.y + 1); context.lineTo(-1, triangles[0].c.y + 1); context.lineTo(triangles[0].b.x, triangles[0].b.y - 1); context.closePath(); context.fill();
}
var transform = new Object();
function updateTriangleTransform(sourceTriangle, transformedTriangle){ var xAngle = Sakri.Geom.Point.angleBetweenTwoPoints( transformedTriangle.b, transformedTriangle.a ); var yAngle = Sakri.Geom.Point.angleBetweenTwoPoints( transformedTriangle.c, transformedTriangle.a ); var transformedWidth = Sakri.Geom.Point.distanceBetweenTwoPoints(transformedTriangle.a, transformedTriangle.b); var transformedHeight = Sakri.Geom.Point.distanceBetweenTwoPoints( transformedTriangle.a, transformedTriangle.c ); var xScale = transformedWidth / (sourceTriangle.b.x - sourceTriangle.a.x); var yScale = transformedHeight / (sourceTriangle.c.y - sourceTriangle.a.y); transform.scaleX = Math.cos(xAngle) * xScale; transform.scaleY = Math.sin(yAngle) * yScale; transform.skewX = Math.sin(xAngle) * xScale; transform.skewY = Math.cos(yAngle) * yScale; transform.tx = transformedTriangle.a.x; transform.ty = transformedTriangle.a.y; //console.log("updateTriangleTransfor() ",transform.scaleX, transform.scaleY, transform.skewX, transform.skewY, transform.tx, transform.ty);
}
var reflectedTriangle = new Sakri.Geom.Triangle();
//receives a "bottom verlet" triangle
function updateReflectedTriangle(transformedTriangle){ reflectedTriangle.b = transformedTriangle.c; reflectedTriangle.c = transformedTriangle.b; var angleAC = Sakri.Geom.Point.angleBetweenTwoPoints( transformedTriangle.c, transformedTriangle.a ); var radius = Sakri.Geom.Point.distanceBetweenTwoPoints(transformedTriangle.a, transformedTriangle.c); reflectedTriangle.a.x = transformedTriangle.b.x + Math.cos(angleAC) * radius; reflectedTriangle.a.y = transformedTriangle.b.y + Math.sin(angleAC) * radius;
}
Jello Text - Script Codes
Jello Text - Script Codes
Home Page Home
Developer Sakri Rosenstrom
Username sakri
Uploaded September 13, 2022
Rating 3.5
Size 12,971 Kb
Views 30,360
Do you need developer help for Jello Text?

Find the perfect freelance services for your business! Fiverr's mission is to change how the world works together. Fiverr connects businesses with freelancers offering digital services in 500+ categories. Find Developer!

Sakri Rosenstrom (sakri) Script Codes
Create amazing Facebook ads with AI!

Jasper is the AI Content Generator that helps you and your team break through creative blocks to create amazing, original content 10X faster. Discover all the ways the Jasper AI Content Platform can help streamline your creative workflows. Start For Free!