Skip to content

Instantly share code, notes, and snippets.

@sxywu
Last active November 17, 2024 04:40
Show Gist options
  • Save sxywu/4996c518e789a3d58369881f18513c72 to your computer and use it in GitHub Desktop.
Save sxywu/4996c518e789a3d58369881f18513c72 to your computer and use it in GitHub Desktop.
Morphing Curve: Radial Gradient
license: gpl-3.0
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>HTML5 Canvas</title>
<script type="text/javascript">
window.addEventListener("load", windowLoadHandler, false);
function windowLoadHandler() {
canvasApp();
}
function canvasApp() {
var displayCanvas = document.getElementById("displayCanvas");
var context = displayCanvas.getContext("2d");
var displayWidth = displayCanvas.width;
var displayHeight = displayCanvas.height;
var numCircles;
var maxMaxRad;
var minMaxRad;
var minRadFactor;
var circles;
var iterations;
var timer;
var drawsPerFrame;
var drawCount;
var bgColor,urlColor;
var TWO_PI = 2*Math.PI;
var lineWidth;
init();
function init() {
/*
In other experiments, you may wish to use more fractal curves ("circles")
and allow the radius of them to vary. If so, modify the next three variables.
*/
numCircles = 2;
maxMaxRad = 100;
minMaxRad = 1;
/*
We draw closed curves with varying radius. The factor below should be set between 0 and 1,
representing the size of the smallest radius with respect to the largest possible.
*/
minRadFactor = 0;
/*
The number of subdividing steps to take when creating a single fractal curve.
Can use more, but anything over 10 (thus 1024 points) is overkill for a moderately sized canvas.
*/
iterations = 5;
//number of curves to draw on every tick of the timer
drawsPerFrame = 20;
bgColor = "#FFFFFF";
urlColor = "#EEEEEE";
lineWidth = 1;
startGenerate();
}
function startGenerate() {
drawCount = 0;
context.setTransform(1,0,0,1,0,0);
context.clearRect(0,0,displayWidth,displayHeight);
setCircles();
// onTimer();
if(timer) {clearInterval(timer);}
timer = setInterval(onTimer,1000/50);
}
function setCircles() {
var i;
var r,g,b,a;
var maxR, minR;
var grad;
circles = [];
for (i = 0; i < numCircles; i++) {
maxR = maxMaxRad;
minR = 0;
//define gradient
grad = context.createRadialGradient(0,0,minR,0,0,maxR);
grad.addColorStop(1,"rgba(255,0,0,0.2)");
grad.addColorStop(0,"rgba(255,255,0,0.2)");
var newCircle = {
centerX: -maxR,
centerY: displayHeight/2-50,
maxRad : maxR,
minRad : minR,
color: grad, //can set a gradient or solid color here.
//fillColor: "rgba(0,0,0,1)",
param : 0,
changeSpeed : 1/250,
phase : TWO_PI, //the phase to use for a single fractal curve.
globalPhase: TWO_PI //the curve as a whole will rise and fall by a sinusoid.
};
circles.push(newCircle);
newCircle.pointList1 = setLinePoints(iterations);
newCircle.pointList2 = setLinePoints(iterations);
}
}
function onTimer() {
var i,j;
var c;
var rad;
var point1,point2;
var x0,y0;
var cosParam;
var xSqueeze = 0.75; //cheap 3D effect by shortening in x direction.
var yOffset;
//draw circles
for (j = 0; j < drawsPerFrame; j++) {
drawCount++;
for (i = 0; i < numCircles; i++) {
c = circles[i];
c.param += c.changeSpeed;
if (c.param >= 1) {
c.param = 0;
c.pointList1 = c.pointList2;
c.pointList2 = setLinePoints(iterations);
}
cosParam = 0.5-0.5*Math.cos(Math.PI*c.param);
context.strokeStyle = c.color;
context.lineWidth = lineWidth;
//context.fillStyle = c.fillColor;
context.beginPath();
point1 = c.pointList1.first;
point2 = c.pointList2.first;
//slowly rotate
c.phase += 0.0002;
theta = c.phase;
rad = c.minRad + (point1.y + cosParam*(point2.y-point1.y))*(c.maxRad - c.minRad);
//move center
c.centerX += 0.5;
c.centerY += 0.04;
yOffset = 40*Math.sin(c.globalPhase + drawCount/1000*TWO_PI);
//stop when off screen
if (c.centerX > displayWidth + maxMaxRad) {
clearInterval(timer);
timer = null;
}
//we are drawing in new position by applying a transform. We are doing this so the gradient will move with the drawing.
context.setTransform(xSqueeze,0,0,1,c.centerX,c.centerY+yOffset)
//Drawing the curve involves stepping through a linked list of points defined by a fractal subdivision process.
//It is like drawing a circle, except with varying radius.
x0 = xSqueeze*rad*Math.cos(theta);
y0 = rad*Math.sin(theta);
context.lineTo(x0, y0);
while (point1.next != null) {
point1 = point1.next;
point2 = point2.next;
theta = TWO_PI*(point1.x + cosParam*(point2.x-point1.x)) + c.phase;
rad = c.minRad + (point1.y + cosParam*(point2.y-point1.y))*(c.maxRad - c.minRad);
x0 = xSqueeze*rad*Math.cos(theta);
y0 = rad*Math.sin(theta);
context.lineTo(x0, y0);
}
context.closePath();
context.stroke();
//context.fill();
}
}
}
//Here is the function that defines a noisy (but not wildly varying) data set which we will use to draw the curves.
function setLinePoints(iterations) {
var pointList = {};
pointList.first = {x:0, y:1};
var lastPoint = {x:1, y:1}
var minY = 1;
var maxY = 1;
var point;
var nextPoint;
var dx, newX, newY;
var ratio;
var minRatio = 0.5;
pointList.first.next = lastPoint;
for (var i = 0; i < iterations; i++) {
point = pointList.first;
while (point.next != null) {
nextPoint = point.next;
dx = nextPoint.x - point.x;
newX = 0.5*(point.x + nextPoint.x);
newY = 0.5*(point.y + nextPoint.y);
newY += dx*(Math.random()*2 - 1);
var newPoint = {x:newX, y:newY};
//min, max
if (newY < minY) {
minY = newY;
}
else if (newY > maxY) {
maxY = newY;
}
//put between points
newPoint.next = nextPoint;
point.next = newPoint;
point = nextPoint;
}
}
//normalize to values between 0 and 1
if (maxY != minY) {
var normalizeRate = 1/(maxY - minY);
point = pointList.first;
while (point != null) {
point.y = normalizeRate*(point.y - minY);
point = point.next;
}
}
//unlikely that max = min, but could happen if using zero iterations. In this case, set all points equal to 1.
else {
point = pointList.first;
while (point != null) {
point.y = 1;
point = point.next;
}
}
return pointList;
}
}
</script>
<title>HTML5 Canvas Generative Art</title>
<style type="text/css">
body {background-color:#ffffff; color:#333333;}
h4 {font-family: sans-serif; color:#333333; font-size:16px;}
h3 {font-family: sans-serif; color:#333333;}
p {font-family: sans-serif; color:#333333; font-size:14px;}
#caption {position:absolute; width:1024px; text-align:center; top:520px; z-index:1}
a {font-family: sans-serif; color:#d15423; text-decoration:none;}
canvas {}
#displayCanvas {position:absolute; top:10px; z-index:0;}
div {}
#container {width:1024px; height:576px; margin:auto;}
</style>
</head>
<body>
<div id="container">
<canvas id="displayCanvas" width="1024px" height="576px">
Your browser does not support HTML5 canvas.
</canvas>
<form>
<p id="caption">
HTML5 Canvas - Morphing Fractal Curve.
<br><a href="http://www.rectangleworld.com">rectangleworld.com</a>
</p>
</form>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment