I'm learning how to use canvas and I wanted to make a painting app. I can easily draw lines and shapes, but I can't understand how to change the width of a line from a specified size to one another with the same line.
I would like to do something like:
Draw a line from point (0, 1) to point (10, 10) and size from 10 to 5
Is it possible?
try this, the idea is to draw several lines
var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');
function drawline(xo,yo,xf,yf,wo,wf, parts){
var stepX = (xf-xo)/parts;
var stepY = (yf-yo)/parts;
var stepW = (wf-wo)/parts;
for(var i=1; i<=parts; ++i){
ctx.beginPath();
ctx.lineWidth = wo + stepW*i;
ctx.moveTo(xo+stepX*(i-1), yo+stepY*(i-1));
ctx.lineTo(xo+stepX*i, yo+stepY*i);
ctx.stroke();
}
}
drawline(10,10,150,150,1,10, 100);
drawline(10,10,150,300,1,10, 100);
<canvas id="canvas1" width="500" height="500">
</canvas>
or, a solution better, using trapezoids (no general solution shows)
var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');
function drawline(xo,yo,xf,yf,wo,wf){
//if xf == xo vertical line, failure ;)
var m = (yf-yo)/(xf-xo);
//if m==0, horizontal line, failure
var mp = -1 / m;
var angle = Math.atan(mp);
var dy1 = Math.sin(angle) * wo/2;
var dx1 = Math.cos(angle) * wo/2;
var dy2 = Math.sin(angle) * wf/2;
var dx2 = Math.cos(angle) * wf/2;
//depending on the positions of xo,yo,xf and yf change signs
//this isn't a general solution
//solution for xo<xf and yo<yf
var x1 = xo + dx1;
var y1 = yo + dy1;
var x2 = xo - dx1;
var y2 = yo - dy1;
var x3 = xf - dx2;
var y3 = yf - dy2;
var x4 = xf + dx2;
var y4 = yf + dy2;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.lineTo(x4, y4);
ctx.fill();
}
drawline(10,10,150,150,1,10);
drawline(10,10,150,300,1,10);
<canvas id="canvas1" width="500" height="500">
</canvas>
Related
I'm working on a webapp and it includes one part where I draw the graph of a function, the coordinate system is made by Canvas. The problem is, I can not zoom into my coordinate system. I want to make it able to zoom in and out + moving the coordinate system using the mouse. The x and y values should also increase/decrease while zooming in/out.
Could somebody help me with this ?
I searched for some solutions, but I couldn't find anything useful. That's why I decided to ask it here.
Here are my codes:
<canvas id="myCanvas" width="300" height="300" style="border:1px solid #d3d3d3;"></canvas>
<!--Canva startup-->
<script>
// Setup values
var height = 300;
var width = 300;
var zoomFactor = 15;
// --------
var c = document.getElementById("myCanvas");
var xZero = width / 2;
var yZero = height / 2;
var ctx = c.getContext("2d");
// Draw Cord-System-Grid
ctx.beginPath();
ctx.moveTo(xZero, 0);
ctx.lineTo(xZero, height);
ctx.strokeStyle = "#000000";
ctx.stroke();
ctx.moveTo(0, yZero);
ctx.lineTo(width, yZero);
ctx.strokeStyle = "#000000";
ctx.stroke();
ctx.beginPath();
// Draw Numbers
ctx.font = "10px Georgia";
var heightTextX = yZero + 10;
for(var i = 0; i < width; i = i + width / 10) {
var numberX = (-1 * xZero / zoomFactor) + i / zoomFactor;
ctx.fillText(numberX, i, heightTextX);
}
var heightTextY = yZero + 10;
for(var n = 0; n < height; n = n + height / 10) {
var numberY = (-1 * yZero / zoomFactor) + n / zoomFactor;
if(numberY !== 0)
ctx.fillText(numberY * -1, heightTextY, n);
}
</script>
I asked this question before a week, but couldn't get an answer.
I hope somebody can help me
Attach the onwheel event to a function and use the ctx.scale() function to zoom in and out.
You will probably want to do something like
let canvas = document.getElementById('myCanvas');
ctx.translate(canvas.width/2, canvas.height/2);
ctx.scale(zoomFactor, zoomFactor);
ctx.translate(-canvas.width/2, -canvas.height/2);
To make sure it zooms from the center.
Sorry for any minor errors I'm writing this from my phone.
I am trying to draw lissajous curves using the canvas and javascript. I have put the formula's in but instead of drawing nice curves, the canvas looks like a complete mess. The code im currently using is:
<canvas id="myCanvas" width="720" height="720" style="border:1px solid #d3d3d3;">
<script src="canvas.js"> </script>
And for my javascript:
var canvas = document.querySelector('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctxt = canvas.getContext('2d');
var x;
var y;
var STARTi = 0;
var MAXi = 100;
var yOffset = canvas.height / 2;
var xOffset = canvas.width / 2;
var xAmp = 2;
var yAmp = 3;
var xFreq = 6;
var yFreq = 3;
var phaseDiff = Math.PI / 2;
//X-Axis
ctxt.beginPath();
ctxt.setLineDash([5, 15]);
ctxt.moveTo(0, canvas.height / 2);
ctxt.lineTo(canvas.width, canvas.height / 2);
ctxt.strokeStyle = "rgba(169,169,169)"
ctxt.stroke();
//y-Axis
ctxt.beginPath();
ctxt.setLineDash([5, 15]);
ctxt.moveTo(canvas.width / 2, 0);
ctxt.lineTo(canvas.width / 2, canvas.height);
ctxt.strokeStyle = "rgba(169,169,169)"
ctxt.stroke();
ctxt.setLineDash([0, 0]);
function xCoord(a) {
return 100 * (xAmp * Math.sin(xFreq * (0.4 * a) + phaseDiff));
}
function yCoord(a) {
return -100 * (yAmp * Math.sin(yFreq * (0.4 * a)));
}
x = xCoord(STARTi);
y = yCoord(x);
ctxt.beginPath();
ctxt.moveTo(x + xOffset, y + yOffset)
for (i = STARTi; i < MAXi; i++) {
ctxt.moveTo(x + xOffset, y + yOffset)
x = xCoord(i);
y = yCoord(x);
ctxt.lineTo(x + xOffset, y + yOffset)
ctxt.strokeStyle = "black"
ctxt.stroke();
}
Im trying to get these curved and i have tried things such as making the increase of the value so small that it will eventually look smooth: didn't work, just crashed my browser eventually.
I also tried to draw these lines using the Arc property. This gave me some smooth lines, but not ones that looked like lisajous curves. I believe this mainly is because i did not know what values or variables in needed to put in for the other required parameters.
I hope it is a little bit clear what i have problems with.
Thanks in advance for all the help. ;)
All angle operations in JavaScript uses radians [0, 2 × π>, but the xCoord() and yCoord() functions uses degrees [0, 360>.
Simple use radians instead or convert inside the functions:
function xCoord(a) {
// "a" converted from degree to radian _________________
return 100 * (xAmp * Math.sin(xFreq * (0.4 * a * Math.PI / 180) + phaseDiff));
}
function yCoord(a) {
return -100 * (yAmp * Math.sin(yFreq * (0.4 * a * Math.PI / 180)));
}
var canvas = document.querySelector('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctxt = canvas.getContext('2d');
var x;
var y;
var STARTi = 0;
var MAXi = 100;
var yOffset = canvas.height / 2;
var xOffset = canvas.width / 2;
var xAmp = 2;
var yAmp = 3;
var xFreq = 6;
var yFreq = 3;
var phaseDiff = Math.PI / 2;
//X-Axis
ctxt.beginPath();
ctxt.setLineDash([5, 15]);
ctxt.moveTo(0, canvas.height / 2);
ctxt.lineTo(canvas.width, canvas.height / 2);
ctxt.strokeStyle = "rgba(169,169,169)"
ctxt.stroke();
//y-Axis
ctxt.beginPath();
ctxt.setLineDash([5, 15]);
ctxt.moveTo(canvas.width / 2, 0);
ctxt.lineTo(canvas.width / 2, canvas.height);
ctxt.strokeStyle = "rgba(169,169,169)"
ctxt.stroke();
ctxt.setLineDash([0, 0]);
function xCoord(a) {
return 100 * (xAmp * Math.sin(xFreq * (0.4 * a * Math.PI / 180) + phaseDiff));
}
function yCoord(a) {
return -100 * (yAmp * Math.sin(yFreq * (0.4 * a * Math.PI / 180)));
}
x = xCoord(STARTi);
y = yCoord(x);
ctxt.beginPath();
ctxt.moveTo(x + xOffset, y + yOffset)
for (i = STARTi; i < MAXi; i++) {
ctxt.moveTo(x + xOffset, y + yOffset)
x = xCoord(i);
y = yCoord(x);
ctxt.lineTo(x + xOffset, y + yOffset)
}
ctxt.strokeStyle = "black"; // can be set right before stroked.
ctxt.stroke(); // call this outside the loop, otherwise the lines will
// overdraw eachother per iteration making it look pixelated
<canvas id="myCanvas" width="720" height="720" style="border:1px solid #d3d3d3;">
I also found an error in the argument for the ordinate Y. I believe this would be correct:
x = xCoord(i);
y = yCoord(i); // NO y = yCoord(x);
Similarly, the above
x = xCoord(STARTi);
y = yCoord(STARTi);
I want to create a spiral through canvas but in alphanumeric...
just like the code below but in alphanumeric..
it will start at A and end at 0...
<!DOCTYPE HTML>
<html><body>
<canvas id="myCanvas" width="300" height="300" style="border:1px solid #c3c3c3;"></canvas>
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var cxt=c.getContext("2d");
var centerX = 150;
var centerY = 150;
cxt.moveTo(centerX, centerY);
var gap = 1.8; // increase this for spacing between spiral lines
var STEPS_PER_ROTATION = 60; // increasing this makes the curve smoother
var increment = 2*Math.PI/STEPS_PER_ROTATION;
var theta = increment;
while( theta < 20*Math.PI) {
var newX = centerX + theta * Math.cos(theta) * gap;
var newY = centerY + theta * Math.sin(theta) * gap;
cxt.lineTo(newX, newY);
theta = theta + increment;
}
cxt.stroke(); // draw the spiral
</script></body></html>
Rotating and drawing text in the Canvas object isn't the easiest thing to do for someone who hasn't done it before. But that doesn't mean that it's hard.
The first part is drawing the text, so to start conversion we have to remove cxt.lineTo(newX, newY) and add cxt.fillText(char, newX, newY) in
while(theta < 20*Math.PI) {
var newX = centerX + theta * Math.cos(theta) * gap;
var newY = centerY + theta * Math.sin(theta) * gap;
//cxt.lineTo(newX, newY);
cxt.fillText('a', newX, newY);
theta = theta + increment;
}
This will put the character a at every curve-point that the spiral was using, but they all face the same default text direction. So to fix that you must rotate the characters. Using cxt.rotate() and Math.atan2() you can rotate the text for that point in the circle. Using cxt.save(), cxt.restore(), and cxt.translate() you won't have to unrotate or use math to position your characters properly. Putting these together you get:
while( theta < 20*Math.PI) {
var newX = centerX + theta * Math.cos(theta) * gap;
var newY = centerY + theta * Math.sin(theta) * gap;
cxt.save()
cxt.translate(newX, newY);
cxt.rotate(Math.atan2(centerY - newY, centerX - newX));
cxt.fillText('a', 0, 0);
cxt.restore();
theta = theta + increment;
}
By adding (0..2)*Math.PI to the rotation, you can then add a static rotation to the characters, making them all face inwards, or all face outwards, etc.
Adding all of this together, along with a counter and a character map, you can make the spiral slowly grow in font size and get what I believe is roughly what you were looking for.
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="300" height="300" style="border:1px solid #c3c3c3;"></canvas>
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var cxt=c.getContext("2d");
var centerX = 150;
var centerY = 150;
cxt.moveTo(centerX, centerY);
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'.split(''); // character map for spiral
var gap = 3; // increase this for spacing between spiral lines
var rotation = 0; // value between 0..1 that rotates the characters 0..360 degrees.
var spread = 1; // increasing this makes the spread more
var spirals = 10; // number of spirals
var STEPS_PER_ROTATION = 60; // increasing this adds more characters
var increment = spread*2*Math.PI/STEPS_PER_ROTATION;
var theta = increment;
var maxFont = 16;
cxt.font = '0px sans';
cxt.textBaseline = 'center';
let spiralCount = 2*spirals*Math.PI;
let char = 0;
while(theta < spiralCount) {
var newX = centerX + theta * Math.cos(theta) * gap;
var newY = centerY + theta * Math.sin(theta) * gap;
var rot = Math.atan2(newY - centerY, newX - centerX);
cxt.save();
cxt.translate(newX, newY);
cxt.rotate(rot + (rotation*2*Math.PI));
cxt.font = (maxFont*(theta/spiralCount)) + 'px sans';
cxt.fillText(characters[char], 0, 0);
cxt.restore();
theta = theta + increment;
char++;
if (char > characters.length - 1) char = 0;
}
cxt.stroke(); // draw the spiral
</script>
</body>
</html>
I am new to javascript so sorry if my mistake is obvious! I am trying to draw an ellipse using the canonical form equation.
<html>
<body>
<canvas id="myCanvas" width="400" height="400" style="border:1px solid #000000;">
</canvas>
<script language="javaScript">
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var HEIGHT = ctx.height;
var WIDTH = ctx.width;
function transform(p){
return [ WIDTH/2 + p[0], HEIGHT/2 - p[1]];
}
var a = 70; //length parallel to x axis
var b = 30; //length parallel to y axis
var h = 150 - WIDTH/2; //when tranformed this gives the pixel 150
var k = -HEIGHT/2 - 100; //when transformed this gives pixel 100
function y(x){
return b*Math.sqrt(1 - Math.pow((x - h)/a, 2)) + k;
}
var dx = a/2/100 ; //each quadrant is broken into 100 segments
var pOld = [h - a/2, k]; //starting point
var pNew = [0,0];
var x1 = 0;
var x2 = 0;
var y1 = 0;
var y2 = 0;
var temp = [0,0];
for (var i = 0; i < 100; ++i){
pNew[0] = pOld[0] + dx;
pNew[1] = y(pNew[0]);
temp = transform(pOld);
x1 = temp[0]; y1 = temp[1];
temp = transform(pNew);
x2 = temp[0]; y2 = temp[1];
ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); //x1, y1 -> x2, y2
ctx.moveTo(x1, -y1); ctx.lineTo(x2, -y2); //x1,-y1 -> x2,-y2
ctx.moveTo(-x1, y1); ctx.lineTo(-x2, y2); //-x1,y1 -> -x2, y2
ctx.moveTo(-x1, -y1); ctx.lineTo(-x2, -y2); //-x1,-y1 -> -x2,-y2
pOld = pNew;
}
ctx.lineWidth = 2;
ctx.strokeStyle = "#232323";
ctx.stroke();
</script>
</body>
</html>
You values are NaN (Not-a-Number). Change these lines:
var HEIGHT = ctx.height;
var WIDTH = ctx.width;
to
var HEIGHT = c.height;
var WIDTH = c.width;
and you get proper Numbers. However, your y value is outside the canvas area - you will see this if you console.log the values. There seem to be some issues with the math in general which I don't address here, but in relation to canvas you should be one step further.
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)
}