<!-- Template Staff -->
<a class="info" href="https://www.sliderrevolution.com/">by Balazs Viertl</a>
<title>Calculating Linear Gradient Degree on Canvas</title>
<smallinfo>Comparing it to CSS Linear Gradient</smallinfo>
<div class="degree"></div>
<div id="up">+1 Degree</div>
<div id="down">-1 Degree</div>
<div id="anim">Animate Toogle</div>
<div id="mchanger">Calculation 1</div>
<div></div>
<div id="cssGradient"></div>
<canvas id="myCanvas" width="600" height="400"></canvas>
/* 
Template Stuff
*/
html {
  background: #323436;
  color: #f1f1f1;
  font-family: Sans-Serif;
  text-align: center;
  padding: 25px;
}
title {
  font-size: 24px;
  line-height: 35px;
  color: #ecf0f1;
  position: relative;
  display: block;
}
.info {
  position: absolute;
  bottom: 0px;
  right: 0px;
  padding: 5px;
  color: #f1c40f !important;
  text-decoration: none;
}
.info:hover {
  filter: brightness(120%);
}

smallinfo {
  color: #f1c40f !important;
  text-decoration: none;
  display: block;
  color: #fff;
  font-size: 15px;
}

#cssGradient {
  width: 300px;
  height: 170px;
  display: inline-block;
  box-shadow: 0px 0px 30px 30px rgba(0, 0, 0, 0.2);
  vertical-align: top;
  margin: 10px;
  position: relative;
  border-radius: 8px;
  overflow: hidden;
}

#myCanvas {
  display: inline-block;
  width: 300px;
  height: 170px;
  box-shadow: 0px 0px 30px 30px rgba(0, 0, 0, 0.2);
  margin: 10px;
  border-radius: 8px;
  overflow: hidden;
}

#up,
#down,
#anim,
#mchanger{
  border-radius: 5px;
  padding: 5px 10px;
  background: #565758;
  display: inline-block;
  margin-right: 5px;
  margin-top: 20px;
  margin-bottom: 20px;
  cursor: pointer;
  -moz-user-select: none;
  -khtml-user-select: none;
  -webkit-user-select: none;
  -o-user-select: none;
}

#up:hover,
#down:hover,
#anim:hover,
#mchanger:hover{
  filter: brightness(110%);
}
#label {
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: capitalize;
  line-height: 24px;
  padding: 0 15px 0 15px;
  background: #00ceab;
  border-radius: 14px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translateX(-50%) translateY(-50%);
  z-index: 1;
}
.boxinfo {
  color: rgba(255, 255, 255, 0.5);
  font-size: 120px;
  top: 50%;
  left: 50%;
  position: absolute;
  transform: translateX(-50%) translateY(-50%);
  z-index: 0;
}
//Define Color Stops
var colorstops = [
    { color: "rgba(130,0,159,1)", pos: 0 },
    { color: "rgba(93,52,175,1)", pos: 50 },
    { color: "rgba(0,206,171,1)", pos: 100 }
  ],
  gradientAngle = 90,
  mode = 1;

// Gather Elements
var animate = false,
  canvas = document.getElementById("myCanvas"),
  cssbox = document.getElementById("cssGradient"),
  ctx = canvas.getContext("2d");

// Animate Gradients
function animateGradients() {
  drawGradients(gradientAngle);
  gradientAngle++;
  gradientAngle = gradientAngle > 360 ? 0 : gradientAngle;
  if (animate)
    requestAnimationFrame(function () {
      animateGradients();
    });
}

//Draw Gradients
function drawGradients(deg) {
  var points = mode===1 ? linearGradient_a(canvas.width, canvas.height, deg) :  linearGradient_b(canvas.width, canvas.height, deg);
  var grd = ctx.createLinearGradient(
    points.tx,
    points.ty,
    points.bx,
    points.by
  );

  // Draw Box
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.beginPath();
  var comma = false,
    csscol = "linear-gradient(" + deg + "deg, ";
  for (var i in colorstops) {
    grd.addColorStop(colorstops[i].pos / 100, colorstops[i].color);
    csscol +=
      (comma ? "," : "") + colorstops[i].color + " " + colorstops[i].pos + "%";
    comma = true;
  }
  csscol += ")";
  cssbox.style.background = csscol;
  cssbox.innerHTML =
    '<div id="label">' + deg + 'deg</div><div class="boxinfo">CSS</div>';
  ctx.fillStyle = grd;
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.stroke();
  ctx.fillStyle = "rgba(255,255,255,0.5)";
  ctx.textAlign = "center";
  ctx.font = "120px Arial";
  ctx.fillText("CANVAS", 300, 240);
}

drawGradients(gradientAngle);

//Calculate Linear Gradient Angle and Cut Points
function linearGradient_a(w, h, deg) {
 var caseAngle1 = Math.round((Math.atan(w / h) * 180) / Math.PI), //rundung
			    caseAngle2 = Math.round(180 - caseAngle1),
			    caseAngle3 = Math.round(180 + caseAngle1),
			    caseAngle4 = Math.round(360 - caseAngle1);
			  
			var bx = tx = wh = w/2,
				hh = h/2,
				ty = h,    
				by = 0,
				angInRad = (deg * Math.PI) / 180,    
				count1;

			if (deg == caseAngle1) {   tx = 0;bx = w;} else 
			if (deg == caseAngle2) {   tx = 0;ty = 0;bx = w;by = h;} else
			if (deg == caseAngle3) {   tx = w;ty = 0;bx = 0;by = h;} else
			if (deg == caseAngle4) {   tx = w;ty = h;bx = 0;by = 0;} else
			{
				var mtan = Math.tan(angInRad);

				if (0 < deg && deg < caseAngle1) {    
					count1 = (mtan * h) / 2;
					tx = wh - count1;    
					bx = wh + count1;    
				} 
				else if (caseAngle1 < deg && deg < caseAngle2) {    
					count1 = wh / mtan;
					tx = 0;
					ty = hh + count1;
					bx = w;
					by = hh - count1;  
				} else if (caseAngle2 < deg && deg < caseAngle3) {    
					count1 = (mtan * h) / 2;
					tx = wh + count1;
					ty = 0;
					bx = wh - count1;
					by = h;    
				} else if (caseAngle3 < deg && deg < caseAngle4) {    
					count1 = wh / mtan;
					tx = w;
					ty = hh - count1;
					bx = 0;
					by = hh + count1;    
				} else if (caseAngle4 < deg && deg < 361) {
					count1 = (mtan * h) / 2;
					tx = wh - count1;
					ty = h;
					bx = wh + count1;
					by = 0;    
				}
			}
  return { tx: tx, ty: ty, bx: bx, by: by };
}



function linearGradient_b(w, h, angle) {
	angle = angle * Math.PI / 180 + Math.PI / 2;

	var tx,
		ty,
		bx,
		by,
		len = Math.sqrt(w / 2 * w / 2 + h / 2 * h / 2),
		l1 = {
			x1: Math.cos(angle) * len + w / 2,
			y1: Math.sin(angle) * len + h / 2,
			x2: w / 2,
			y2: h / 2
		};

	var lines = [
		getPerpLine({x: 0, y: 0}, angle),
		getPerpLine({x: w, y: 0}, angle),
		getPerpLine({x: w, y: h}, angle),
		getPerpLine({x: 0, y: h}, angle),
	];
	var intersects = []
	for (var i = 0; i < lines.length; i++) {

		var line = lines[i];
		var p = lineIntersect(line, l1);
		intersects.push(p);
	}

	if(dist(w/2, h/2, intersects[0].x, intersects[0].y) > dist(w/2, h/2, intersects[1].x, intersects[1].y)){
		tx = intersects[0].x; 
		ty = intersects[0].y;
	} else {
		tx = intersects[1].x; 
		ty = intersects[1].y;
	}

	if(dist(w/2, h/2, intersects[2].x, intersects[2].y) > dist(w/2, h/2, intersects[3].x, intersects[3].y)){
		bx = intersects[2].x; 
		by = intersects[2].y;
	} else {
		bx = intersects[3].x; 
		by = intersects[3].y;
	}
	
	var eAngle = Math.round( Math.atan2((h/2 - ty), (w/2 - tx)) * 100) / 100;
	var aAngle = Math.round(angle % (Math.PI * 2) * 100) / 100; 

	if(eAngle === aAngle){
		var x = tx, y = ty;
		tx = bx, ty = by;
		bx = x, by = y;
	}

	l1 = {
		x1: tx, y1: ty, x2: w/2, y2: h/2
	};
	l2 = {
		x1: w/2, y1: h/2, x2: bx, y2: by
	};

	return {
    tx:Math.round(tx), 
    ty:Math.round(ty), 
    bx:Math.round(bx), 
    by:Math.round(by)}		
}


function degToRad(d) {
	return d * Math.PI / 180;
}

function dist(x1, y1, x2, y2){
	var dx = x1 - x2;
	var dy = y1 - y2;
	return Math.sqrt( dx * dx + dy * dy );
}


function getPerpLine(p, ang){
	return {
		x1: p.x,
		y1: p.y,
		x2: p.x + Math.cos(ang + Math.PI/2) * 100,
		y2: p.y + Math.sin(ang + Math.PI/2) * 100,
	}
}

function lineIntersect(l1, l2) {
	var p0 = {
		x: l1.x1,
		y: l1.y1
	}
	var p1 = {
		x: l1.x2,
		y: l1.y2
	}
	var p2 = {
		x: l2.x1,
		y: l2.y1
	}
	var p3 = {
		x: l2.x2,
		y: l2.y2
	}

	var A1 = p1.y - p0.y,
		B1 = p0.x - p1.x,
		C1 = A1 * p0.x + B1 * p0.y,
		A2 = p3.y - p2.y,
		B2 = p2.x - p3.x,
		C2 = A2 * p2.x + B2 * p2.y,
		denominator = A1 * B2 - A2 * B1;

		if(denominator === 0){
			return false;
		}

	return {
		x: Math.round((B2 * C1 - B1 * C2) / denominator * 100) / 100,
		y: Math.round((A1 * C2 - A2 * C1) / denominator * 100) / 100
	}

}



// Navigation
var up = document.getElementById("up"),
  down = document.getElementById("down"),
  anim = document.getElementById("anim"),
  mchange = document.getElementById("mchanger");

anim.onclick = function () {
  animate = animate === true ? false : true;
  animateGradients();
};

up.onclick = function () {
  gradientAngle++;
  gradientAngle = gradientAngle > 360 ? 0 : gradientAngle;
  drawGradients(gradientAngle);
};

down.onclick = function () {
  gradientAngle--;
  gradientAngle = gradientAngle < 0 ? 360 : gradientAngle;
  drawGradients(gradientAngle);
};

mchange.onclick = function () {
  mode = mode ===1 ? 2 : 1;
  this.innerHTML = "Calculation "+mode;
  drawGradients(gradientAngle);
};

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/gsap/3.0.5/gsap.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js