Draw gauge chart by Canvas - html5-canvas

I can draw gauge chart by canvas with function arc(). But, the shape is a half of circle.
Now, I'd like to draw gaugle chart like this (please ignore color or number).
How can I draw it? Thanks
UPDATE:
I draw the chart by drawing 2 arc (I don't use ctx.lineWidth)
var min=Math.PI*.60;
var max=Math.PI*2+Math.PI*.40;
var R = 100;
var arcWidth = 40;
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.arc(100, 100, R-arcWidth, max, min, true);
ctx.arc(100, 100, R, min, max);
ctx.fillStyle = "red";
ctx.fill();
http://jsfiddle.net/b0nw4gma/
However, at position of min and max, chart is line instead of round. I tried to use ctx.lineCap='round', but it not work.

Just stroke an arc with a sweep-angle based on your desired gauge percentage.
In your example the 0% angle is (estimating...)
PI * 0.60
and your 100% angle is (again estimating...)
PI*2 + PI*.40
So your arc will always start at angle = PI*0.60.
Your arc will end at the angle calculated like this:
zeroAngle + (hundredAngle-zeroAngle) * guagePercentageValue
Here's example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var PI=Math.PI;
var PI2=PI*2;
var cx=150;
var cy=150;
var r=80;
var min=PI*.60;
var max=PI2+PI*.40;
var percent=50;
ctx.lineCap='round';
ctx.font='24px verdana';
ctx.textAlign='center';
ctx.textBaseline='middle';
ctx.fillStyle='gray';
$myslider=$('#myslider');
$myslider.attr({min:0,max:100}).val(50);
$myslider.on('input change',function(){
percent=parseInt($(this).val());
drawGuage();
});
drawGuage();
function drawGuage(){
ctx.clearRect(0,0,cw,ch);
// draw full guage outline
ctx.beginPath();
ctx.arc(cx,cy,r,min,max);
ctx.strokeStyle='lightgray';
ctx.lineWidth=15;
ctx.stroke();
// draw percent indicator
ctx.beginPath();
ctx.arc(cx,cy,r,min,min+(max-min)*percent/100);
ctx.strokeStyle='red';
ctx.lineWidth=6;
ctx.stroke();
ctx.fillText(percent+'%',cx,cy);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<input id=myslider type=range><br>
<canvas id="canvas" width=300 height=300></canvas>

Related

why doesn't d3-geo-zoom pan correctly for more then 1 canvas elements on the page?

When panning in the first canvas, things work like expected. When panning in the second canvas, it doesn't work like expected. I expected both to work the same. The second globe spins rapidly after a little bit of panning, the first globe keeps the cursor on the same coordinates.
https://codepen.io/tonytrupe/pen/jOqjGvE
class UI {
constructor(canvas) {
var width = canvas.width,
height = canvas.height;
//set projection type here, geoOrthographic, geoWinkel3
var projection = d3
.geoWinkel3()
//.scale((Math.min(width, height)) / 2)
.translate([width / 2, height / 2])
//.rotate([0,0,0])
.fitExtent(
[
[6, 6],
[width - 6, height - 6]
],
{
type: "Sphere"
}
);
draw();
//this.addZoomPan = function () {
d3
.geoZoom()
.northUp(true)
.projection(projection)
.onMove(draw)(canvas);
//};
function draw() {
var ctx = canvas.getContext("2d");
var path = d3.geoPath().context(ctx).projection(projection);
// Store the current transformation matrix
ctx.save();
// Use the identity matrix while clearing the canvas
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Restore the transform
ctx.restore();
var border = {
type: "Sphere"
};
ctx.beginPath();
path(border);
ctx.strokeStyle = "#000";
ctx.stroke();
var lat = 45;
var lon = 45;
var graticule = d3.geoGraticule().step([lat, lon]);
ctx.beginPath();
path(graticule());
ctx.strokeStyle = "#000";
ctx.stroke();
}
}
}
//var one = new UI(document.getElementById("one"));
//var two = new UI(document.getElementById("two"));
var three = new UI(document.getElementById("three"));
html
<html>
<script src="//d3js.org/d3.v6.js"></script>
<script src="//d3js.org/d3-geo.v2.min.js"></script>
<script src="//d3js.org/d3-geo-projection.v3.min.js"></script>
<script src="//unpkg.com/d3-geo-zoom"></script>
<!--removing all but the last canvas element makes things work as expected-->
<canvas id="one" class="canvas" width="320" height="200"></canvas>
<canvas id="two" class="canvas" width="320" height="200"></canvas>
<canvas id="three" class="canvas" width="320" height="200"></canvas>
</html>
https://github.com/vasturiano/d3-geo-zoom/issues/12
It previously wasn't getting pointer location relative to the node element. Now it is.
const pointers = d3Pointers(zoomEv, nodeEl);
https://github.com/vasturiano/d3-geo-zoom/blob/86da0d98f267a838a4715abec60e4a278ace2121/src/geoZoom.js#L59

Three js - the best way to draw text and line

after gone through three.js docs, I found it is quite hard to draw line with stroke width, and text.
For the line with stroke width I have to draw an rectange and adjust it to look likes a thick line
For the text, there only two option, one is using font loader, which I found it very hard to handle the promise of font loader, and it become slower if I have so many text.
But if I use canvas to draw the text then use sprite to add that canvas, then I got my texts keep rotates whenever I rotate my camera
so it is 2018, is there any way to make life more easier with three js.
Thanks all
my code to create a thick curve line
function curveLine(start, mid, end, width) {
var curveLine = new THREE.Shape();
curveLine.moveTo(start.x, start.y + width);
curveLine.quadraticCurveTo(mid.x, mid.y + width, end.x, end.y + width);
curveLine.lineTo(end.x, end.y);
curveLine.quadraticCurveTo(mid.x, mid.y, start.x, start.y);
curveLine.lineTo(start.x, start.y);
return curveLine;
}
var line = curveLine({x:0,y:0}, {x:120,y:80}, {x:240,y:0}, 20);
var lineGeo = new THREE.ExtrudeGeometry(line, extrudeSettings);
var lineMesh = new THREE.Mesh(lineGeo, new THREE.MeshBasicMaterial({color: 'red'}));
scene.add(lineMesh);
And this my code to draw text, current using canvas to draws
function text2D(text, params) {
var canvas = document.createElement('canvas');
canvas.width = params.radius * 4;
canvas.height = params.radius * 2;
var context = canvas.getContext('2d');
context.font = '20px Verdana';
var metrics = context.measureText(text);
var textWidth = metrics.width;
context.fillStyle = params.color;
context.fillText(text, (canvas.width - textWidth) / 2, params.radius);
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var spriteMaterial = new THREE.SpriteMaterial({map: texture});
var sprite = new THREE.Sprite(spriteMaterial);
sprite.scale.set(canvas.width, canvas.height, 1);
return sprite;
}
and then whenever i rotate the camera, the text keep face the camera. is there any way to prevent that
Cheers all

crop an image multipile times using drawImage() canvas?

I have this code and successfully croped the left corner , but I need to crop all 4 corners of this image ,can it be done with the same object?
I have this code and successfully croped the left corner , but I need to crop all 4 corners of this image ,can it be done with the same object?
the croped image
//Global variables
var myImage = new Image(); // Create a new blank image.
// Load the image and display it.
function displayImage() {
// Get the canvas element.
canvas = document.getElementById("myCanvas");
// Make sure you got it.
if (canvas.getContext) {
// Specify 2d canvas type.
ctx = canvas.getContext("2d");
// When the image is loaded, draw it.
myImage.onload = function() {
// Load the image into the context.
ctx.drawImage(myImage, 0, 0);
// Get and modify the image data.
changeImage();
}
// Define the source of the image.
myImage.src = "ice.jpg";
}
}
function changeImage() {
ctx.strokeStyle = "white";
ctx.lineWidth = "70";
ctx.beginPath();
ctx.arc(0,0,10,0*Math.PI,0.5*Math.PI);
ctx.closePath();
ctx.stroke();
}
</script>
</head>
<body onload="displayImage()">
<canvas id="myCanvas" width="200" height="200">
</canvas>
</body>
</html>
Kind of what #AndreaBogazzi says, but kind of not...
Yes, you can draw all 4 of the corner cutouts using a single path.
This is done by drawing a cutout circle and then picking up the "brush" and moving it to the center of the next cutout.
You must pick up the brush or otherwise you will get a line drawn that connects the centerpoints of each cutout circle.
Begin a single path: ctx.beginPath()
Draw an arc: ctx.arc(0,0,cutRadius,0,Math.PI*2)
Pick up the "brush" and move it to the center of the next arc: ctx.moveTo(w,0)
Draw an arc: ctx.arc(w,0,cutRadius,0,Math.PI*2)
Pick up the "brush" and move it to the center of the next arc: ctx.moveTo(w,h)
Draw an arc: ctx.arc(w,h,cutRadius,0,Math.PI*2)
Pick up the "brush" and move it to the center of the next arc: ctx.moveTo(0,h)
Draw an arc: ctx.arc(0,h,cutRadius,0,Math.PI*2)
Fill all 4 cutout circles: ctx.fill()
Note: since the "brush" begins at [0,0] you don't have to moveTo(0,0) after step#1.
Example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var cutRadius=10;
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/reef.jpg";
function start(){
cw=canvas.width=img.width;
ch=canvas.height=img.height
ctx.drawImage(img,0,0);
changeImage();
}
function changeImage() {
ctx.fillStyle = "white";
ctx.beginPath();
ctx.arc(0,0,cutRadius,0,Math.PI*2);
ctx.moveTo(cw,0);
ctx.arc(cw,0,cutRadius,0,Math.PI*2);
ctx.moveTo(cw,ch);
ctx.arc(cw,ch,cutRadius,0,Math.PI*2);
ctx.moveTo(0,ch);
ctx.arc(0,ch,cutRadius,0,Math.PI*2);
ctx.fill();
}
<canvas id="canvas" width=300 height=300></canvas>
You have to manually draw the four quarter circle:
//Global variables
var myImage = new Image(); // Create a new blank image.
// Load the image and display it.
function displayImage() {
// Get the canvas element.
canvas = document.getElementById("myCanvas");
// Make sure you got it.
if (canvas.getContext) {
// Specify 2d canvas type.
ctx = canvas.getContext("2d");
// When the image is loaded, draw it.
myImage.onload = function() {
canvas.width = myImage.width;
canvas.height = myImage.height;
// Load the image into the context.
ctx.drawImage(myImage, 0, 0);
// Get and modify the image data.
changeImage(myImage.width, myImage.height);
}
// Define the source of the image.
myImage.src = "http://www.keenthemes.com/preview/metronic/theme/assets/global/plugins/jcrop/demos/demo_files/image1.jpg";
}
}
function changeImage(w,h) {
var r = 70;
ctx.fillStyle = "white";
ctx.beginPath();
ctx.moveTo(0,0);
ctx.arc(0,0,r,0*Math.PI,0.5*Math.PI);
ctx.moveTo(0,r);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.arc(w,0,r, -0.5*Math.PI,-1*Math.PI);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.moveTo(0,h);
ctx.arc(0,h,r, -0.5*Math.PI,0*Math.PI);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.moveTo(w,h);
ctx.arc(w,h,r, -1*Math.PI,-0.5*Math.PI);
ctx.closePath();
ctx.fill();
}
displayImage();
<canvas id="myCanvas" width="200" height="200">
</canvas>

Circular Slider using Jquery

How to slide mouse in circular ?
Draw an arc and a mouse pointer in a canvas. Mouse should be drag gable on the circular path?
//function to create mouse event to drag the mouse hover the arc
function mousedrag() {
var canvasoffset = $(this.canvas).offset();
var offsetX = canvasoffset.left;
var offsetY = canvasoffset.top;
var mouseX = parseInt(e.offsetX || e.clientX - offsetX);
var mouseY = parseInt(e.offsetY || e.clientY - offsetY);
var radius = this.width / 2;
var twoPI = 2 * Math.PI;
var toRad = twoPI / 360;
var r_width = this.width * 0.8;
var radial_Angle = Math.atan2(mouseY - radius,mouseX - radius);
var p_side_x = radius + r_width * Math.cos(radial_Angle);
var p_side_y = radius + r_width * Math.sin(radial_Angle);
var p_mouse_x = radius + ((r_width+10) * Math.sin(radial_Angle));
var p_mouse_y = radius + ((r_width+ 10) * Math.sin(radial_Angle));
var imgData = this.ctx.getImageData(p_side_x, p_side_y, 1, 1).data;
var selectedColor = new Color(imgData[0], imgData[1], imgData[2]);
clearDraw();
renderSpectrum();
renderMouse(p_side_x, p_side_y, p_mouse_x, p_mouse_y);
}
mouse handle does not slide properly.
You can't actually force the mouse to be constrained into a circle.
But you can calculate the mouse position relative to a centerpoint.
// define a centerpoint
var cx=150;
var cy=150;
var angleVersusCenter=0;
// listen for mouse moves
function handleMouseMove(e){
// get the mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// set the current radian angle of the mouse
// versus the centerpoint
var angleVersusCenter = Math.atan2( mouseY-cy, mouseX-cx );
}
A Demo: http://jsfiddle.net/m1erickson/z6cQB/
Here is the jQuery roundSlider plugin for your requirement, check here http://roundsliderui.com/. This might helps you.
This roundslider having the similar options like the jQuery ui slider. It support default, min-range and range slider type. Not only the round slider it also supports various circle shapes such as quarter, half and pie circle shapes.
For more details check the demos and documentation page.
Please check the demo from jsFiddle.
Live demo:
$("#slider").roundSlider({
width: 10,
handleSize: "+8",
value: "40"
});
$("#half-slider").roundSlider({
width: 10,
circleShape: "half-top",
handleSize: "+8",
value: "80"
});
.rs-control {
display: inline-block;
float: left;
margin-left: 30px;
}
.rs-control .rs-path-color {
background-color: #e9e9e9;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/jquery.roundslider/1.0/roundslider.min.js"></script>
<link href="https://cdn.jsdelivr.net/jquery.roundslider/1.0/roundslider.min.css" rel="stylesheet"/>
<div id="slider" class="rslider"></div>
<div id="half-slider" class="rslider"></div>
Screenshots with different appearances:
Check more details about different theme from here.

Kinetic JS - Calculate the degree of rotation of a circle

I am trying to calculate the angle of rotation of a circle, I am using the following script:
var circle = new Kinetic.Circle({
x: 256,
y: 256,
radius: 140,
stroke: 'black',
strokeWidth: 4 ,
offset: [0, 0],
draggable: true,
dragBoundFunc: function (pos) {
var pos = stage.getMousePosition();
var xd = 140 - pos.x;
var yd = 140 - pos.y;
var theta = Math.atan2(yd, xd);
var degree = (theta / (Math.PI / 180) - 45);
this.setRotationDeg(degree);
return {
x: this.getAbsolutePosition().x,
y: this.getAbsolutePosition().y
};
}
});
I don't think it is accurate, I added a shape inside the circle to see the rotation but could not group them together, I would appreciate your suggestions on how to calculate the degree of rotation and how to group the shape with the circle so the rotate at the same time. The complete project script is at http://jsfiddle.net/user373721/Ja6GB. Thanks in advance.
Here is how you calculate the angle of the mouse position from "12 o'clock"
Pretend your canvas is a clock centered in the canvas.
Here's how to calculate the angle of the current mouse position assuming 12 o'clock is zero degrees.
function degreesFromTwelveOclock(cx,cy,mouseX,mouseY){
// calculate the angle(theta)
var theta=Math.atan2(mouseY-centerY,mouseX-centerX);
// be sure theta is positive
if(theta<0){theta += 2*Math.PI};
// convert to degrees and rotate so 0 degrees = 12 o'clock
var degrees=(theta*180/Math.PI+90)%360;
return(degrees);
}
Here is complete code and a Fiddle: http://jsfiddle.net/m1erickson/HKq77/
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var canvasOffset=$("#canvas").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var centerX=canvas.width/2;
var centerY=canvas.height/2;
var radius=10;
// draw a center dot for user's reference
ctx.beginPath();
ctx.arc(centerX,centerY, radius, 0 , 2 * Math.PI, false);
ctx.fill();
function handleMouseMove(e){
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
$("#movelog").html("Mouse: "+ mouseX + " / " + mouseY);
$("#angle").html("Angle: "+parseInt(degreesFromTwelveOclock(centerX,centerY,mouseX,mouseY)));
}
function degreesFromTwelveOclock(cx,cy,mouseX,mouseY){
// calculate the angle(theta)
var theta=Math.atan2(mouseY-centerY,mouseX-centerX);
// be sure theta is positive
if(theta<0){theta += 2*Math.PI};
// convert to degrees and rotate so 0 degrees = 12 o'clock
var degrees=(theta*180/Math.PI+90)%360;
return(degrees);
}
$("#canvas").mousemove(function(e){handleMouseMove(e);});
}); // end $(function(){});
</script>
</head>
<body>
<p id="movelog">Move</p>
<p id="angle">Out</p>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>

Resources