Making a Graph/Curve with Javascript and HTML - javascript

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.

Related

Canvas text is not all displaying

I'm recreating the classic Sandwich Alignment Chart using the HTML5 canvas, but my programatic titles for the blocks are not working properly.
They seem to print out fine in the console, so I cannot see why they don't all get rendered on the canvas.
Below is my latest Alignment Chart code.
// we create a canvas element
var canvas = document.getElementById('chart');
var height = 500;
var width = height * 1.5;
var blockWidth = width / 4;
var blockHeight = height / 4;
var largestFont = height / 12;
const subject = 'Sandwich';
const yAxisTerm = 'Structure';
const xAxisTerm = 'Ingredients';
const alignmentDegrees = ['Purist', 'Neutral', 'Rebel']
const alignmentDet = [
'Hardline Traditionalist',
'True Neutral',
`Radical ${subject} Anarchy`
]
const xDegrees = alignmentDegrees.map(degree => ({
title: `${xAxisTerm} ${degree}`
}));
const yDegrees = alignmentDegrees.map(degree => ({
title: `${yAxisTerm} ${degree}`
}));
const degreeColours = ['#264211', '#7B5C09', '#600000']
const blockColours = ['#303030', '#454545', '#595959', '#727272', '#828282'];
canvas.height = height*2;
canvas.width = width*2;
// getting the context will allow to manipulate the image
var context = canvas.getContext("2d");
// We create a new imageData.
var imageData = context.createImageData(width, height);
// The property data will contain an array of int8
var data = imageData.data;
context.lineWidth = 2;
context.strokeStyle = "black";
context.fillStyle = "white";
context.fillRect(0, 0, canvas.width, canvas.height);
context.stroke();
context.lineWidth = 2;
context.strokeStyle = "black";
for (let row = 0; row < 3; row++) {
const rowColours = blockColours.slice(row, row + 3)
for (let col = 0; col < 3; col++) {
context.fillStyle = rowColours[col];
context.fillRect(
width - blockWidth * (1 + row),
height - blockHeight * (1 + col),
blockWidth,
blockHeight
);
context.stroke();
const yBlockText = `${yAxisTerm} ${alignmentDegrees[col]},`;
const xBlockText = `${xAxisTerm} ${alignmentDegrees[row]}`;
context.fillStyle = "white";
context.font = `${largestFont/3}px Impact`;
const topTextX = blockWidth * (1 + row);
const topTextY = blockHeight * (1 + col) + largestFont / 2;
if (col === row) {
console.log(`${topTextX}, ${topTextY}, ${alignmentDet[col]}`)
context.fillText(
alignmentDet[col],
topTextX,
topTextY,
blockWidth
)
} else {
console.log(`${topTextX}, ${topTextY}, ${yBlockText} ${xBlockText}`)
context.fillText(
yBlockText,
topTextX,
topTextY,
blockWidth
)
context.fillText(
xBlockText,
topTextX,
topTextY + largestFont / 2,
blockWidth
)
}
}
};
context.beginPath();
context.strokeStyle = 'grey';
context.moveTo(blockWidth * 0.1, blockHeight * 2);
context.lineTo(blockWidth * 0.9, blockHeight * 2);
context.stroke();
context.beginPath();
context.strokeStyle = 'grey';
context.moveTo(blockWidth * 0.1, blockHeight * 3);
context.lineTo(blockWidth * 0.9, blockHeight * 3);
context.stroke();
context.beginPath();
context.strokeStyle = 'grey';
context.moveTo(blockWidth * 2, blockHeight * 0.5);
context.lineTo(blockWidth * 2, blockHeight * 0.9);
context.stroke();
context.beginPath();
context.strokeStyle = 'grey';
context.moveTo(blockWidth * 3, blockHeight * 0.5);
context.lineTo(blockWidth * 3, blockHeight * 0.9);
context.stroke();
context.fillStyle = "#e5e5e5";
context.fillRect(0, 0, canvas.width, blockHeight * 0.45);
context.stroke();
// text
context.fillStyle = "#ACACAC";
context.font = `${largestFont}px Impact`
context.textBaseline = 'middle';
context.textAlign = "center";
context.fillText(`The ${subject} Alignment chart`.toUpperCase(), canvas.width / 2, blockHeight * 0.25, canvas.width);
yDegrees.forEach((degreeText, index) => {
context.fillStyle = degreeColours[index];
context.font = `${largestFont/2}px Impact`;
context.fillText(degreeText.title, blockWidth / 2, blockHeight * (1 + index) + largestFont / 2, blockWidth)
})
xDegrees.forEach((degreeText, index) => {
context.fillStyle = degreeColours[index];
context.font = `${largestFont/2}px Impact`;
context.fillText(degreeText.title, blockWidth * (1.5 + index), blockHeight * (0.6), blockWidth)
})
function createData(type, mimetype) {
var value = canvas.toDataURL(mimetype);
if (value.indexOf(mimetype) > 0) { // we check if the format is supported
return {
type: type,
value: value
}
} else {
return false;
}
}
<canvas id="chart"></canvas>
Change the following:
context.fillRect(
width - blockWidth * (1 + row),
height - blockHeight * (1 + col),
blockWidth,
blockHeight
);
to:
context.fillRect(
blockWidth * (1 + row),
blockHeight * (1 + col),
blockWidth,
blockHeight
);
What you are doing is drawing the rectangle from right to left while writing the text in the grid from left to right and it wipes out what was plotted previously.
While this fixes the text issue you need to make adjustment to get the background colors correctly, by reversing the list of colours used:
const blockColours = ['#828282', '#727272', '#595959', '#454545', '#303030'];

How can we stop this HTML5 Canvas wheel at exact points after spin?

In the Below code link HTML5 canvas spin wheel game. I want to stop this canvas at a user-defined position as if the user wants to stop always at 200 texts or 100 texts like that.
Currently, it is stopping at random points I want to control where to stop as in if I want to stop circle at 100 or 200 or 0 whenever I want.
How can we achieve that??? Can anyone Help!!!!!
Attached Codepen link also.
Html file
<div>
<canvas class="spin-wheel" id="canvas" width="300" height="300"></canvas>
</div>
JS file
var color = ['#ca7','#7ac','#77c','#aac','#a7c','#ac7', "#caa"];
var label = ['10', '200','50','100','5','500',"0"];
var slices = color.length;
var sliceDeg = 360/slices;
var deg = 270;
var speed = 5;
var slowDownRand = 0;
var ctx = canvas.getContext('2d');
var width = canvas.width; // size
var center = width/2; // center
var isStopped = false;
var lock = false;
function rand(min, max) {
return Math.random() * (max - min) + min;
}
function deg2rad(deg){ return deg * Math.PI/180; }
function drawSlice(deg, color){
ctx.beginPath();
ctx.fillStyle = color;
ctx.moveTo(center, center);
ctx.arc(center, center, width/2, deg2rad(deg), deg2rad(deg+sliceDeg));
console.log(center, center, width/2, deg2rad(deg), deg2rad(deg+sliceDeg))
ctx.lineTo(center, center);
ctx.fill();
}
function drawText(deg, text) {
ctx.save();
ctx.translate(center, center);
ctx.rotate(deg2rad(deg));
ctx.textAlign = "right";
ctx.fillStyle = "#fff";
ctx.font = 'bold 30px sans-serif';
ctx.fillText(text, 130, 10);
ctx.restore();
}
function drawImg() {
ctx.clearRect(0, 0, width, width);
for(var i=0; i<slices; i++){
drawSlice(deg, color[i]);
drawText(deg+sliceDeg/2, label[i]);
deg += sliceDeg;
}
}
// ctx.rotate(360);
function anim() {
isStopped = true;
deg += speed;
deg %= 360;
// Increment speed
if(!isStopped && speed<3){
speed = speed+1 * 0.1;
}
// Decrement Speed
if(isStopped){
if(!lock){
lock = true;
slowDownRand = rand(0.994, 0.998);
}
speed = speed>0.2 ? speed*=slowDownRand : 0;
}
// Stopped!
if(lock && !speed){
var ai = Math.floor(((360 - deg - 90) % 360) / sliceDeg); // deg 2 Array Index
console.log(slices)
ai = (slices+ai)%slices; // Fix negative index
return alert("You got:\n"+ label[ai] ); // Get Array Item from end Degree
// ctx.arc(150,150,150,8.302780584487312,9.200378485512967);
// ctx.fill();
}
drawImg();
window.requestAnimationFrame(anim);
}
function start() {
anim()
}
drawImg();
Spin wheel codepen
Ease curves
If you where to plot the wheel position over time as it slows to a stop you would see a curve, a curve that looks like half a parabola.
You can get the very same curve if you plot the value of x squared in the range 0 to 1 as in the next snippet, the red line shows the plot of f(x) => x * x where 0 <= x <= 1
Unfortunately the plot is the wrong way round and needs to be mirrored in x and y. That is simple by changing the function to f(x) => 1 - (1 - x) ** 2 (Click the canvas to get the yellow line)
const size = 200;
const ctx = Object.assign(document.createElement("canvas"),{width: size, height: size / 2}).getContext("2d");
document.body.appendChild(ctx.canvas);
ctx.canvas.style.border = "2px solid black";
plot(getData());
plot(unitCurve(x => x * x), "#F00");
ctx.canvas.addEventListener("click",()=>plot(unitCurve(x => 1 - (1 - x) ** 2), "#FF0"), {once: true});
function getData(chart = []) {
var pos = 0, speed = 9, deceleration = 0.1;
while(speed > 0) {
chart.push(pos);
pos += speed;
speed -= deceleration;
}
return chart;
}
function unitCurve(f,chart = []) {
const step = 1 / 100;
var x = 0;
while(x <= 1) {
chart.push(f(x));
x += step
}
return chart;
}
function plot(chart, col = "#000") {
const xScale = size / chart.length, yScale = size / 2 / Math.max(...chart);
ctx.setTransform(xScale, 0, 0, yScale, 0, 0);
ctx.strokeStyle = col;
ctx.beginPath();
chart.forEach((y,x) => ctx.lineTo(x,y));
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.stroke();
}
In animation this curve is an ease in.
We can create function that uses the ease function, takes the time and returns the position of the wheel. We can provide some additional values that controls how long the wheel will take to stop, the starting position and the all important stop position.
function wheelPos(currentTime, startTime, endTime, startPos, endPos) {
// first scale the current time to a value from 0 to 1
const x = (currentTime - startTime) / (endTime - startTime);
// rather than the square, we will use the square root (this flips the curve)
const xx = x ** (1 / 2);
// convert the value to a wheel position
return xx * (endPos - startPos) + startPos;
}
Demo
The demo puts it in action. Rather than using the square root the function in the demo defines the root as the constant slowDownRate = 2.6. The smaller this value the greater start speed and the slower the end speed. A value of 1 means it will move at a constant speed and then stop. The value must be > 0 and < 1
requestAnimationFrame(mainLoop);
Math.TAU = Math.PI * 2;
const size = 160;
const ctx = Object.assign(document.createElement("canvas"),{width: size, height: size}).getContext("2d");
document.body.appendChild(ctx.canvas);
const stopAt = document.createElement("div")
document.body.appendChild(stopAt);
ctx.canvas.style.border = "2px solid black";
var gTime; // global time
const colors = ["#F00","#F80","#FF0","#0C0","#08F","#00F","#F0F"];
const wheelSteps = 12;
const minSpins = 3 * Math.TAU; // min number of spins before stopping
const spinTime = 6000; // in ms
const slowDownRate = 1 / 1.8; // smaller this value the greater the ease in.
// Must be > 0
var startSpin = false;
var readyTime = 0;
ctx.canvas.addEventListener("click",() => { startSpin = !wheel.spinning });
stopAt.textContent = "Click wheel to spin";
const wheel = { // hold wheel related variables
img: createWheel(wheelSteps),
endTime: performance.now() - 2000,
startPos: 0,
endPos: 0,
speed: 0,
pos: 0,
spinning: false,
set currentPos(val) {
this.speed = (val - this.pos) / 2; // for the wobble at stop
this.pos = val;
},
set endAt(pos) {
this.endPos = (Math.TAU - (pos / wheelSteps) * Math.TAU) + minSpins;
this.endTime = gTime + spinTime;
this.startTime = gTime;
stopAt.textContent = "Spin to: "+(pos + 1);
}
};
function wheelPos(currentTime, startTime, endTime, startPos, endPos) {
const x = ((currentTime - startTime) / (endTime - startTime)) ** slowDownRate;
return x * (endPos - startPos) + startPos;
}
function mainLoop(time) {
gTime = time;
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0, 0, size, size);
if (startSpin && !wheel.spinning) {
startSpin = false;
wheel.spinning = true;
wheel.startPos = (wheel.pos % Math.TAU + Math.TAU) % Math.TAU;
wheel.endAt = Math.random() * wheelSteps | 0;
} else if (gTime <= wheel.endTime) { // wheel is spinning get pos
wheel.currentPos = wheelPos(gTime, wheel.startTime, wheel.endTime, wheel.startPos, wheel.endPos);
readyTime = gTime + 1500;
} else { // wobble at stop
wheel.speed += (wheel.endPos - wheel.pos) * 0.0125;
wheel.speed *= 0.95;
wheel.pos += wheel.speed;
if (wheel.spinning && gTime > readyTime) {
wheel.spinning = false;
stopAt.textContent = "Click wheel to spin";
}
}
// draw wheel
ctx.setTransform(1,0,0,1,size / 2, size / 2);
ctx.rotate(wheel.pos);
ctx.drawImage(wheel.img, -size / 2 , - size / 2);
// draw marker shadow
ctx.setTransform(1,0,0,1,1,4);
ctx.fillStyle = "#0004";
ctx.beginPath();
ctx.lineTo(size - 13, size / 2);
ctx.lineTo(size, size / 2 - 7);
ctx.lineTo(size, size / 2 + 7);
ctx.fill();
// draw marker
ctx.setTransform(1,0,0,1,0,0);
ctx.fillStyle = "#F00";
ctx.beginPath();
ctx.lineTo(size - 13, size / 2);
ctx.lineTo(size, size / 2 - 7);
ctx.lineTo(size, size / 2 + 7);
ctx.fill();
requestAnimationFrame(mainLoop);
}
function createWheel(steps) {
const ctx = Object.assign(document.createElement("canvas"),{width: size, height: size}).getContext("2d");
const s = size, s2 = s / 2, r = s2 - 4;
var colIdx = 0;
for (let a = 0; a < Math.TAU; a += Math.TAU / steps) {
const aa = a - Math.PI / steps;
ctx.fillStyle = colors[colIdx++ % colors.length];
ctx.beginPath();
ctx.moveTo(s2, s2);
ctx.arc(s2, s2, r, aa, aa + Math.TAU / steps);
ctx.fill();
}
ctx.fillStyle = "#FFF";
ctx.beginPath();
ctx.arc(s2, s2, 12, 0, Math.TAU);
ctx.fill();
ctx.beginPath();
ctx.lineWidth = 2;
ctx.arc(s2, s2, r, 0, Math.TAU);
ctx.moveTo(s2 + 12, s2);
ctx.arc(s2, s2, 12, 0, Math.TAU);
for (let a = 0; a < Math.TAU; a += Math.TAU / steps) {
const aa = a - Math.PI / steps;
ctx.moveTo(Math.cos(aa) * 12 + s2, Math.sin(aa) * 12 + s2);
ctx.lineTo(Math.cos(aa) * r + s2, Math.sin(aa) * r + s2);
}
//ctx.fill("evenodd");
ctx.stroke();
ctx.fillStyle = "#000";
ctx.font = "13px arial black";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
const tr = r - 8;
var idx = 1;
for (let a = 0; a < Math.TAU; a += Math.TAU / steps) {
const dx = Math.cos(a);
const dy = Math.sin(a);
ctx.setTransform(dy, -dx, dx, dy, dx * (tr - 4) + s2, dy * (tr - 4) + s2);
ctx.fillText(""+ (idx ++), 0, 0);
}
return ctx.canvas;
}
body { font-family: arial }

pie chart in html and js

hello all i used canvas to create pie chart by surfing through stackoverflow and i found the below code but i need only for 2 values instead of 5 because my pie chart doesnt appear fully .below chv2 and chs2 are js values
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var lastend = 0;
var data = [chv2, chs2]; // If you add more data values make sure you add more colors
var myTotal = 0; // Automatically calculated so don't touch
var myColor = ['red', 'green']; // Colors of each slice
for (var e = 0; e < data.length; e++) {
myTotal += data[e];
}
alert(myTotal);
for (var i = 0; i < data.length; i++) {
ctx.fillStyle = myColor[i];
ctx.beginPath();
ctx.moveTo(canvas.width / 2, canvas.height / 2);
// Arc Parameters: x, y, radius, startingAngle (radians), endingAngle (radians), antiClockwise (boolean)
ctx.arc(canvas.width / 2, canvas.height / 2, canvas.height / 2, lastend, lastend + (Math.PI * 2 * (data[i] / myTotal)), false);
ctx.lineTo(canvas.width / 2, canvas.height / 2);
ctx.fill();
lastend += Math.PI * 2 * (data[i] / myTotal);
}
<canvas id="canvas" width="400" height="300">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
I think you missed the chv2 and chs2 varible declaration.
Please check this code
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var lastend = 0;
var chv2= 20;
var chs2= 40;
var data = [chv2,chs2]; // If you add more data values make sure you add more colors
var myTotal = 0; // Automatically calculated so don't touch
var myColor = ['red','green']; // Colors of each slice
for (var e = 0; e < data.length; e++) {
myTotal += data[e];
}
alert(myTotal);
for (var i = 0; i < data.length; i++) {
ctx.fillStyle = myColor[i];
ctx.beginPath();
ctx.moveTo(canvas.width / 2, canvas.height / 2);
// Arc Parameters: x, y, radius, startingAngle (radians), endingAngle (radians), antiClockwise (boolean)
ctx.arc(canvas.width / 2, canvas.height / 2, canvas.height / 2, lastend, lastend + (Math.PI * 2 * (data[i] / myTotal)), false);
ctx.lineTo(canvas.width / 2, canvas.height / 2);
ctx.fill();
lastend += Math.PI * 2 * (data[i] / myTotal);
}
Fiddle
Hope this will help you.

If/Then Javascript to Display HTML Code

I have some html5 and javascript code to create a spinning roulette wheel. It is split up into 14 outcomes. My question is, how can I get some unique HTML code to display per each outcome of the wheel. For example, if the wheel lands on Business, some text will appear below the wheel that has information on Business.
Here is my HTML code and below it is a link to a working example:
<!--[if IE]><script type="text/javascript" src="/sites/default/files/1010/source/excanvas.js"></script><![endif]-->
<input type="button" value="spin" onclick="spin();" style="float: left;" />
<canvas id="wheelcanvas" width="800" height="750"></canvas>
<script type="application/javascript">
var colors = ["#B8D430", "#3AB745", "#029990", "#3501CB",
"#2E2C75", "#673A7E", "#CC0071", "#F80120",
"#F35B20", "#FB9A00", "#FFCC00", "#FEF200", "#B2DF00", "#5C8300"];
var classes = ["Business", "Office Education", "Continuing Care Assistant", "Practical Nursing",
"Primary Care Paramedic", "Early Childhood Education", "Cooking", "Electrician",
"Heavy Equipment Operator", "Industrial Mechanic - Millwright", "Plumbing & Pipefitting", "Truck Driver Training", "Welding", "Power Engineering"];
var startAngle = 0;
var arc = Math.PI / 7;
var spinTimeout = null;
var spinArcStart = 10;
var spinTime = 0;
var spinTimeTotal = 0;
var ctx;
function draw() {
drawRouletteWheel();
}
function drawRouletteWheel() {
var canvas = document.getElementById("wheelcanvas");
if (canvas.getContext) {
var outsideRadius = 300;
var textRadius = 260;
var insideRadius = 100;
ctx = canvas.getContext("2d");
ctx.clearRect(5,0,1000,1000);
ctx.strokeStyle = "black";
ctx.lineWidth = 2;
ctx.font = 'bold 10px sans-serif';
for(var i = 0; i < 14; i++) {
var angle = startAngle + i * arc;
ctx.fillStyle = colors[i];
ctx.beginPath();
ctx.arc(400, 400, outsideRadius, angle, angle + arc, false);
ctx.arc(400, 400, insideRadius, angle + arc, angle, true);
ctx.stroke();
ctx.fill();
ctx.save();
ctx.shadowOffsetX = -1;
ctx.shadowOffsetY = -1;
ctx.shadowBlur = 0;
ctx.shadowColor = "rgb(220,220,220)";
ctx.fillStyle = "black";
ctx.translate(400 + Math.cos(angle + arc / 2) * textRadius, 400 + Math.sin(angle + arc / 2) * textRadius);
ctx.rotate(angle + arc / 2 + Math.PI / 2);
var text = classes[i];
ctx.fillText(text, -ctx.measureText(text).width / 2, 0);
ctx.restore();
}
//Arrow
ctx.fillStyle = "black";
ctx.beginPath();
ctx.moveTo(400 - 4, 400 - (outsideRadius + 5));
ctx.lineTo(400 + 4, 400 - (outsideRadius + 5));
ctx.lineTo(400 + 4, 400 - (outsideRadius - 5));
ctx.lineTo(400 + 9, 400 - (outsideRadius - 5));
ctx.lineTo(400 + 0, 400 - (outsideRadius - 13));
ctx.lineTo(400 - 9, 400 - (outsideRadius - 5));
ctx.lineTo(400 - 4, 400 - (outsideRadius - 5));
ctx.lineTo(400 - 4, 400 - (outsideRadius + 5));
ctx.fill();
}
}
function spin() {
spinAngleStart = Math.random() * 10 + 10;
spinTime = 0;
spinTimeTotal = Math.random() * 3 + 4 * 1000;
rotateWheel();
}
function rotateWheel() {
spinTime += 30;
if(spinTime >= spinTimeTotal) {
stopRotateWheel();
return;
}
var spinAngle = spinAngleStart - easeOut(spinTime, 0, spinAngleStart, spinTimeTotal);
startAngle += (spinAngle * Math.PI / 180);
drawRouletteWheel();
spinTimeout = setTimeout('rotateWheel()', 30);
}
function stopRotateWheel() {
clearTimeout(spinTimeout);
var degrees = startAngle * 180 / Math.PI + 90;
var arcd = arc * 180 / Math.PI;
var index = Math.floor((360 - degrees % 360) / arcd);
ctx.save();
ctx.font = 'bold 30px sans-serif';
var text = classes[index]
ctx.fillText(text, 400 - ctx.measureText(text).width / 2, 400 + 10);
ctx.restore();
document.getElementById('wheelResult').innerHTML = 'Test' + text;
}
function easeOut(t, b, c, d) {
var ts = (t/=d)*t;
var tc = ts*t;
return b+c*(tc + -3*ts + 3*t);
}
draw();
</script>
<p id="wheelResult"></p>
http://www.ctrc.sk.ca/wheel.html
There are a couple of ways to do it but this is the way I would do it.
You could create a div tag with the css style of display:none and then set it with the info desired and hide and show it as needed.
Example:
<script>
var htmlData = ['<h1>Business</h1><br><span>What is Business?</span><br>...', ...];
....
function spin()
{
//Hide description box.
document.getElementById('idDescriptionBox').style.display = 'none';
spinAngleStart = Math.random() * 10 + 10;
....
}
....
function stopRotateWheel()
{
....
//Set description and display it.
var descriptionBox = document.getElementById('idDescriptionBox');
descriptionBox.innerHTML = htmlData[index];
descriptionBox.style.display = 'block';
}
....
</script>
<div id="idDescriptionBox" style="display:none;">
<!--Description info in here-->
....
</div>

How would I make this line work?

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)
}

Categories

Resources