I am making a program that graphs a line based on data inputted by the user. (It's based on the slope form/equation). I am using the Canvas to graph my equation. I've been having a problem graphing the equation in a way that lets it adapt to the scaling (which is based on how large the numbers inputted are.)
How can I make the graphed equation (line) fit the graph as the canvas scales?
Here's my code:
var c=document.getElementById("graph_");
var ctx=c.getContext("2d");
graph_.style.backgroundColor="white";
// This is used to define the parameters of the canvas. Variables a and b are the x and y intercepts of the linear function.
var z0=Math.max(Math.abs(a),Math.abs(b));
var z=Math.round(z0);
var z1=Math.round(z);
var z2=z*2
// alert(z1);
// alert(z2);`
//The code below is used to properly scale the canvas and lines so they can accomodate larger numbers
var scale = 2*z/360;
var offsetX = 150;
var offsetY = 75
ctx.translate((-c.width /2 * scale) + offsetX,(-c.height / 2 * scale) + offsetY);
ctx.scale(scale,scale);
var lw = scale/2
var xnew = 360/2+360/2*a
var ynew = 360/2-360/2*b
alert(xnew);
alert(ynew);
//The two lines drawn below are the axises of the graph
ctx.lineWidth = 2/lw;
ctx.beginPath()
ctx.moveTo(150, 40000*-1);
ctx.lineTo(150, 40000);
ctx.closePath();
ctx.lineWidth = 1/lw;
ctx.moveTo(400000*-1, 75);
ctx.lineTo(40000, 75);
ctx.strokeStyle = "#8B8682";
ctx.stroke();
ctx.closePath();
//var xmax = 400000 - b
//var xmax1 = xmax/s
//var ymax = 400000*s
//var ymax1 = ymax + b
// The code below graphs the equation.
ctx.beginPath();
ctx.lineWidth = 1/lw;
ctx.moveTo(xnew, 180);
ctx.lineTo(180, ynew);
// ctx.lineTo(xmax, ymax)
// ctx.lineTo(xmax*-1, ymax*-1)
ctx.strokeStyle = "red";
ctx.stroke();
Here is the coding for the whole page:
As you can see the line, if drawn at all, doesn't become long enough, like it should. (linear lines are always infinite, so the line should be going across the WHOLE graph, not a small portion.)
var canwith=360
var canheight=360
// alert(window.innerWidth)
function doSolve() {
var s=''
var x1 = document.getElementById('x1').value
var y1 = document.getElementById('y1').value
var x2 = document.getElementById('x2').value
var y2 = document.getElementById('y2').value
var m
var b
var a
try {
if ((x2 - x1)==0) {
m='Undefined'
b='Undefined'
a=x1
} else {
m = (y2 - y1)/(x2 - x1)
b = (y2-x2*m)
a = (-b/m)
}
s += 'Coordinates are: ('
s += x1
s += ','
s += y1
s += '),('
s += x2
s += ','
s += y2
s += ')'
s += '<br>Slope:'
s += m
s +='<br>y intercept:'
s += b
s += '<br>x intercept:'
s += a
if (m=='undefined') {
s += '<br>Equation: x = ' + x1
} else {
s += '<br>Equation: y = '
if (m!=0) {
if (m!=1) {
s += m + 'x'
} else {
s += 'x'
}
}
if (b!=0) {
if (b>0) {
s += ' + ' + b
} else {
s += ' - ' + b*-1
}
}
}
document.getElementById('outputx').innerHTML=s
} catch (e) {alert(e.message)}
try {
var c=document.getElementById("graph_");
var ctx=c.getContext("2d");
graph_.style.backgroundColor="white";
var z0=Math.max(Math.abs(a),Math.abs(b));
var z=Math.round(z0);
var z1=Math.round(z);
var z2=z*2
// alert(z1);
// alert(z2);
var scale = 2*z/360;
var offsetX = 150;
var offsetY = 75
ctx.translate((-c.width /2 * scale) + offsetX,(-c.height / 2 * scale) + offsetY);
ctx.scale(scale,scale);
var lw = scale/2
var xnew = 360/2+360/2*a
var ynew = 360/2-360/2*b
alert(xnew);
alert(ynew);
ctx.lineWidth = 2/lw;
ctx.beginPath()
ctx.moveTo(150, 40000*-1);
ctx.lineTo(150, 40000);
ctx.closePath();
ctx.lineWidth = 1/lw;
ctx.moveTo(400000*-1, 75);
ctx.lineTo(40000, 75);
ctx.strokeStyle = "#8B8682";
ctx.stroke();
ctx.closePath();
var xmax = 400000 - b
var xmax1 = xmax/s
var ymax = 400000*s
var ymax1 = ymax + b
ctx.beginPath();
ctx.lineWidth = 1/lw;
ctx.moveTo(xnew, 180);
ctx.lineTo(180, ynew);
ctx.lineTo(xmax, ymax)
ctx.lineTo(xmax*-1, ymax*-1)
ctx.strokeStyle = "red";
ctx.stroke();
} catch (e) {alert(e.message)}
}
I couln't cope with your code, so I made my own implementation adjusting to your visual requirements, hope this fix the problem:
try {
var c = document.getElementById("graph_");
var ctx = c.getContext("2d");
graph_.style.backgroundColor="white";
var w = c.width;
var h = c.height;
var xAxisSize = 40;
var yAxisSize = 40;
var scaleFactorX = w / xAxisSize;
var scaleFactorY = -(h / yAxisSize);
var offsetX = -10;
var offsetY = -10;
ctx.scale(scaleFactorX, scaleFactorY);
ctx.translate((xAxisSize / 2) + offsetX, -((yAxisSize / 2) + offsetY));
ctx.lineWidth = 3 / scaleFactorX;
ctx.beginPath();
ctx.moveTo(-xAxisSize, 0);
ctx.lineTo( xAxisSize, 0);
ctx.strokeStyle = "#8B8682";
ctx.stroke();
ctx.closePath();
ctx.lineWidth = 3 / scaleFactorY;
ctx.beginPath();
ctx.moveTo(0, -yAxisSize);
ctx.lineTo(0, yAxisSize);
ctx.strokeStyle = "#8B8682";
ctx.stroke();
ctx.closePath();
ctx.lineWidth = 3 / scaleFactorY;
ctx.beginPath();
var xx1 = -xAxisSize - offsetX;
var yy1 = m * xx1 + b;
var xx2 = xAxisSize + offsetX;
var yy2 = m * xx2 + b;
ctx.moveTo(xx1, yy1);
ctx.lineTo(xx2,yy2);
ctx.strokeStyle = "red";
ctx.stroke();
ctx.closePath();
} catch (e) {
alert(e.message)
}
Related
I am trying to make a program that you can can input numbers into, and it creates a graph based off of those (like Desmos, but the numbers you input don't need to be formatted like (1, 0) and you can control the spacing of the numbers). I am coding this in Javascript, and HTML for the actual inputs and canvas.
I have tried using this website as a foundation to help me create the curve.
Here is the combined Javascript and HTML code:
<!DOCTYPE html>
<html>
<body>
<form>
<label></label>
<input id="numbers" value="Numbers">
<button onClick="refresh()">Submit</button>
</form>
<form>
<label></label>
<input id="spacing" value="Spacing">
<button onClick="refresh()">Submit</button>
</form>
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;"></canvas>
<script>
refresh()
numbers = document.getElementById("numbers").value;
function refresh(){
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.moveTo(20, 20);
ctx.bezierCurveTo(numbers);
ctx.stroke();
}
</script>
</html>
Please note that I know that the canvas does not work (the variable numbers isn's working, any help?), but my main concern is that this code doesn't create a graph like I want it to. I want to type in numbers, and have it plot invisible points x numbers above the "ground" (the bottom of the canvas) and then use bezier to make a curve go through those invisible points (don't make the points, that was the only way I could think of explaining it).
For my internship, I was asked to develop a lightweight web application for some of our internal operations. One of the functions I wanted to include was a graph/chart that plotted Sales data for a selected employee. I also wanted to be able to change the resolution of the chart's x-axis such that I could view Sales by day, week, month, quarter, or year.
Here's what I came up with using just basic JavaScript and HTML Canvas.
Regarding the fix_dpi() function (that may be confusing to new coders, like myself), here was an article explaining what it is and why you will want to adjust for it: https://medium.com/wdstack/fixing-html5-2d-canvas-blur-8ebe27db07da .
Also, note that my drawYTicks() function has a special case for the "50K" and "500K" labels. If you need something a little more general, just copy the code of from drawXTicks() and make the minor changes necessary for it's domain.
Code:
<script>
let canvas = document.getElementById('canvas'), ctx = canvas.getContext('2d'), dpi = window.devicePixelRatio;
let xOrig = canvas.width * 0.15;
let yOrig = canvas.height * 0.85;
let xLen = canvas.width * 0.7;
let yLen = canvas.height * 0.7;
function fix_dpi() {
let style = {
height() {
return +getComputedStyle(canvas).getPropertyValue('height').slice(0, -2);
},
width() {
return +getComputedStyle(canvas).getPropertyValue('width').slice(0, -2);
}
}
canvas.setAttribute('width', style.width() * dpi);
canvas.setAttribute('height', style.height() * dpi);
xOrig = canvas.width * 0.15;
yOrig = canvas.height * 0.85;
xLen = canvas.width * 0.7;
yLen = canvas.height * 0.7;
}
function draw() {
fix_dpi(); //fixes dpi for sharper image...
var dataSet = // int[][] containing the data.
var xLabs = // a string[] containing x-labels.
var yLabs = // a string[] containing y-labels.
var range = // an integer expressing the top of your range (x-axis);
var domain = // an integer expressing the top of your domain (y-axis)
console.log(xLabs);
console.log(xLabs.length);
console.log(yLabs);
console.log(yLabs.length);
drawChartTitle("Employee (Total) Sales History");
fillDataSpace(dataSet, range, domain, '#3F3');
drawDataLine(dataSet, range, domain, '#000');
drawDataPoints(dataSet, range, domain, '#F00');
drawXTicks(xLabs);
drawYTicks(yLabs);
drawAxes();
}
function drawChartTitle(title) {
ctx.save();
ctx.fillStyle = '#000';
ctx.font = '24px Times New Roman';
ctx.textAlign = 'center';
ctx.fillText(title, canvas.width / 2, canvas.height * 0.1);
ctx.restore();
}
function drawAxes(color='#000') {
ctx.save();
ctx.beginPath();
ctx.strokeStyle = color;
ctx.moveTo(xOrig, (yOrig - yLen));
ctx.lineTo(xOrig, yOrig);
ctx.lineTo((xOrig + xLen),yOrig);
ctx.stroke();
ctx.restore();
}
function drawYTicks(yLabs) {
//Note: two of the ticks have to be handled separately
//for this program, 50K and 500K.
ctx.save();
let tickLen = canvas.height / 50;
let yDivs = yLen / (yLabs.length - 1);
let tickX = xOrig - tickLen / 2;
let labX = xOrig - tickLen;
ctx.beginPath();
ctx.strokeStyle = '#000';
ctx.fillStyle = '#000';
ctx.font = '12px Times New Roman';
ctx.textAlign = 'right';
if (yLabs.length >= 4) {
yDivs = yLen / (yLabs.length - 3);
//Draw million dollar ticks and labels
for (let i = 3; i < yLabs.length; i++) {
let tickY = yOrig - yDivs * (i-2);
ctx.moveTo(tickX, tickY);
ctx.lineTo((tickX + tickLen), tickY);
}
for (let i = 3; i < yLabs.length; i++) {
let labY = yOrig - yDivs * (i-2) + 4;
ctx.fillText(yLabs[i], labX, labY);
}
//Draw 50K and 500K
let fifty = yOrig - yDivs * 0.1;
ctx.moveTo(tickX, fifty);
ctx.lineTo((tickX + tickLen), fifty);
ctx.fillText(yLabs[1], labX, fifty + 4);
let fHundredK = yOrig - yDivs * 0.5;
ctx.moveTo(tickX, fHundredK);
ctx.lineTo((tickX + tickLen), fHundredK);
ctx.fillText(yLabs[2], labX, fHundredK + 4);
}
else if (yLabs.length == 3) {
yDivs = yLen / (yLabs.length - 2);
//Draw 50K and 500K only
let fifty = yOrig - yDivs * 0.1;
ctx.moveTo(tickX, fifty);
ctx.lineTo((tickX + tickLen), fifty);
ctx.fillText(yLabs[1], labX, fifty + 4);
let fHundredK = yOrig - yDivs;
ctx.moveTo(tickX, fHundredK);
ctx.lineTo((tickX + tickLen), fHundredK);
ctx.fillText(yLabs[2], labX, fHundredK + 4);
}
else {
//Draw 50K only
let fifty = yOrig - yDivs;
ctx.moveTo(tickX, fifty);
ctx.lineTo((tickX + tickLen), fifty);
ctx.fillText(yLabs[1], labX, fifty + 4);
}
let zero = yOrig;
ctx.moveTo(tickX, zero);
ctx.lineTo((tickX + tickLen), zero);
ctx.fillText(yLabs[0], labX, zero + 4);
ctx.stroke();
ctx.restore();
}
function drawXTicks(xLabs) {
ctx.save();
let tickLen = canvas.height / 50;
let xDivs = xLen / (xLabs.length - 1);
ctx.beginPath();
ctx.strokeStyle = '#000';
for (let i = 0; i < xLabs.length; i++) {
let tickX = xOrig + xDivs * i;
let tickY = yOrig + tickLen / 2;
ctx.moveTo(tickX, tickY);
ctx.lineTo(tickX, (tickY - tickLen));
}
ctx.stroke();
ctx.restore();
for (let i = 0; i < xLabs.length; i++) {
ctx.save();
ctx.fillStyle = '#000';
ctx.font = '12px Times New Roman';
ctx.textAlign = 'right';
ctx.translate((canvas.width*0.15) + (xDivs * i), canvas.height*0.15);
ctx.rotate(-Math.PI / 4);
let labY = canvas.height * 0.52;
let labX = -canvas.width * 0.38;
ctx.fillText(xLabs[i], labX, labY);
ctx.restore();
}
}
function drawDataPoints(coords, maxX, maxY, color='#000') {
ctx.save();
if (coords.length >= 2) {
let xScale = xLen / maxX;
let yScale = yLen / maxY;
let pointCir = canvas.height / 200;
ctx.beginPath();
for (let i = 0; i < coords.length; i++) {
let xp = xOrig + coords[i][0] * xScale;
let yp = yOrig - coords[i][1] * yScale;
ctx.moveTo(xp, yp);
ctx.arc(xp, yp, pointCir, 0, Math.PI * 2);
}
ctx.fillStyle = color;
ctx.fill();
ctx.strokeStyle = color;
ctx.stroke();
}
else {
ctx.fillStyle = '#000';
ctx.font = '24px Sans-Serif';
ctx.fillText('There is no data to display', (xOrig + xLen * 0.3), (yOrig - yLen * 0.5));
}
ctx.restore();
}
function drawDataLine(coords, maxX, maxY, color='#000') {
ctx.save();
if (coords.length >= 2) {
let xScale = xLen / maxX;
let yScale = yLen / maxY;
let xp = xOrig + coords[0][0] * xScale;
let yp = yOrig - coords[0][1] * yScale;
ctx.beginPath();
ctx.moveTo(xp, yp);
for (let i = 1; i < coords.length; i++) {
xp = xOrig + coords[i][0] * xScale;
yp = yOrig - coords[i][1] * yScale;
ctx.lineTo(xp, yp);
}
ctx.strokeStyle = color;
ctx.stroke();
}
else {
ctx.fillStyle = '#000';
ctx.font = '24px Sans-Serif';
ctx.fillText('There is no data to display', (xOrig + xLen * 0.3), (yOrig - yLen * 0.5));
}
ctx.restore();
}
function fillDataSpace(coords, maxX, maxY, color = '#00F') {
ctx.save();
if (coords.length >= 2) {
let xScale = xLen / maxX;
let yScale = yLen / maxY;
let xp = xOrig + coords[0][0] * xScale;
let yp = yOrig - coords[0][1] * yScale;
var lingrad = ctx.createLinearGradient(xOrig, yOrig - yLen, xOrig + xLen, yOrig);
lingrad.addColorStop(0, '#FFF');
lingrad.addColorStop(0.5, color);
lingrad.addColorStop(1, '#FFF');
ctx.beginPath();
ctx.moveTo(xp, yp);
for (let i = 1; i < coords.length; i++) {
xp = xOrig + coords[i][0] * xScale;
yp = yOrig - coords[i][1] * yScale;
ctx.lineTo(xp, yp);
}
ctx.lineTo(xOrig + xLen, yOrig);
ctx.lineTo(xOrig, yOrig);
ctx.closePath();
ctx.strokeStyle = lingrad;
ctx.stroke();
ctx.fillStyle = lingrad;
ctx.fill();
}
else {
ctx.fillStyle = '#000';
ctx.font = '24px Sans-Serif';
ctx.fillText('There is no data to display', (xOrig + xLen * 0.3), (yOrig - yLen * 0.5));
}
ctx.restore();
}
</script>
Here's what it looks like with some rendered data: Employee Sales Chart
Final note: I handled the collection of the data points and data labels using a static class object within my program, and passed this object to the HTML (Razor) page for my project. But, this solution should work if you have a way to get the data to your JS functions. If nothing else, you can start playing with this by defining the dataSet, xLabs, yLabs, range, and domain variables first and then figuring out how to pass larger data sets to the function later.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
ctx.lineWidth = 2;
ctx.font = '14px verdana';
var PI2 = Math.PI * 2;
var myColor = ["Green", "Red", "Blue"];
var myData = [30, 15, 38, 22, 30, 20, 10];
var cx = 150;
var cy = 150;
var radius = 100;
ctx.globalAlpha = 0.50;
pieChart(myData, myColor);
ctx.globalAlpha = 1.00;
function pieChart(data, colors) {
// calc data total
var total = 0;
for (var i = 0; i < data.length; i++) {
total += data[i];
}
// calc sweep angles for each piece of pie
var sweeps = []
for (var i = 0; i < data.length; i++) {
sweeps.push(data[i] / total * PI2);
}
// draw outer pie
var accumAngle = 0;
for (var i = 0; i < sweeps.length; i++) {
var f = randomColor();
drawWedge(radius, accumAngle, accumAngle + sweeps[i], f, data[i]);
accumAngle += sweeps[i];
}
}
function drawWedge(radius, startAngle, endAngle, fill, label) {
// draw the wedge
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.arc(cx, cy, radius, startAngle, endAngle, false);
ctx.closePath();
ctx.fillStyle = fill;
ctx.strokeStyle = 'white';
ctx.fill();
ctx.stroke();
}
function randomColor() {
return ('#' + (Math.floor(Math.random() * 0x1000000) +
0x1000000).toString(16).substr(1));
}
<canvas id="canvas" width=512 height=512></canvas>
I have a simply question - is there any option to add a tooltip on mouseover event on canvas pie arc? Can anyone help me how could i do that.
I am able to draw a pie chart using canvas please fine the below snippet:
Canvas.title = "Tool tip text"
At the most basic level you can use the canvas.title property to set the tooltip. Just get the mouse move events and record the mouse position. Then when you detect that the mouse has changed position, check which slice the mouse is over, if any and set the tooltip to match.
Note that the browsers tooltip is a little slow to respond to changes.
For a better response.
You could also draw the chart every frame of the render loop and highlight the parts of the pie the mouse is over and create a more responsive tooltip than the browser default which can be a little slow to respond at times.
The example below is just your code with the modification needed to get the basic tooltip.
Note that I keep the mouse event listener and the function that changes the tooltip separate, this is because on some systems the mouse can have a very high sample rate (my current gaming mouse can fire 700+ times a second) and doing too much in the mousemove event can cause laggy response to user input.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
ctx.lineWidth = 2;
ctx.font = '14px verdana';
var PI2 = Math.PI * 2;
var myColor = ["Green", "Red", "Blue"];
var myData = [30, 15, 38, 22, 30, 20, 10];
var cx = 150;
var cy = 150;
var radius = 100;
var mouse = {x :0,y:0,oldx : 0,oldy:0};
ctx.globalAlpha = 0.50;
pieChart(myData, myColor);
ctx.globalAlpha = 1.00;
function pieChart(data, colors) {
// calc data total
var total = 0;
for (var i = 0; i < data.length; i++) {
total += data[i];
}
// calc sweep angles for each piece of pie
var sweeps = []
for (var i = 0; i < data.length; i++) {
sweeps.push(data[i] / total * PI2);
data[i] = {
value : data[i],
angle : data[i] / total * PI2,
text : "Data is "+((data[i] / total) * 100).toFixed(0) + "%",
}
}
// draw outer pie
var accumAngle = 0;
for (var i = 0; i < sweeps.length; i++) {
var f = randomColor();
drawWedge(radius, accumAngle, accumAngle + sweeps[i], f, data[i].value);
accumAngle += sweeps[i];
}
}
function drawWedge(radius, startAngle, endAngle, fill, label) {
// draw the wedge
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.arc(cx, cy, radius, startAngle, endAngle, false);
ctx.closePath();
ctx.fillStyle = fill;
ctx.strokeStyle = 'white';
ctx.fill();
ctx.stroke();
}
function randomColor() {
return ('#' + (Math.floor(Math.random() * 0x1000000) +
0x1000000).toString(16).substr(1));
}
canvas.addEventListener("mousemove",function(event){
mouse.x = event.clientX;
mouse.y = event.clientY;
})
function update(){
// only on change in mouse position
if(mouse.x !== mouse.oldx || mouse.y !== mouse.oldy){
var x = mouse.oldx = mouse.x;
var y = mouse.oldy = mouse.y;
x -= cx; // vector from pie center
y -= cy;
var newText = "My pie chart. Mouse over slices for more info.";
var dist = Math.sqrt(x * x + y * y); // get distance from center
if(dist < radius){
var ang = Math.atan2(y,x); // get angle note y is first
ang += Math.PI * 2; // rotate 360 as atan2 starts at -Pi
ang %= Math.PI * 2; // normalize to range 0 to 2Pi
var i = 0;
var tAng = 0
while(i < myData.length-1){
if(ang < tAng + myData[i].angle){
break;
}
tAng += myData[i].angle;
i += 1;
}
newText = myData[i].text;
}
canvas.title = newText;
}
requestAnimationFrame(update);
}
update();
<canvas id="canvas" width=512 height=512></canvas>
I'm drawing on an HTML canvas using Javascript. I'm having an issue where one of my shapes isn't getting filled with the correct colour. It's a complicated situation but I'll do my best to simplify it.
The shape that is causing me grief is representing a plane on a radar screen. I have a diamond (which I draw using lineTo commands), a rectangle which will hold some information about that plane, and a line connecting the two. The plane has a property selected, which is set to false by default, but if you click on the diamond or rectangle, it selects that plane and sets its selected property to true.
When the plane is not selected, I want the shape to be drawn with a red outline, and filled with a black background. This is currently working. However, when the plane is selected, I want the diamond to be filled in with red. I have coded this scenario but when I click on a plane to select it, the fill colour of the diamond stays black, and the border gets bigger.
I can't figure out why this is happening. I'm guessing it has something to do with the previous settings of the canvas carrying over from the last time I drew a shape, but I set these values each time, so I can't work it out.
I have created a JSFiddle here: JSFiddle and here is the same code snippet
//---------------------------- MARK: Declarations ----------------------------\\
var canvas = document.getElementById("gameCanvas");
//canvas.width = window.innerWidth;
//canvas.height = window.innerHeight;
canvas.width = 1280;
canvas.height = 627;
var ctx = canvas.getContext("2d");
var timer = 0;
var seconds = 0;
var textToDisplay = "";
var strLevel = "";
var intPlaneInfoBoxSelected = -1;
var intPlaneInfoBoxSelectedXOffset = 0;
var intPlaneInfoBoxSelectedYOffset = 0;
var border = new Border();
var intLessWidth = 0;
var intLessHeight = 0;
var aRunways = [];
var aWayPoints = [];
var aPlanes = [];
var aAircraftTypes = [];
var aAirlineName = [];
var colourBrightGreen = "#1EFF00";
var colourWaypointBorder = "#FFFFFF";
var colourWaypointBackground = "#188C08";
var intWaypointLineWidth = 5;
var intRunwayWaypointLineWidth = 4;
var intInfoBoxLineWidth = 3;
var intEntrySpeed = 0.1;
//---------------------------- MARK: Object Creation ----------------------------\\
function WayPoint() {
this.topLeft = new Point();
this.widthHeight = 15;
this.name = "";
this.inbound = false;
this.outbound = false;
this.border = "";
this.highlighted = false;
}
function Runway() {
this.topLeft = new Point();
this.height = 0;
this.width = 0;
this.highlighted = false;
this.name = "";
this.wayPointTopLeft = new Point();
this.wayPointWidthHeight = 0;
}
function Plane() {
this.flightNumber = "";
this.status = "";
this.selected = false;
this.airRoute = [];
this.heading = 0;
this.speed = 0;
this.topPoint = new Point();
this.widthHeight = 20;
this.trailing1TopLeft = new Point();
this.trailing2TopLeft = new Point();
this.trailing3TopLeft = new Point();
this.trailing4TopLeft = new Point();
this.infoBoxTopLeft = new Point();
this.infoBoxWidth = 60;
this.infoBoxHeight = 30;
this.dx = 3;
this.dy = 3;
}
function AircraftType() {
this.type = "";
this.minSpeed = 0;
}
function Point() {
this.x = 0;
this.y = 0;
}
function Border() {
this.topLeft = new Point();
this.width = 0;
this.height = 0;
this.borderTop = 0;
this.borderBottom = 0;
this.borderLeft = 0;
this.borderRight = 0;
this.lineThickness = 10;
this.lineColour = "#1EFF00";
}
//---------------------------- MARK: Event Listeners ----------------------------\\
document.addEventListener("click", mouseClickHandler, false);
document.addEventListener("mousedown", mouseDownHandler, false);
document.addEventListener("mouseup", mouseUpHandler, false);
document.addEventListener("mousemove", mouseMoveHandler, false);
function mouseClickHandler(e) {
// Get Mouse Position
var rectCanvas = canvas.getBoundingClientRect();
var positionX = e.clientX;
var positionY = e.clientY;
var booClickedOnWaypoint = false;
var booClickedOnRunway = false;
var booClickedOnPlane = false;
for (i = 0; i < aPlanes.length; i++) {
var intPlaneLeft = aPlanes[i].topPoint.x - (aPlanes[i].widthHeight / 2);
var intPlaneRight = aPlanes[i].topPoint.x + (aPlanes[i].widthHeight / 2);
var intPlaneTop = aPlanes[i].topPoint.y;
var intPlaneBottom = aPlanes[i].topPoint.y + aPlanes[i].widthHeight;
var intInfoBoxLeft = aPlanes[i].infoBoxTopLeft.x - (intInfoBoxLineWidth / 2);
var intInfoBoxRight = aPlanes[i].infoBoxTopLeft.x + aPlanes[i].infoBoxWidth + (intInfoBoxLineWidth / 2);
var intInfoBoxTop = aPlanes[i].infoBoxTopLeft.y - (intInfoBoxLineWidth / 2);
var intInfoBoxBottom = aPlanes[i].infoBoxTopLeft.y + aPlanes[i].infoBoxHeight + (intInfoBoxLineWidth / 2);
if (((positionX >= intPlaneLeft) && (positionX <= intPlaneRight) && (positionY >= intPlaneTop) && (positionY <= intPlaneBottom)) || ((positionX >= intInfoBoxLeft) && (positionX <= intInfoBoxRight) && (positionY >= intInfoBoxTop) && (positionY <= intInfoBoxBottom))) {
aPlanes[i].selected = true;
booClickedOnPlane = true;
} else {
aPlanes[i].selected = false;
}
}
}
function mouseDownHandler(e) {
var positionX = e.clientX;
var positionY = e.clientY;
for (i = 0; i < aPlanes.length; i++) {
var intInfoBoxLeft = aPlanes[i].infoBoxTopLeft.x - (intInfoBoxLineWidth / 2);
var intInfoBoxRight = aPlanes[i].infoBoxTopLeft.x + aPlanes[i].infoBoxWidth + (intInfoBoxLineWidth / 2);
var intInfoBoxTop = aPlanes[i].infoBoxTopLeft.y - (intInfoBoxLineWidth / 2);
var intInfoBoxBottom = aPlanes[i].infoBoxTopLeft.y + aPlanes[i].infoBoxHeight + (intInfoBoxLineWidth / 2);
if ((positionX >= intInfoBoxLeft) && (positionX <= intInfoBoxRight) && (positionY >= intInfoBoxTop) && (positionY <= intInfoBoxBottom)) {
intPlaneInfoBoxSelected = i;
intPlaneInfoBoxSelectedXOffset = positionX - aPlanes[i].infoBoxTopLeft.x;
intPlaneInfoBoxSelectedYOffset = positionY - aPlanes[i].infoBoxTopLeft.y;
}
}
}
function mouseUpHandler(e) {
intPlaneInfoBoxSelected = -1;
}
function mouseMoveHandler(e) {
var positionX = e.clientX;
var positionY = e.clientY;
if (intPlaneInfoBoxSelected > -1) {
aPlanes[intPlaneInfoBoxSelected].infoBoxTopLeft.x = positionX - intPlaneInfoBoxSelectedXOffset;
aPlanes[intPlaneInfoBoxSelected].infoBoxTopLeft.y = positionY - intPlaneInfoBoxSelectedYOffset;
}
}
//---------------------------- MARK: Setup ----------------------------\\
function SetupArrays() {}
function SetupLevel() {
if (strLevel == "YSSY") {
//Waypoints
addWaypoint("LEFT", {
x: border.borderLeft,
y: 100
}, true, false);
}
}
function SetupCanvas() {
strLevel = "YSSY";
}
function LoadAircraftTypes() {
}
function LoadAirlineNames() {}
//---------------------------- MARK: Draw Existing Things ----------------------------\\
function drawPlanes() {
for (i = 0; i < aPlanes.length; i++) {
// Line
ctx.lineWidth = 4;
ctx.fillStyle = 'red';
ctx.strokeStyle = 'red';
ctx.beginPath();
ctx.moveTo((aPlanes[i].topPoint.x), (aPlanes[i].topPoint.y + (aPlanes[i].widthHeight / 2)));
ctx.lineTo((aPlanes[i].infoBoxTopLeft.x + (aPlanes[i].infoBoxWidth / 2)), (aPlanes[i].infoBoxTopLeft.y + (aPlanes[i].infoBoxHeight / 2)));
ctx.stroke();
// Plane
ctx.fillStyle = 'red';
ctx.strokeStyle = 'red';
if (aPlanes[i].selected == true) {
var pointX = aPlanes[i].topPoint.x;
var pointY = aPlanes[i].topPoint.y;
var width = aPlanes[i].widthHeight;
ctx.beginPath();
ctx.moveTo(pointX, pointY);
ctx.lineTo(pointX + (width / 2), pointY + (width / 2));
ctx.lineTo(pointX, pointY + width);
ctx.lineTo(pointX - (width / 2), pointY + (width / 2));
ctx.closePath();
ctx.fill();
} else {
var lineThickness = 3;
var pointX = aPlanes[i].topPoint.x;
var pointY = aPlanes[i].topPoint.y + lineThickness;
var width = aPlanes[i].widthHeight - (lineThickness * 2);
ctx.beginPath();
ctx.lineWidth = lineThickness;
ctx.moveTo(pointX, pointY);
ctx.lineTo(pointX + (width / 2), pointY + (width / 2));
ctx.lineTo(pointX, pointY + width);
ctx.lineTo(pointX - (width / 2), pointY + (width / 2));
ctx.closePath();
ctx.stroke();
}
// Flight Information Box
ctx.rect(aPlanes[i].infoBoxTopLeft.x, aPlanes[i].infoBoxTopLeft.y, aPlanes[i].infoBoxWidth, aPlanes[i].infoBoxHeight);
ctx.strokeStyle = 'red';
ctx.fillStyle = 'black';
ctx.lineWidth = clone(intInfoBoxLineWidth);
ctx.fill();
ctx.stroke();
}
}
function drawRunways() {
}
function drawText() {
}
function DrawWaypoints() {
}
function DrawCanvas() {
}
//---------------------------- MARK: Create Motion ----------------------------\\
function movePlanes() {
//TODO
if (aPlanes.length > 0) {
for (i = 0; i < aPlanes.length; i++) {
aPlanes[i].topPoint.x += aPlanes[i].dx;
aPlanes[i].infoBoxTopLeft.x += aPlanes[i].dx;
aPlanes[i].topPoint.y += aPlanes[i].dy;
aPlanes[i].infoBoxTopLeft.y += aPlanes[i].dy;
}
}
}
//---------------------------- MARK: Collision Detection ----------------------------\\
function detectLanded() {
}
//---------------------------- MARK: Create New Things ----------------------------\\
function addPlane() {
var tempPlane = new Plane();
var astrInboundOutbound = ["Inbound", "Outbound"];
var strRouteDirection = astrInboundOutbound[random(astrInboundOutbound.length)];
strRouteDirection = "Inbound";
if (strRouteDirection == "Inbound") {
// Start at an inbound waypoint
var astrInboundWaypoints = [];
for (var i = 0; i < aWayPoints.length; i++) {
if (aWayPoints[i].inbound == true) {
astrInboundWaypoints.push(aWayPoints[i]);
}
};
var intArrayPosition = random(astrInboundWaypoints.length);
var selectedWaypoint = astrInboundWaypoints[intArrayPosition];
tempPlane.topPoint = clone(selectedWaypoint.topLeft);
tempPlane.topPoint.x += (selectedWaypoint.widthHeight / 2);
tempPlane.topPoint.y += ((selectedWaypoint.widthHeight - tempPlane.widthHeight) / 2);
switch (selectedWaypoint.border) {
case "Left":
tempPlane.dx = intEntrySpeed;
tempPlane.dy = 0;
break;
case "Right":
tempPlane.dx = -1 * intEntrySpeed;
tempPlane.dy = 0;
break;
case "Top":
tempPlane.dx = 0;
tempPlane.dy = intEntrySpeed;
break;
case "Bottom":
tempPlane.dx = 0;
tempPlane.dy = -1 * intEntrySpeed;
break;
}
} else {
// Start at the upwind end of a runway
// TODO
}
tempPlane.infoBoxTopLeft = {
x: (tempPlane.topPoint.x - (tempPlane.infoBoxWidth / 2)),
y: (tempPlane.topPoint.y - tempPlane.infoBoxHeight - 20)
};
aPlanes.push(tempPlane);
}
function addRunway(name, topLeft, width, height) {
}
function addWaypoint(name, topLeft, inbound, outbound) {
var tempWaypoint = new WayPoint();
tempWaypoint.name = name;
tempWaypoint.topLeft = topLeft;
tempWaypoint.inbound = inbound;
tempWaypoint.outbound = outbound;
if (tempWaypoint.topLeft.x == border.borderLeft) {
tempWaypoint.border = "Left";
tempWaypoint.topLeft.x = border.borderLeft - (tempWaypoint.widthHeight / 2);
}
if (tempWaypoint.topLeft.y == border.borderTop) {
tempWaypoint.border = "Top";
tempWaypoint.topLeft.y = border.borderTop - (tempWaypoint.widthHeight / 2);
}
if (tempWaypoint.topLeft.x == border.borderRight) {
tempWaypoint.border = "Right";
tempWaypoint.topLeft.x = border.borderRight - (tempWaypoint.widthHeight / 2);
}
if (tempWaypoint.topLeft.y == border.borderBottom) {
tempWaypoint.border = "Bottom";
tempWaypoint.topLeft.y = border.borderBottom - (tempWaypoint.widthHeight / 2);
}
if (tempWaypoint.border != "") {
// Plus half the line width one all sides
tempWaypoint.topLeft.x -= (intWaypointLineWidth / 2);
tempWaypoint.topLeft.y -= (intWaypointLineWidth / 2);
tempWaypoint.widthHeight += intWaypointLineWidth;
}
aWayPoints.push(tempWaypoint);
}
//---------------------------- MARK: Timer ----------------------------\\
function timerTick() {
timer += 1;
if (timer % 1000 == 0) {
seconds++;
}
if (timer == 1) {
SetupArrays();
SetupCanvas();
SetupLevel();
}
if (timer == 300) {
addPlane();
}
/*if(timer==5){
document.location.reload();
}*/
}
//---------------------------- MARK: Supplimentary Routines ----------------------------\\
function random(intNumber) {
return Math.floor((Math.random() * intNumber));
}
function getTextWidth(text, font) {
ctx.font = font;
var metrics = ctx.measureText(text);
return metrics.width;
}
function getTextHeight(text, font) {
ctx.font = font;
var metrics = ctx.measureText(text);
return metrics.height;
}
function clone(obj) {
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
var copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
var copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = clone(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
var copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
//---------------------------- MARK: DRAW ----------------------------\\
function draw() {
// All movement and display
// Call functions to make things happen, listeners will work on their own, no need to call
ctx.clearRect(0, 0, canvas.width, canvas.height);
movePlanes();
drawPlanes();
requestAnimationFrame(draw);
}
//---------------------------- MARK: Other ----------------------------\\
setInterval(timerTick, 1);
draw();
//---------------------------- END OF SCRIPT ----------------------------\\
<canvas id="gameCanvas"></canvas>
The function that draws the plane is called drawPlanes(), and I have put in a dozen or so blank lines after it so you can reach it quickly when scrolling.
You will see that when you run the code, the plane shape starts 'flying' from the left to right, and that it is in its unselected state. However, clicking on it to select it doesn't fill it in like it should, it just enlarges the shape size and moves the border out.
Can anyone help me out?
Turns out this issue had quite a simple fix. My problem was that I wasn't using beginPath() or closePath() correctly. I was able to fix my issues by putting a beginPath() before modifying the parameters for each shape, and a closePath() when I'd finished drawing it.
Here is my code after fixing the issue:
// Line
ctx.beginPath();
ctx.lineWidth = 4;
ctx.fillStyle = 'red';
ctx.strokeStyle = 'red';
ctx.moveTo((aPlanes[i].topPoint.x), (aPlanes[i].topPoint.y + (aPlanes[i].widthHeight / 2)));
ctx.lineTo((aPlanes[i].infoBoxTopLeft.x + (aPlanes[i].infoBoxWidth / 2)), (aPlanes[i].infoBoxTopLeft.y + (aPlanes[i].infoBoxHeight / 2)));
ctx.stroke();
ctx.closePath();
// Plane
ctx.beginPath();
ctx.fillStyle = 'red';
ctx.strokeStyle = 'red';
if(aPlanes[i].selected == true) {
var pointX = aPlanes[i].topPoint.x;
var pointY = aPlanes[i].topPoint.y;
var width = aPlanes[i].widthHeight;
ctx.beginPath();
ctx.moveTo(pointX, pointY);
ctx.lineTo(pointX + (width / 2), pointY + (width / 2));
ctx.lineTo(pointX, pointY + width);
ctx.lineTo(pointX - (width / 2), pointY + (width / 2));
//ctx.closePath();
ctx.fill();
} else {
var lineThickness = 3;
var pointX = aPlanes[i].topPoint.x;
var pointY = aPlanes[i].topPoint.y + lineThickness;
var width = aPlanes[i].widthHeight - (lineThickness * 2);
ctx.beginPath();
ctx.lineWidth = lineThickness;
ctx.moveTo(pointX, pointY);
ctx.lineTo(pointX + (width / 2), pointY + (width / 2));
ctx.lineTo(pointX, pointY + width);
ctx.lineTo(pointX - (width / 2), pointY + (width / 2));
//ctx.closePath();
ctx.stroke();
}
ctx.closePath();
// Flight Information Box
ctx.beginPath();
ctx.rect(aPlanes[i].infoBoxTopLeft.x, aPlanes[i].infoBoxTopLeft.y, aPlanes[i].infoBoxWidth, aPlanes[i].infoBoxHeight);
ctx.strokeStyle = 'red';
ctx.fillStyle = 'black';
ctx.lineWidth = clone(intInfoBoxLineWidth);
ctx.fill();
ctx.stroke();
ctx.closePath();
Hopefully this will help someone!
Not 100% sure what you are trying to do so this is my best guess.
You missed a ctx.beginPath() at the bottom of the draw function. This caused the diamond to be rerendered. And in the selected if clause you were not calling fill, and the unselected else clause not calling stroke. But then I am not sure what you wanted.
Here is the code that makes it do what you describe in the question. Be careful with variable declarations. The i in the for loop was undeclared so was a global. If you called a function that did the same you would have had a very hard to trace bug.
function drawPlanes() {
var i,pointX,pointY,width,lineThickness; // Always declare the variables with var, const or let
lineThickness = 3;// <<================= Moved this out of loop as it did not seem to change
for (i = 0; i < aPlanes.length; i++) {
// Line
ctx.lineWidth = 4;
ctx.fillStyle = 'red';
ctx.strokeStyle = 'red';
ctx.beginPath();
ctx.moveTo((aPlanes[i].topPoint.x), (aPlanes[i].topPoint.y + (aPlanes[i].widthHeight / 2)));
ctx.lineTo((aPlanes[i].infoBoxTopLeft.x + (aPlanes[i].infoBoxWidth / 2)), (aPlanes[i].infoBoxTopLeft.y + (aPlanes[i].infoBoxHeight / 2)));
ctx.stroke();
if (aPlanes[i].selected == true) {
// set fill and stroke style
ctx.fillStyle = 'red'; // <<=================== I added this line
ctx.strokeStyle = 'red'; // <<=================== I added this line
ctx.lineWidth = lineThickness; // <<=================== I added this line
pointX = aPlanes[i].topPoint.x;
pointY = aPlanes[i].topPoint.y;
width = aPlanes[i].widthHeight;
ctx.beginPath();
ctx.moveTo(pointX, pointY);
ctx.lineTo(pointX + (width / 2), pointY + (width / 2));
ctx.lineTo(pointX, pointY + width);
ctx.lineTo(pointX - (width / 2), pointY + (width / 2));
ctx.closePath();
ctx.stroke(); // <<=================== I added this line
ctx.fill();
} else {
ctx.fillStyle = 'black'; // <<=================== I added this line
ctx.strokeStyle = 'red'; // <<=================== I added this line
ctx.lineWidth = lineThickness; // <<=================== I added this line
pointX = aPlanes[i].topPoint.x;
pointY = aPlanes[i].topPoint.y + lineThickness;
width = aPlanes[i].widthHeight - (lineThickness * 2);
ctx.beginPath();
ctx.moveTo(pointX, pointY);
ctx.lineTo(pointX + (width / 2), pointY + (width / 2));
ctx.lineTo(pointX, pointY + width);
ctx.lineTo(pointX - (width / 2), pointY + (width / 2));
ctx.closePath();
ctx.stroke();
ctx.fill(); // <<=================== I added this line
}
// Flight Information Box
ctx.beginPath(); // <<=================== I added this
ctx.rect(aPlanes[i].infoBoxTopLeft.x, aPlanes[i].infoBoxTopLeft.y, aPlanes[i].infoBoxWidth, aPlanes[i].infoBoxHeight);
ctx.strokeStyle = 'red';
ctx.fillStyle = 'black';
ctx.lineWidth = clone(intInfoBoxLineWidth); // <<==?????? you are cloning a number. Not sure why maybe you have something else in mind.
ctx.fill();
ctx.stroke();
}
}
I have drawn a grid in html5 canvas using StrokeLineX and StrokeLineY. I want to highlight the particular square/rectangle when I click on it in the grid.
I have tried using math.floor to define an index for a space but as soon as the width or height increases it starts giving different answers. I have tried too many times before finally posting it here.
Here is the code.
var canvas = document.getElementById("myCanvas");
canvas.addEventListener('click', on_canvas_click, false);
var ctx = canvas.getContext("2d");
var tray_length = 800;
var tray_depth = 800;
var boxHeight = 50;
var boxWidth = 50;
var canvas_X = tray_length;
var canvas_Y = tray_depth;
var box_x_pixels = canvas_X/no_of_columns;
var box_y_pixels = canvas_Y/no_of_rows;
// Drawing the grid
for (var y = boxWidth; y < canvas_Y; y += boxWidth) {
strokeLineX(ctx, y);
}
for (var x = boxHeight; x < canvas_X; x += boxHeight) {
strokeLineY(ctx, x);
}
function strokeLineX(ctx, y) {
ctx.beginPath();
ctx.strokeStyle = 'green';
ctx.moveTo(0, y);
ctx.lineTo(canvas_X, y);
ctx.stroke();
ctx.closePath();
}
function strokeLineY(ctx, x) {
ctx.beginPath();
ctx.strokeStyle = 'red';
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas_Y);
ctx.stroke();
ctx.closePath();
}
function on_canvas_click(ev) {
var x = ev.pageX - canvas.offsetLeft;
var y = ev.pageY - canvas.offsetTop;
console.log(x+":"+y);
var coordinateDisplay = "x=" + x + ", y=" + y;
if (y>= 0 && y <= y+boxHeight ) {
var indexOfX = Math.floor(x/boxWidth); //divide on width and round off
var indexOfY = Math.floor(y/boxHeight);
// alert('You clicked bar index: ' + indexOfX+"-"+indexOfY);
ctx.fillRect="green";
ctx.rect(x,y,box_x_pixels,box_y_pixels);
ctx.stroke();
console.log(indexOfX + "-" + indexOfY);
}
}
In on_canvas_click change the following:
Instead of
ctx.fillRect="green";
do:
ctx.fillStyle="green";
And instead of:
ctx.rect(x,y,box_x_pixels,box_y_pixels);
do:
ctx.fillRect(boxWidth*indexOfX, boxHeight*indexOfY, boxWidth, boxHeight);
... and you don't need:
ctx.stroke();
Here is a working snippet with those changes applied, but also with some simplifications which are unrelated to your problem:
Removal of unused variables;
Use of one function to draw lines, instead of two;
Use of canvas.width and canvas.height properties;
Drawing grid lines also on the outer grid boundaries
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var boxHeight = 50;
var boxWidth = 50;
var canvas_X = canvas.width;
var canvas_Y = canvas.height;
canvas.addEventListener('click', on_canvas_click, false);
// Drawing the grid
for (var y = 0; y <= canvas_Y; y += boxWidth) {
strokeLine(ctx, 0, y, canvas_X, y, 'green');
}
for (var x = 0; x <= canvas_X; x += boxHeight) {
strokeLine(ctx, x, 0, x, canvas_Y, 'red');
}
function strokeLine(ctx, x0, y0, x1, y1, color) {
ctx.beginPath();
ctx.strokeStyle = color;
ctx.moveTo(x0, y0);
ctx.lineTo(x1, y1);
ctx.stroke();
ctx.closePath();
}
function on_canvas_click(ev) {
var x = ev.pageX - canvas.offsetLeft;
var y = ev.pageY - canvas.offsetTop;
if (y>= 0 && y <= y+boxHeight ) {
var indexOfX = Math.floor(x/boxWidth); //divide on width and round off
var indexOfY = Math.floor(y/boxHeight);
ctx.fillStyle="green";
ctx.fillRect(boxWidth*indexOfX, boxHeight*indexOfY, boxWidth, boxHeight);
}
}
<canvas id="myCanvas" width="600" height="200"></canvas>
I have a canvas element that is currently animating lines. However I want to make an array of stored functions that change the numbers and colors of those lines animating in the canvas.
Thus, when I click a specific element, it will select one of the functions in an array which change the colors, speed, line-width, amplitude, etc. to one of those functions.
So let's say I have an array of functions, settings = [A, B, C, D];
where A to D are functions that change the canvas.
Ultimately, I want it so that when I click an element I randomly change the canvas element's settings to those in A, B, C, or D.
I have the following code but am having trouble refactoring the click function to include an array of settings to separate functions. Any help?
Below is the following code I have so far:
var c = document.querySelector('.c') /* canvas element */,
w /* canvas width */, h /* canvas height */,
ctx = c.getContext('2d') /* canvas context */,
/* previous & current coordinates */
x0, y0, x, y,
t = 0, t_step = 1/600,
u = 4, m,
tmp,
/* just me being lazy */
ceil = Math.ceil,
exp = Math.exp, pow = Math.pow, sqrt = Math.sqrt,
PI = Math.PI, sin = Math.sin, cos = Math.cos;
/* FUNCTIONS */
/* a random number between min & max */
var rand = function(max, min) {
var b = (max === 0 || max) ? max : 1, a = min || 0;
return a + (b - a)*Math.random();
};
var trimUnit = function(input_str, unit) {
return parseInt(input_str.split(unit)[0], 10);
};
var initCanvas = function() {
var s = getComputedStyle(c);
w = c.width = trimUnit(s.width, 'px');
h = c.height = trimUnit(s.height, 'px');
m = ceil(w/(10*u)) + 90;
};
var wave = function () {
ctx.clearRect(0, 0, w, h);
ctx.lineWidth = 1.75;
for(var i = 0; i < m; i++) {
x0 = -80;
y0 = i*4*u;
ctx.beginPath();
ctx.moveTo(x0, y0);
for(x = 0; x < w; x++) {
y = u*sin(x/4/(10*i/m + 1) - w*(i/m + 2)*t/20) + i*2*u;
ctx.lineTo(x, y);
x0 = x;
y0 = y;
}
ctx.strokeStyle = 'hsl(' + i*360/m + ', 100%, 70%)';
ctx.stroke();
}
t += t_step;
requestAnimationFrame(wave);
};
addEventListener('resize', initCanvas, false);
initCanvas();
wave();
/*Moods*/
var red = function () {
ctx.clearRect(0, 0, w, h);
ctx.lineWidth = 10;
for(var i = 0; i < m; i++) {
x0 = -100;
y0 = i*8*u;
ctx.beginPath();
ctx.moveTo(x0, y0);
for(x = 0; x < w; x++) {
y = u*sin(x/4/(16*i/m + 1) - w*(i/m + 1)*t/12) + i*2.5*u;
ctx.lineTo(x, y);
x0 = x;
y0 = y;
}
var gradient=ctx.createLinearGradient(0,1000,0,0);
gradient.addColorStop("0.1","orange");
gradient.addColorStop("0.5","red");
gradient.addColorStop("1.0","pink");
ctx.strokeStyle = gradient;
ctx.stroke();
}
t += t_step;
requestAnimationFrame(red);
};
var blue = function () {
ctx.clearRect(0, 0, w, h);
ctx.lineWidth = 1.5;
for(var i = 0; i < m; i++) {
x0 = -100;
y0 = i*8*u;
ctx.beginPath();
ctx.moveTo(x0, y0);
for(x = 0; x < w; x++) {
y = u*sin(x/4/(16*i/m + 1) - w*(i/m + 1)*t/12) + i*2.5*u;
ctx.lineTo(x, y);
x0 = x;
y0 = y;
}
var gradient=ctx.createLinearGradient(0,1000,0,0);
gradient.addColorStop("0.1","lightblue");
gradient.addColorStop("0.5","blue");
gradient.addColorStop("1.0","white");
ctx.strokeStyle = gradient;
ctx.stroke();
}
t += t_step;
requestAnimationFrame(blue);
};
/*Mood Functions Above This Point*/
function hundred(min, max) {
return Math.random() * (max - min) + min;
}
$('#click').on('click',function(){
$(".c").fadeOut('700');
setTimeout(function(){
$(".c").fadeIn('900');
},100);
setTimeout(function(){
m = ceil(w/(10*u)) + hundred(0,100);Math.random()*60*9;
/*m = ceil(w/(10*u)) + 100;*/
u = hundred(2,6)
},100);
blue();
});
Most of your red() & blue() code is identical so create 1 animation loop with the common code (animate()).
Gradients are expensive so create each gradient once at the beginning of the app and store them in an object (gradients{}).
Declare a gradientMix variable to tell animate() which gradient to use.
Here is refactored code:
// gradients are expensive so create them once at the start of the app
var gradients={};
gradients['red']=addGradient('orange','red','pink');
gradients['blue']=addGradient('lightblue','blue','white');
var gradientMix='blue';
// animate function with common code
function animate(time){
ctx.clearRect(0, 0, w, h);
ctx.lineWidth = 1.5;
for(var i = 0; i < m; i++) {
x0 = -100;
y0 = i*8*u;
ctx.beginPath();
ctx.moveTo(x0, y0);
for(x = 0; x < w; x++) {
y = u*sin(x/4/(16*i/m + 1) - w*(i/m + 1)*t/12) + i*2.5*u;
ctx.lineTo(x, y);
x0 = x;
y0 = y;
}
// set the strokeStyle to the selected gradient mix
ctx.strokeStyle = gradients[gradientMix];
ctx.stroke();
}
t += t_step;
requestAnimationFrame(animate);
};
function addGradient(color,g1,g2,g3){
var gradient=ctx.createLinearGradient(0,1000,0,0);
gradient.addColorStop("0.1",g1);
gradient.addColorStop("0.5",g2);
gradient.addColorStop("1.0",g3);
}