Circular Wander Text

Size
9,998 Kb
Views
20,240

How do I make an circular wander text?

Small text wanders around the canvas, rendering a bigger text as it goes. A little patience, it can take a moment ;) It uses the circular wander I explained in this Pen : http://codepen.io/sakri/pen/emBKv. What is a circular wander text? How do you make a circular wander text? This script and codes were developed by Sakri Rosenstrom on 13 September 2022, Tuesday.

Circular Wander Text Previews

Circular Wander Text - Script Codes HTML Codes

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

Circular Wander Text - Script Codes CSS Codes

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

Circular Wander Text - Script Codes JS Codes

/* * * @author Sakri Rosenstrom * * Usees the Circular Wander explained here: https://codepen.io/sakri/pen/emBKv * * 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+"}"; };	//==================================================	//===================::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+"}";	}; //================================================== //=====================::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+"}"; };
}(window));
/** * Created by sakri on 15-1-14. */
//has a dependency on MathUtil, Geom,
(function (window){ var Sakri = window.Sakri || {}; window.Sakri = window.Sakri || Sakri; //bounds is a Rectangle, startingPoint is a Point //TODO : speed should have some documentation? in pixels? Sakri.CircularWander = function(bounds, speed, startingPoint ){ this.minimumRadius = 10; //circles that particles travel along cannot be smaller than this this.minCircleRotation = Math.PI/10;//minimum number of radians that a particle travels along a circle before switching to a new one this.maxCircleRotation = Sakri.MathUtil.PI2;//max number of radians that a particle travels along a circle before switching to a new one this.updates = 0;//current number of "moves" this.bounds = bounds; this.speed = speed; this.validateStartingPoint(startingPoint); this.setStartCircle(); this.radiusLine = new Sakri.Geom.Line();//used for creating circles this.circleFinder = new Sakri.LargestCircleInBoundsForRadiusFinder();//TODO make LargestCircleInBoundsForRadiusFinder a "static" class this.setNextChangeTarget(); }; Sakri.CircularWander.prototype.validateStartingPoint = function(startingPoint){ if(!startingPoint || !this.bounds.containsPoint(startingPoint)){ this.position = this.bounds.getCenter(); return; } this.position = new Sakri.Geom.Point(startingPoint.x, startingPoint.y); } Sakri.CircularWander.prototype.setStartCircle = function(){ this.currentRadian = Math.random() * Sakri.MathUtil.PI2; this.currentCircle = new Sakri.Geom.Circle(0, 0, this.minimumRadius); this.currentCircle.x = this.position.x + Math.cos(this.currentRadian+Math.PI) * this.minimumRadius; this.currentCircle.y = this.position.y + Math.sin(this.currentRadian+Math.PI) * this.minimumRadius; } Sakri.CircularWander.prototype.update = function(){ this.currentRadian += this.updateAngleIncrement; this.currentRadian = Sakri.MathUtil.constrainRadianTo2PI(this.currentRadian); this.position.x = this.currentCircle.x + Math.cos(this.currentRadian) * this.currentCircle.radius; this.position.y = this.currentCircle.y + Math.sin(this.currentRadian) * this.currentCircle.radius; if(this.shiftToCenter){ this.currentCircle.radius.x += this.bounds.getCenterX() < this.position.x ? -2 : 2; this.currentCircle.radius.y += this.bounds.getCenterY() < this.position.y ? -2 : 2; } this.updates++; if(this.updates >= this.updatesBeforeNextCircle){ this.setNextCircle(); } }; Sakri.CircularWander.prototype.setNextCircle = function(){ this.radiusLine.pointA.update(this.currentCircle.x, this.currentCircle.y); this.radiusLine.pointB.update(this.position.x, this.position.y); this.radiusLine.updateLineProperties(); var nextCircle = this.circleFinder.findLargestFittingCircle(this.bounds, this.radiusLine, this.currentRadian); //if the radius is too small, just keep rotating until an option big enough becomes available if(nextCircle.radius < this.minimumRadius){ this.updatesBeforeNextCircle = 10; this.shiftToCenter = true; this.updates = 0; return; } //determine direction if(!nextCircle.containsPoint(this.currentCircle) && !this.currentCircle.containsPoint(nextCircle)){ this.speed *= -1; this.currentRadian = Sakri.MathUtil.constrainRadianTo2PI(this.currentRadian - Math.PI); } //set to a random size within the circle nextCircle.radius = Sakri.MathUtil.getRandomNumberInRange(this.minimumRadius, nextCircle.radius); var angle = Math.atan2( nextCircle.y - this.position.y, nextCircle.x - this.position.x ); nextCircle.x = this.position.x + Math.cos(angle) * nextCircle.radius; nextCircle.y = this.position.y + Math.sin(angle) * nextCircle.radius; this.currentCircle.update(nextCircle.x, nextCircle.y, nextCircle.radius); this.setNextChangeTarget(); }; //sets speed and duration of stay within current circle Sakri.CircularWander.prototype.setNextChangeTarget = function(){ var circumference = Math.PI * (this.currentCircle.radius * 2); var moveDistance = Sakri.MathUtil.getRandomNumberInRange(this.minCircleRotation, this.maxCircleRotation); var totalDist = (moveDistance / Sakri.MathUtil.PI2) * circumference; this.updatesBeforeNextCircle = Math.ceil(totalDist / Math.abs(this.speed));//number of moves before switching circles, TODO rename this.updateAngleIncrement = (moveDistance / this.updatesBeforeNextCircle) * (this.speed > 0 ? 1 : -1); this.updates = 0; }; //============================================================================= //===================::LARGEST CIRCLE IN BOUNDS FOR RADIUS FINDER::============ //============================================================================= //I know I know, lame ass class name... Sakri.LargestCircleInBoundsForRadiusFinder = function(){ this.nextCircleOption1 = new Sakri.Geom.Circle(); this.nextCircleOption2 = new Sakri.Geom.Circle(); this.candidateCircleA= new Sakri.Geom.Circle(); this.candidateCircleB = new Sakri.Geom.Circle(); this.angleZone = 0;//all options can be calculated within 90° "zones" this.vertexAngle = 0;//isosceles triangles have one unique angle called the vertex angle this.baseAngle = 0;//isosceles triangles have two equal angles called the base angles this.baseRadian = 0;//angle of isosceles triangle base line, used to calculate intersection with a boundary wall this.candidateDiameter = 0;//diameter of maximum size circle between "point" and a wall this.smaller = 0; }; Sakri.LargestCircleInBoundsForRadiusFinder.prototype.findLargestFittingCircle = function(bounds, radiusLine, radian){ var nextCircle = this.findLargestFittingCircleForRadian(bounds, radiusLine, radian); if(nextCircle){ this.nextCircleOption1.update(nextCircle.x, nextCircle.y, nextCircle.radius); }else{ this.nextCircleOption1.radius = 0;//0 is used to check if valid option TODO: change, not immediately clear why this is done } nextCircle = this.findLargestFittingCircleForRadian(bounds, radiusLine, Sakri.MathUtil.constrainRadianTo2PI(radian-Math.PI)); if(nextCircle){ this.nextCircleOption2.update(nextCircle.x, nextCircle.y, nextCircle.radius); }else{ this.nextCircleOption2.radius = 0;//0 is used to check if valid option } if(!this.nextCircleOption1.radius && !this.nextCircleOption2.radius){ throw new Error("NO CIRCLE CANDIDATES AVAILABLE?!");//obviously this should never happen. If it does then I have some bug huntin' to do } if(!this.nextCircleOption1.radius){ return this.nextCircleOption2; } if(!this.nextCircleOption2.radius){ return this.nextCircleOption1; } return this.nextCircleOption1.radius>this.nextCircleOption2.radius ? this.nextCircleOption1 : this.nextCircleOption2;//bigger one } Sakri.LargestCircleInBoundsForRadiusFinder.prototype.findLargestFittingCircleForRadian = function(bounds, radiusLine, radian){ var point = radiusLine.pointB;//pointB is travelling particle, pointA is the current center Point //HORIZONTAL AND VERTICAL CASES switch(radian){ //horizontal case 0: this.candidateCircleA.x = point.x + (bounds.getRight() - point.x)/2; this.candidateCircleA.y = point.y; this.candidateCircleA.radius = bounds.getRight() - this.candidateCircleA.x; return; case Math.PI: this.candidateCircleA.x = bounds.x + (point.x-bounds.x)/2; this.candidateCircleA.y = point.y; this.candidateCircleA.radius = this.candidateCircleA.x-bounds.x; return; //vertical case Sakri.MathUtil.HALF_PI: this.candidateCircleA.x = point.x; this.candidateCircleA.y = point.y + (bounds.getBottom() - point.y)/2; this.candidateCircleA.radius = bounds.getBottom() - this.candidateCircleA.y; return; case Sakri.MathUtil.PI_AND_HALF: this.candidateCircleA.x = point.x; this.candidateCircleA.y = bounds.y + (point.y-bounds.y)/2; this.candidateCircleA.radius = this.candidateCircleA.y - bounds.y; return; } //ALL OTHER ANGLES this.angleZone = Math.floor(radian / Sakri.MathUtil.HALF_PI);//all options can be calculated within 90° "zones" switch(this.angleZone){ case 0: //Candidate from right wall this.vertexAngle = Math.PI - radian; //180 - radian this.baseAngle = (Math.PI - this.vertexAngle) / 2;//180 minus vertex angle divided by 2 this.baseRadian = radian - this.baseAngle; this.candidateDiameter = (bounds.getRight() - point.x) / Math.cos(this.baseRadian); this.candidateCircleA.y = point.y + Math.sin(this.baseRadian) * this.candidateDiameter; this.candidateCircleA.x = radiusLine.getXatY(this.candidateCircleA.y); this.candidateCircleA.radius = Sakri.Geom.Point.distanceBetweenTwoPoints(point, this.candidateCircleA);//Circle extends point //Candidate from bottom wall this.vertexAngle = Sakri.MathUtil.HALF_PI + radian; //90 + radian this.baseAngle = (Math.PI - this.vertexAngle) / 2;//180 minus vertex angle divided by 2 this.baseRadian = radian + this.baseAngle; this.candidateDiameter = (bounds.getBottom() - point.y) / Math.sin(this.baseRadian); this.candidateCircleB.x = point.x + Math.cos(this.baseRadian) * this.candidateDiameter; this.candidateCircleB.y = radiusLine.getYatX(this.candidateCircleB.x); this.candidateCircleB.radius = Sakri.Geom.Point.distanceBetweenTwoPoints(point, this.candidateCircleB);//Circle extends point break; case 1: //Candidate from left wall this.vertexAngle = radian; this.baseAngle = (Math.PI - this.vertexAngle) / 2;//180 minus vertex angle divided by 2 this.baseRadian = radian + this.baseAngle; this.candidateDiameter = (bounds.x - point.x) / Math.cos(this.baseRadian); this.candidateCircleA.y = point.y + Math.sin(this.baseRadian) * this.candidateDiameter; this.candidateCircleA.x = radiusLine.getXatY(this.candidateCircleA.y); this.candidateCircleA.radius = Sakri.Geom.Point.distanceBetweenTwoPoints(point, this.candidateCircleA);//Circle extends point //Candidate from bottom wall this.vertexAngle = Math.PI-radian + Sakri.MathUtil.HALF_PI; this.baseAngle = (Math.PI - this.vertexAngle) / 2;//180 minus vertex angle divided by 2 this.baseRadian = radian - this.baseAngle; this.candidateDiameter = (bounds.getBottom() - point.y) / Math.sin(this.baseRadian); this.candidateCircleB.x = point.x + Math.cos(this.baseRadian) * this.candidateDiameter; this.candidateCircleB.y = radiusLine.getYatX(this.candidateCircleB.x); this.candidateCircleB.radius = Sakri.Geom.Point.distanceBetweenTwoPoints(point, this.candidateCircleB);//Circle extends point break; case 2: //Candidate from left wall this.vertexAngle = Sakri.MathUtil.PI2 - radian;//360 - angle this.baseAngle = (Math.PI - this.vertexAngle) / 2;//180 minus vertex angle divided by 2 this.baseRadian = radian - this.baseAngle; this.candidateDiameter = (bounds.x - point.x) / Math.cos(this.baseRadian); this.candidateCircleA.y = point.y + Math.sin(this.baseRadian) * this.candidateDiameter; this.candidateCircleA.x = radiusLine.getXatY(this.candidateCircleA.y); this.candidateCircleA.radius = Sakri.Geom.Point.distanceBetweenTwoPoints(point, this.candidateCircleA);//Circle extends point //Candidate from top wall this.vertexAngle = radian - Math.PI + Sakri.MathUtil.HALF_PI; this.baseAngle = (Math.PI - this.vertexAngle) / 2;//180 minus vertex angle divided by 2 this.baseRadian = radian + this.baseAngle; this.candidateDiameter = (bounds.y - point.y) / Math.sin(this.baseRadian); this.candidateCircleB.x = point.x + Math.cos(this.baseRadian) * this.candidateDiameter; this.candidateCircleB.y = radiusLine.getYatX(this.candidateCircleB.x); this.candidateCircleB.radius = Sakri.Geom.Point.distanceBetweenTwoPoints(point, this.candidateCircleB);//Circle extends point break; case 3: //Candidate from right wall this.vertexAngle = Math.PI - (Sakri.MathUtil.PI2-radian); this.baseAngle = (Math.PI - this.vertexAngle) / 2;//180 minus vertex angle divided by 2 this.baseRadian = radian + this.baseAngle; this.candidateDiameter = (bounds.getRight() - point.x) / Math.cos(this.baseRadian); this.candidateCircleA.y = point.y + Math.sin(this.baseRadian) * this.candidateDiameter; this.candidateCircleA.x = radiusLine.getXatY(this.candidateCircleA.y); this.candidateCircleA.radius = Sakri.Geom.Point.distanceBetweenTwoPoints(point, this.candidateCircleA);//Circle extends point //Candidate from top wall this.vertexAngle = Sakri.MathUtil.PI2 - radian + Sakri.MathUtil.HALF_PI; this.baseAngle = (Math.PI - this.vertexAngle) / 2;//180 minus vertex angle divided by 2 this.baseRadian = radian - this.baseAngle; this.candidateDiameter = (bounds.y - point.y) / Math.sin(this.baseRadian); this.candidateCircleB.x = point.x + Math.cos(this.baseRadian) * this.candidateDiameter; this.candidateCircleB.y = radiusLine.getYatX(this.candidateCircleB.x); this.candidateCircleB.radius = Sakri.Geom.Point.distanceBetweenTwoPoints(point, this.candidateCircleB);//Circle extends point break; } this.candidateCircleA.radius *= .95;//small margin to make sure the circle doesn't exceed borders this.candidateCircleB.radius *= .95;//small margin to make sure the circle doesn't exceed borders var containsA = Sakri.LargestCircleInBoundsForRadiusFinder.rectangleContainsCircle(bounds, this.candidateCircleA); var containsB = Sakri.LargestCircleInBoundsForRadiusFinder.rectangleContainsCircle(bounds, this.candidateCircleB); if(!containsA && !containsB){ return null; } if(!containsA){ return this.candidateCircleB; } if(!containsB){ return this.candidateCircleA; } return this.candidateCircleA.radius > this.candidateCircleB.radius ? this.candidateCircleA : this.candidateCircleB; }; Sakri.LargestCircleInBoundsForRadiusFinder.rectangleContainsCircle = function(rectangle, circle){ //0° if(!rectangle.containsPoint(circle.x+circle.radius, circle.y)){ return false; } //90° if(!rectangle.containsPoint(circle.x, circle.y+circle.radius)){ return false; } //180° if(!rectangle.containsPoint(circle.x-circle.radius, circle.y)){ return false; } //270° if(!rectangle.containsPoint(circle.x, circle.y-circle.radius)){ return false; } return true; }
}(window));
window.requestAnimationFrame = window.__requestAnimationFrame || window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || (function () { return function (callback, element) { var lastTime = element.__lastTime; if (lastTime === undefined) { lastTime = 0; } var currTime = Date.now(); var timeToCall = Math.max(1, 33 - (currTime - lastTime)); window.setTimeout(callback, timeToCall); element.__lastTime = currTime + timeToCall; }; })();
var readyStateCheckInterval = setInterval( function() { if (document.readyState === "complete") { clearInterval(readyStateCheckInterval); init(); }
}, 10);
//========================
//general properties for demo set up
//========================
var canvas;
var context;
var canvasContainer;
var htmlBounds;
var bounds;
var minimumStageWidth = 800;
var minimumStageHeight = 400;
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,canvas.width, canvas.height); clearTimeout(resizeTimeoutId); clearTimeoutsAndIntervals(); resizeTimeoutId = setTimeout(commitResize, 300 );
}
function commitResize(){ bounds = new Sakri.Geom.Rectangle(0,0, getWidth(this.canvasContainer) , getHeight(canvasContainer)); canvas.width = bounds.width; canvas.height = bounds.height; context.clearRect(0,0,canvas.width, canvas.height); textWandererProps = [ {fontSize:15, fontHeight:12, speed: 13}, {fontSize:15, fontHeight:12, speed: 13}, {fontSize:15, fontHeight:12, speed: 13}, {fontSize:25, fontHeight:21, speed: 20}, {fontSize:25, fontHeight:21, speed: 20}, {fontSize:35, fontHeight:26, speed: 27} ]; if(canvas.height > 250 && canvas.width>600){ textWandererProps.push({fontSize:45, fontHeight:32, speed: 34}); textWandererProps.push({fontSize:55, fontHeight:42, speed: 44}); } if(canvas.height > 400 && canvas.width>800){ textWandererProps.push({fontSize:65, fontHeight:52, speed: 54}); } startDemo();
}
//========================
//Demo specific properties
//========================
function clearTimeoutsAndIntervals(){ animating = false;
}
var animating = false;
var wanderers;
var sourceCanvas;
var sourceContext;
var currentWord = "SAKRI" ;
var wanderText = "SAKRI DEVSTATE CODEPEN CANVAS ";
var wandererColors = [ "#070720", "#0b0b2e", "#0f0f38", "#262994"];//"#03030f",
var textWandererProps;
function startDemo(){ context.textBaseline = "top"; showNextWord();
}
function showNextWord(){ renderBG(); renderSourceText(); setCharacterMetrics(); createWanderers(); context.shadowColor = '#000000'; context.shadowBlur = 2; context.shadowOffsetX = 1; context.shadowOffsetY = 1; context.textBaseline = "top"; context.textAlign = "left"; animating = true; loop();
}
function loop(){ if(animating){ updateParticles(); window.requestAnimationFrame(loop, canvas); }
}
function renderSourceText(){ if(!sourceCanvas){ sourceCanvas = document.createElement("canvas"); } sourceCanvas.width = bounds.width; sourceCanvas.height = bounds.height; sourceContext = sourceCanvas.getContext("2d"); var wordBounds = bounds.clone(); var margin = bounds.width * .05; wordBounds.x += margin; wordBounds.width -= margin*2; var fontSize = 50; sourceContext.font = "bold "+fontSize+"px sans-serif"; while(sourceContext.measureText(currentWord).width < wordBounds.width && fontSize < bounds.height){ fontSize++; sourceContext.font = "bold "+fontSize+"px sans-serif"; } wordBounds.y = bounds.getCenterY() - fontSize/2; wordBounds.width = sourceContext.measureText(currentWord).width; var textGradient = context.createLinearGradient(wordBounds.x, wordBounds.y, wordBounds.x, wordBounds.getBottom() ); textGradient.addColorStop(0, "#dc400f"); textGradient.addColorStop(1, "#ff9c2c"); sourceContext.fillStyle = textGradient; sourceContext.textBaseline = "top"; sourceContext.fillText(currentWord, bounds.getCenterX() - wordBounds.width/2 , bounds.getCenterY() - fontSize/2);
}
function createWanderers(){ wanderers=[]; var props, i; var center = bounds.getCenter(); for(i=0; i < 30; i++){ props = textWandererProps[i%textWandererProps.length]; wanderers[i] = new Sakri.CircularWander(bounds, 4, center);//assume no character will be less wide than "speed" wanderers[i].maxCircleRotation = Math.PI; wanderers[i].minimumRadius = props.fontSize*1.5; wanderers[i].fontSize = props.fontSize; wanderers[i].fontHeight = props.fontHeight; wanderers[i].color = wandererColors[i%wandererColors.length]; wanderers[i].charMetrics = props.charMetrics; wanderers[i].distanceTravelled = 0; wanderers[i].currentCharacterIndex = 0; wanderers[i].nextTravelTarget = 0; }
}
function renderBG(){ var bgGradient = context.createRadialGradient(bounds.getCenterX(),bounds.getCenterY(),bounds.height/8,bounds.getCenterX(),bounds.getCenterY(),bounds.height/1.5); bgGradient.addColorStop(0,"#2225a8"); bgGradient.addColorStop(1,"#020315"); context.fillStyle = bgGradient;//"#000000"; context.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
}
function setCharacterMetrics(){ var metrics, i, j, char; for(i = 0; i < textWandererProps.length; i++){ textWandererProps[i].charMetrics = []; context.font = "bold "+textWandererProps[i].fontSize+"px sans-serif"; for(j=0;j<wanderText.length;j++){ metrics = context.measureText(wanderText.charAt(j)); textWandererProps[i].charMetrics[j] = metrics.width; } }
}
function updateParticles(){ for(var i=0; i<wanderers.length; i++){ updateWanderer(wanderers[i]); }
}
function updateWanderer(wanderer){ wanderer.update(); wanderer.distanceTravelled += Math.abs(wanderer.speed); if(wanderer.distanceTravelled <= wanderer.nextTravelTarget){ return; } var pixel = sourceContext.getImageData(wanderer.position.x, wanderer.position.y, 1, 1).data;//could offset this a bit towards center of character? var style = Sakri.MathUtil.rgbToHex(pixel[0], pixel[1], pixel[2]); style = style.toUpperCase()=="#000000" || (pixel[0] + pixel[1] + pixel[2] < 100) ? wanderer.color : style; context.save(); var character = wanderText.charAt(wanderer.currentCharacterIndex); wanderer.nextTravelTarget = wanderer.charMetrics[wanderer.currentCharacterIndex]; wanderer.currentCharacterIndex++; wanderer.currentCharacterIndex %= wanderText.length; wanderer.distanceTravelled = 0; context.fillStyle = style; var radius = wanderer.currentCircle.radius + (wanderer.speed>0 ? wanderer.fontHeight : -wanderer.fontHeight)/2; var fontX = wanderer.currentCircle.x + Math.cos(wanderer.currentRadian)*radius; var fontY = wanderer.currentCircle.y + Math.sin(wanderer.currentRadian)*radius; context.translate(fontX,fontY); context.rotate(wanderer.currentRadian+(wanderer.speed>0 ? -Sakri.MathUtil.PI_AND_HALF : Sakri.MathUtil.PI_AND_HALF)); context.font = "bold "+wanderer.fontSize+"px sans-serif"; context.fillText (character,0,0) context.restore();
}
Circular Wander Text - Script Codes
Circular Wander Text - Script Codes
Home Page Home
Developer Sakri Rosenstrom
Username sakri
Uploaded September 13, 2022
Rating 3
Size 9,998 Kb
Views 20,240
Do you need developer help for Circular Wander 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 sales emails 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!