Draw path - canvas - javascript

i want to draw a simple path in canvas like this:
i have allready tried (also im not sure how to create the point with a radius at 440, 400):
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(500, 0);
ctx.lineTo(440, 400);
ctx.lineTo(0, 500);
ctx.lineTo(0, 0);
ctx.fill();
but it shows me just a black 600x600 rectangle
https://jsfiddle.net/aaroniker/pmgkymch/
Thanks!

Canvas elements contain rasterized pixel data for an image of the same dimensions as those of the canvas element's width and height attributes, which default to 300 and 150 respectively. Drawing to a canvas element uses pixel coordinates of the canvas unless a context drawing transform is in effect.
Setting width and height of a canvas element in CSS scales the canvas to the dimensions set in CSS when presenting it on screen. As with scaling an ordinary image element, canvas image quality can drop noticeably if a small canvas is scaled to too large a size.
Answer A
You are seeing a black square because you drew onto a 300 x 150 pixel canvas using 600 x 600 coordinates. Filling the oversized path drawn filled in all the actual canvas pixels. The 300 x 150 pixel canvas was presented as a 600 x 600 screen area due to CSS scaling.
Answer B
The context's path drawing arcto method is used to create a rounded corner but you don't need to work out where it leaves the drawing position provided you continue by drawing a line to a known point.
In this example I've set the canvas element dimensions in HTML to 600 x 600, and scaled it to a different size (150px x 150px) in CSS
function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
var radius = 100; // a number
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(500, 0);
ctx.arcTo( 440, 400, 0, 500, radius)
ctx.lineTo( 0, 500); // join end of arc to next point
ctx.lineTo(0, 0);
ctx.fill();
}
}
draw();
#canvas {
width: 150px;
height: 150px;
}
<canvas id="canvas" width="600", height="600"></canvas>

As I stated in my comment, the coordinate system gets deformed when you define canvas dimensions in CSS. Use either inline styling (as I've done) or code it into your JS. For the arc you need, use ctx.arcTo(x1, x2, y1, y2, r), where x1, y1 is the point you are arcing around (440, 400 in your case) and x2,y2 is where you want the arc to meet back up with your figure, r is the radius.
https://www.w3schools.com/tags/canvas_arcto.asp
function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(500, 0);
ctx.lineTo(441.2, 392);
ctx.arcTo(440, 400, 431.2, 402, 8);
ctx.lineTo(0, 500);
ctx.lineTo(0, 0);
ctx.fillStyle = "#008AFF";
ctx.fill();
}
}
draw();
<canvas height="600" id="canvas" width="600"></canvas>

Related

Html canvas zoom to specific region

I have Html canvas code that draws the following graph. Is there an easy way of zooming the area that is outlined with red rectangle without rewriting the code? This area is always top right quarter of the graph and I also would like to leave some space for the axis.
So, I found the solution:
Draw two times bigger original canvas
Retrieve and put the region image on new canvas:
var imgData = ctx.getImageData(canvas.width/2 - 20, 0, canvas.width/2 + 20, canvas.width/2 + 20);
ctx.clearRect(0, 0, canvas.width, canvas.height);
canvas.width /= 2
canvas.height /= 2
canvas.height += 20
ctx.putImageData(imgData, 0, 0);

html5 canvas redraw on resize

I have two canvas elements and need them to be resized on buttons click.
<div class="sDetails"><div>
<div id="canvasDiv" style="width: 310px;"><canvas id="canvasGraph"></canvas></div></div>
<div class="kDetails"><div><div>
<div id="canvasDiv" style="width: 310px; height: 240px;"><canvas id="canvasGraph"></canvas></div></div>
and the script:
var sketch;var sketch_sl;var onPaint;var canvas=null;var ctx=null;var tmp_ctx=null;
function drawCanvas(div) {
canvas = document.querySelector(div + " #canvasGraph");
ctx = canvas.getContext('2d');
sketch = document.querySelector(div + " #canvasDiv");
sketch_sl = getComputedStyle(sketch);
canvas.width = parseInt(sketch_style.getPropertyValue('width'));
canvas.height = parseInt(sketch_style.getPropertyValue('height'));
tmp_canvas = document.createElement('canvas');
tmp_ctx = tmp_canvas.getContext('2d');
tmp_canvas.id = 'tmp_canvas';
tmp_canvas.width = canvas.width;
tmp_canvas.height = canvas.height;
sketch.appendChild(tmp_canvas);
the redraw function:
// here I must redraw my lines resized 2 times ( *cScale ) where cScale=2 or =1
function drawScales(ctx, canvas)
ctx.strokeStyle = 'green';
ctx.fillStyle = 'green';
ctx.beginPath();
ctx.moveTo(5, 0);
ctx.lineTo(0, canvas.height);
scaleStep = 24*cScale;
for some reason it works really bad, old positions stay.
Is there a way to completely delete the whole canvas and append it or redraw it completely?
I tried canvas.width=canvas.width, tried ctx.clearRect(0, 0, canvas.width, canvas.height);tmp_ctx.clearRect(0, 0, canvas.width, canvas.height);, tried $(".sDetails #canvasGraph")[0].reset();
logically, drawCanvas(".sDetails");drawLines(ctx, canvas); should redraw it from scratch but it will not.
Resize the canvas element's width & height and use context.scale to redraw the original drawings at their newly scaled size.
Resizing the canvas element will automatically clear all drawings off the canvas.
Resizing will also automatically reset all context properties back to their default values.
Using context.scale is useful because then the canvas will automatically rescale the original drawings to fit on the newly sized canvas.
Important: Canvas will not automatically redraw the original drawings...you must re-issue the original drawing commands.
Illustration with 2 canvases at same size (their sizes are controlled by range controls)
Illustration with left canvas resized larger
Illustration with right canvas resized larger
Here's example code and a Demo. This demo uses range elements to control the resizing, but you can also do the resizing+redrawing inside window.onresize
var canvas1=document.getElementById("canvas1");
var ctx1=canvas1.getContext("2d");
var canvas2=document.getElementById("canvas2");
var ctx2=canvas2.getContext("2d");
var originalWidth=canvas1.width;
var originalHeight=canvas1.height;
var scale1=1;
var scale2=1;
$myslider1=$('#myslider1');
$myslider1.attr({min:50,max:200}).val(100);
$myslider1.on('input change',function(){
var scale=parseInt($(this).val())/100;
scale1=scale;
redraw(ctx1,scale);
});
$myslider2=$('#myslider2');
$myslider2.attr({min:50,max:200}).val(100);
$myslider2.on('input change',function(){
var scale=parseInt($(this).val())/100;
scale2=scale;
redraw(ctx2,scale);
});
draw(ctx1);
draw(ctx2);
function redraw(ctx,scale){
// Resizing the canvas will clear all drawings off the canvas
// Resizing will also automatically clear the context
// of all its current values and set default context values
ctx.canvas.width=originalWidth*scale;
ctx.canvas.height=originalHeight*scale;
// context.scale will scale the original drawings to fit on
// the newly resized canvas
ctx.scale(scale,scale);
draw(ctx);
// always clean up! Reverse the scale
ctx.scale(-scale,-scale);
}
function draw(ctx){
// note: context.scale causes canvas to do all the rescaling
// math for us, so we can always just draw using the
// original sizes and x,y coordinates
ctx.beginPath();
ctx.moveTo(150,50);
ctx.lineTo(250,150);
ctx.lineTo(50,150);
ctx.closePath();
ctx.stroke();
ctx.fillStyle='skyblue';
ctx.beginPath();
ctx.arc(150,50,20,0,Math.PI*2);
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.arc(250,150,20,0,Math.PI*2);
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.beginPath();;
ctx.arc(50,150,20,0,Math.PI*2);
ctx.fill();
ctx.stroke();
}
$("#canvas1, #canvas2").mousemove(function(e){handleMouseMove(e);});
var $mouse=$('#mouse');
function handleMouseMove(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
var bb=e.target.getBoundingClientRect();
mouseX=parseInt(e.clientX-bb.left);
mouseY=parseInt(e.clientY-bb.top);
if(e.target.id=='canvas1'){
$mouse.text('Mouse1: '+mouseX/scale1+' / '+mouseY/scale1+' (scale:'+scale1+')');
}else{
$mouse.text('Mouse2: '+mouseX/scale2+' / '+mouseY/scale2+' (scale:'+scale2+')');
}
}
body{ background-color: ivory; }
canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div>Resize left canvas</div>
<input id=myslider1 type=range><br>
<div>Resize right canvas</div>
<input id=myslider2 type=range><br>
<h4 id=mouse>Mouse coordinates:</h4>
<canvas id="canvas1" width=300 height=300></canvas>
<canvas id="canvas2" width=300 height=300></canvas>
If you need scale-independent positions you could use normalized values ([0, 1]) instead and use the size of canvas as the scale factor. This way you can scale and store values without too much concern about the actual target size.
You would also be able to use the mouse positions almost as is and normalize by just dividing them on canvas size.
For example:
When rendering, a point of (1,1) will always draw in lower-right corner as you would do (1 * canvas.width, 1 * canvas.height).
When you store a point you would use the mouse position and divide it on the canvas dimension, for example, if I click in the lower right corner of a canvas of size 400x200, the points would be 400/400 = 1, 200/200 = 1.
Note that width and height would be exclusive (ie. width-1 etc.), but for sake of simplicity...
Example
In this example you can start with any size of the canvas, draw points which are normalized, change size of canvas and have the points redrawn proportionally relative to the original position.
var rng = document.querySelector("input"),
c = document.querySelector("canvas"),
ctx = c.getContext("2d"),
points = [];
// change canvas size and redraw all points
rng.onchange = function() {
c.width = +this.value;
render();
};
// add a new normalized point to array
c.onclick = function(e) {
var r = this.getBoundingClientRect(), // to adjust mouse position
x = e.clientX - r.left,
y = e.clientY - r.top;
points.push({
x: x / c.width, // normalize value to range [0, 1]
y: y / c.height
}); // store point
render(); // redraw (for demo)
};
function render() {
ctx.clearRect(0, 0, c.width, c.height); // clear canvas
ctx.beginPath(); // clear path
for(var i = 0, p; p = points[i]; i++) { // draw points as fixed-size circles
var x = p.x * c.width, // normalized to absolute values
y = p.y * c.height;
ctx.moveTo(x + 5, y);
ctx.arc(x, y, 5, 0, 6.28);
ctx.closePath();
}
ctx.stroke();
}
canvas {background:#ddd}
<h3>Click on canvas to add points, then resize</h3>
<label>Width: <input type="range" min=50 max=600 value=300></label><br>
<canvas></canvas>
I decided to use a scale variable to resize my scales. I resize the canvas canvas.width *= 2; and then I redraw my scales.
var scaleStep;
and use add it into the code: ctx.lineTo(12*24*cScale+12, canvas.height-24); where the scaling needs to be done.
The scaleStep is 2 when maximizing the canvas and 1 when returning to the original size.

Reveal image on canvas with draw/strokes/paint

I want to achive the following:
Draw a bg-image to the canvas (once or if needed repeatedly)
The image should not be visible at the beginning
While i "paint" shapes to the canvas the bg-image should get visible where the shapes were drawn
The parts of the image that will be revealed shall be "painted" (like with a brush) so i want to use strokes.
What i tried:
- Do not clear the canvas
- Paint rects to the canvas with globalCompositeOperation = 'destination-in'
This works, the rectangles reveal the image but i need strokes
If i use strokes they are ignored with 'destination-in' while i see them with normal globalCompositeOperation.
Is this intended that the strokes are ignored? Is there a workaround like somehow converting the stroke/shape to a bitmap? Or do i have have to use two canvas elements?
In OpenGL i would first draw the image with its rgb values and with a = 0 and then only "paint" the alpha in.
You can solve it by these steps:
Set the image as a pattern
Set the pattern as fillStyle or strokeStyle
When you now fill/stroke your shapes the image will be revealed. Just make sure the initial image fits the area you want to reveal.
Example showing the principle, you should be able to adopt this to your needs:
var ctx = canvas.getContext("2d"),
img = new Image,
radius = 40;
img.onload = setup;
img.src = "http://i.imgur.com/bnAEEXq.jpg";
function setup() {
// set image as pattern for fillStyle
ctx.fillStyle = ctx.createPattern(this, "no-repeat");
// for demo only, reveals image while mousing over canvas
canvas.onmousemove = function(e) {
var r = this.getBoundingClientRect(),
x = e.clientX - r.left,
y = e.clientY - r.top;
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.arc(x, y, radius, 0, 2*Math.PI);
ctx.fill();
};
}
<canvas id=canvas width=900 height=600></canvas>
Hope this helps!
Alternative solution:
Put the image as a normal image on your website
add a canvas and use CSS positioning to place it right above the image
Fill the canvas with the color you use as the page background
have your paint tools erase the canvas when you draw. By the way, you can set context.globalCompositionOperation = 'destination-out' to turn all drawing operations into an eraser.
Here is an example. As you can see, the alpha properties of your tools are respected.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
//prepare canvas
ctx.fillStyle = '#ffffff'
ctx.fillRect(0, 0, 120, 120);
//prepare a 30% opacity eraser
ctx.globalCompositeOperation = 'destination-out';
ctx.lineWidth = 5;
ctx.strokeStyle = 'rgba(0, 0, 0, 0.3)';
// make random strokes around cursor while mouse moves
canvas.onmousemove = function(e) {
var rect = this.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
ctx.beginPath();
ctx.moveTo(x + Math.random() * 33 - 16, y + Math.random() * 33 - 16);
ctx.lineTo(x + Math.random() * 33 - 16, y + Math.random() * 33 - 16);
ctx.stroke();
}
<span>Move your mouse:</span>
<div>
<img src='https://upload.wikimedia.org/wikipedia/commons/thumb/6/61/HTML5_logo_and_wordmark.svg/120px-HTML5_logo_and_wordmark.svg.png' style='position:absolute'>
<canvas id='canvas' width=120 height=120 style='position:absolute'></canvas>
</div>

HTML 5 Canvas and Draw Circle

I am trying to understand why my circle is not in the middle of my Canvas in HTML 5.
I am trying to create a circle in the middle the canvas.
The canvas is as follows:
Canvas:
Width: 600
Height: 300
Then I draw a circle with the following code:
context.beginPath();
context.fillStyle = '#424';
context.arc(300, 150, 10, 0, 2*Math.PI, false);
context.fill();
context.closePath();
The circle is drawn inthe lower right corner.
Now if I change the (x, y) to (150, 75) then it shows in the middle.
I am just hoping someone can shed a little light on why the original code doesn't work.
You are not showing how you are setting the actual width and height of the canvas, but if the center point always is 150, 75 then the canvas is always at default size which means you are probably setting the size using css instead of directly on the element.
Try something like this:
<canvas id="myCanvas" width=600 height=300></canvas>
in JavaScript:
canvas.width = 600; // don't use canvas.style.*
canvas.height = 300;
You could also set the center this way for the arc (to make it adopt center automatically):
context.arc(context.canvas.width * 0.5, context.canvas.height * 0.5,
10, 0, 2*Math.PI, false);

Set canvas as a "circle" - allow nothing to draw outside the circle

I've created a canvas element and set it's width and height.
Then I've set the border-radius on the ID of the canvas so that the canvas looks like a circle.
However, if I draw something outside the circle area, it'll still draw it, as shown on my example code :
http://jsfiddle.net/mN9Eh/
JavaScript :
<script>
function animate() {
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.save();
ctx.clearRect(0, 0, c.width, c.height);
if(i > 80) {
i = 1;
}
if( i > 40) {
ctx.beginPath();
ctx.arc(50, 50, i-40, 0, 2 * Math.PI, true);
ctx.fillStyle = "#FF0033";
ctx.fill();
}
i++;
ctx.restore();
setTimeout(animate, 10);
}
var i = 0;
animate();
</script>
CSS :
#myCanvas {
background: #333;
border-radius: 300px;
}
HTML :
<canvas id="myCanvas" width="300" height="300"></canvas>
I remember reading something that you can't apply CSS transformations to canvas elements as it won't know about them (i.e. setting width in the CSS instead of the element didn't work right). How would I fix my canvas element to appear as a circle that doesn't allow drawing outside the circle (or at least doesn't appear for users if drawn outside the circle).
Use the circle to create a "clipping path" for all subsequent drawing actions.
var cx = c.width / 2;
var cy = c.height / 2;
var r = Math.min(cx, cy);
ctx.beginPath();
ctx.arc(cx, cy, r, 0, 2 * Math.PI);
ctx.clip();
See http://jsfiddle.net/alnitak/MvSB2/
Note that there's a bug in Chrome which prevents the clipping mask edge from being antialiased, although it seems that your border-radius hack prevents that from looking as bad as it might.
Try using a clipping mask:
ctx.beginPath();
ctx.arc(150,150,150,0,360,false);
ctx.clip();

Categories

Resources