I have two canvas and a select button.
The select button is for changing the scale in the 2nd canvas.
The content of the 1rst canvas is copyed in the 2nd canvas.
When I increase the scale with the select button, the 2nd canvas is resized and scaled perfectly, but his render is bad (the rectangle and the text are blured).
What is the problem ?
Here the source code (you can try it https://jsfiddle.net/0kqqnkmp/) :
<canvas id="canvas"></canvas>
<canvas id="canvas_second"></canvas>
<br>Choose your scale : <select onchange="change_scale(this);" autocomplete="off">
<option>0.5</option>
<option selected>1</option>
<option>1.5</option>
<option>2</option>
</select>
<script type="text/javascript">
//The canvas :
c = document.getElementById("canvas");
c.style.border = "solid #000000 1px";
//The second canvas :
c_second = document.getElementById("canvas_second");
c_second.style.border = "solid #000000 1px";
//Define the original width and height canvas :
ORIGINAL_WIDTH_CANVAS = 300;
ORIGINAL_HEIGHT_CANVAS = 300;
c.width = ORIGINAL_WIDTH_CANVAS;
c.height = ORIGINAL_HEIGHT_CANVAS;
c_second.width = ORIGINAL_WIDTH_CANVAS;
c_second.height = ORIGINAL_HEIGHT_CANVAS;
//The canvas context :
ctx = c.getContext("2d");
ctx_second = c_second.getContext("2d");
//Default scaling
scale = 1;
//Drawing function :
function draw()
{
//Clear the drawing :
ctx.clearRect(0, 0, ORIGINAL_WIDTH_CANVAS, ORIGINAL_HEIGHT_CANVAS);
//Drawing a red rectangle :
ctx.fillStyle = "#000000";
ctx.fillRect(5, 5, 50, 50);
//Drawing a text :
ctx.font = "normal bold 20px sans-serif";
ctx.fillText("Hello world", ORIGINAL_WIDTH_CANVAS-220, ORIGINAL_HEIGHT_CANVAS-10);
//Clear the drawing on the second canvas :
ctx_second.clearRect(0, 0, ORIGINAL_WIDTH_CANVAS, ORIGINAL_HEIGHT_CANVAS);
//Copy drawing on the second canvas :
ctx_second.drawImage(c, 0, 0);
}
//Function for scaling the second canvas :
function change_scale(this_select)
{
//Retrieve the scale value :
scale = parseFloat(this_select.value);
//Resize the second canvas :
c_second.width = ORIGINAL_WIDTH_CANVAS * scale;
c_second.height = ORIGINAL_HEIGHT_CANVAS * scale;
//Apply scaling on the second canvas :
ctx_second.scale(scale, scale);
}
//Draw :
setInterval("draw()", 300);
</script>
Your blurry results are to be expected when you scale up an image.
A canvas is effectively a bitmap image. And a bitmap image becomes blurry when scaled up.
So when you scale & draw your bitmap-canvas#1 onto canvas#2 you will get a blurry result.
The fix is to scale(2,2) canvas#2 and then reissue the same commands that drew your rectangle & text onto the first canvas.
The nice bit is that scale will automatically take care of changing your [x,y] coordinates when redrawing. So you use the exact same [x,y] coordinates that you used to draw into canvas#1.
// scale the second canvas
secondContext.scale(2,2);
//Drawing a red rectangle :
secondContext.fillStyle = "#000000";
secondContext.fillRect(5, 5, 50, 50);
//Drawing a text :
secondContext.font = "normal bold 20px sans-serif";
secondContext.fillText("Hello world", ORIGINAL_WIDTH_CANVAS-220, ORIGINAL_HEIGHT_CANVAS-10);
Related
i made 2 deference size html canvas to drawing
First canvas = width : 400px,height:200px
Second canvas = width : 200px,height :100px
Now when i drawing in first html canvas i send that coordinates(x1,y1,x2,y2) to second canvas.
When first canvas coordinates send in second canvas it's not drawing in same place as first canvas.
is there way to equal first canvas coordinates to second one without changing canvas width and height .
ctx.beginPath();
ctx.lineWidth = 5;
ctx.lineCap = 'round';
ctx.strokeStyle = "red";
ctx.moveTo(coord.x, coord.y);
ctx.lineTo(ncoord.x , ncoord.y);
ctx.stroke();
//SECOUND CANVAS
ctx2.beginPath();
ctx2.lineWidth = 5;
ctx2.lineCap = 'round';
ctx2.strokeStyle = "red";
ctx2.moveTo(coord.x, coord.y);
ctx2.lineTo(ncoord.x , ncoord.y);
ctx2.stroke();
when user drwaing in canvas 1 i send that coordinates to both canvas. but in second canvas not drawing in same place as canvas 1.
Note : canvas 1 and 2 have deferent width and height.
I need to slove this without changing width height of the both canvas.
I hope I have made the right assumptions to answer your question. I created two different canvases of two different sizes. The coordinates only fit on the first, bigger, canvas.
You can transform the 'big' coordinates to 'small' coordinates by dividing the width or height of the bigger smaller canvases by the bigger canvases.
For example, the height of the big canvas is 200 but the height of the smaller one is 100. If you divide 100 / 200 you get 0.5. The 'small' coordinates should be half as high as the original ones. See for yourself below:
//just for testing purposes
var coord = {
x: 320,
y: 125
};
var ncoord = {
x: 220,
y: 90
};
function drawBig() {
var canvas = document.getElementById("canvas1");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.lineWidth = 5;
ctx.lineCap = 'round';
ctx.strokeStyle = "red";
ctx.moveTo(coord.x, coord.y);
ctx.lineTo(ncoord.x, ncoord.y);
ctx.stroke();
}
function drawSmall() {
let bigCanvas = document.getElementById("canvas1");
let smallCanvas = document.getElementById("canvas2");
//Devide the dimensions of the big and small canvas in order to get the magnification factor:
let widthDimension = smallCanvas.width / bigCanvas.width;
let heightDimension = smallCanvas.height / bigCanvas.height
var ctx2 = smallCanvas.getContext("2d");
ctx2.beginPath();
ctx2.lineWidth = 5;
ctx2.lineCap = 'round';
ctx2.strokeStyle = "red";
//Transform the original coordinates to the right dimensions:
ctx2.moveTo(coord.x * widthDimension, coord.y * heightDimension);
ctx2.lineTo(ncoord.x * widthDimension, ncoord.y * heightDimension);
ctx2.stroke();
}
canvas {
border: 1px solid black;
}
<canvas id="canvas1" width="400" height="200"></canvas>
<hr>
<canvas id="canvas2" width="200" height="100"></canvas>
<button onclick="drawBig()">draw big canvas</button>
<button onclick="drawSmall()">draw small canvas</button>
Hope this helps! If not, please comment
I've been experimenting with the <canvas> recently, and I noticed a strange behaviour when stroking rectangles near the origin (0, 0) of the canvas.
// canvas context
var ctx = document.querySelector('#screen').getContext('2d');
// draw a rectangle
ctx.fillStyle = 'orange';
ctx.fillRect(0, 0, 100, 100);
// stroke a border for the rectangle
ctx.lineWidth = 20;
ctx.strokeRect(0, 0, 100, 100);
<canvas id="screen"></canvas>
What went wrong?
In the example above, the rectangle itself was drawn at (0, 0) as intended, but its border (the stroked rectangle) seems to be drawn at an offset.
Generally, when stroking a rectangle at a position away from the origin, this effect is omitted —
Meaning that the stroked rectangles aren't being drawn starting at the position specified, but at an offset, I suppose.
Why is that?
The stroke is centered around the coordinates that your primitve is defined at. In the case of your rectangle with stroke width of 20, drawing this at the top left of the canvas will cause half of the strokes width to be drawn outside of the canvas boundary.
Adjusting the coordinates of strokeRect() to 10,10,.. causes the rectangle to be offset from the canvas origin, meaning that the full stroke of 20 pixels will be visible from the top-left of the canvas:
ctx.lineWidth = 20;
ctx.strokeRect(10, 10, 100, 100);
Consider the following adjustments, made to ensure the stroke is fully visible around the drawn rectangle:
var canvas = document.querySelector('#screen');
// Set the width and height to specify dimensions of canvas (in pixels)
// Choosing a 100x100 square matches the strokeRect() drawn below and
// therefore achieves the appearance of a symmetric stroke
canvas.width = 100;
canvas.height = 100;
// canvas context
var ctx = canvas.getContext('2d');
// draw a rectangle
ctx.fillStyle = 'orange';
ctx.fillRect(10, 10, 90, 90);
// stroke a border for the rectangle
ctx.lineWidth = 20;
var halfStroke = ctx.lineWidth * 0.5;
ctx.strokeRect(halfStroke, halfStroke, 100 - (halfStroke * 2), 100 - (halfStroke * 2));
<canvas id="screen"></canvas>
Update
Here is a visualisation of the stroke in relation to the line/rectangle edge provided by Ibrahim Mahrir:
I'm using node-canvas and I was wonder how style an imported image in canvas similar to how you would an image in CSS.
For example, how would I crop a square image in canvas to a circle. In CSS, all you need to do is set border radius to 50%.
Well obviously you cannot use CSS in this case since CSS is applied to the DOM and not the the pixel based content of a Canvas element.
However the Canvas element has its own set of draw functions which allow to you replicate or at least approximate CSS rules.
Since you mentioned cropping an image to a circle I'll focus on this example. To achieve this effect you want to specify a clipping region before drawing the image. Every pixel outside of the clipped region will not be drawn. Effectively this will crop the image to the clipped region.
In code:
// Retrieve canvas and get context
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
// Save the context so we can undo the clipping region at a later time
context.save();
// Define the clipping region as an 360 degrees arc at point x and y
context.beginPath();
context.arc(x, y, radius, 0, 2 * Math.PI, false);
// Clip!
context.clip();
// Draw the image at imageX, imageY.
context.drawImage(image, imageX, imageY);
// Restore context to undo the clipping
context.restore();
I'd advice taking a look at this page to give you an idea of what you can do with the Canvas element and the 2D rendering context.
I don't know if this would work in node, However you can do this with canvas;
The simplest way of doing it is using, as you intended, border-radius:
canvas{border-radius:50%;}
An other way of doing it is by using the ctx.clip() method.
let canvas = document.querySelector("canvas");
let ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.arc(125,120,100,0,2*Math.PI);
// you clip the context
ctx.clip();
let img = document.querySelector("#testImg");
ctx.drawImage(img, 0, 20);
<canvas width="250" height="240" >
<img id="testImg" src="theImage.jpg">
</canvas>
Yet an other way of doing it is by using ctx.globalCompositeOperation = "destination-atop"in this way:
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let cw = canvas.width = 400,
cx = cw / 2;
let ch = canvas.height = 400,
cy = ch / 2;
ctx.globalCompositeOperation = "destination-atop";
let img = document.querySelector("#testImg");
ctx.drawImage(img, 0, 0);
ctx.beginPath();
ctx.fillStyle = "#f00";
ctx.arc(cx, cx, 100, 0, 2 * Math.PI);
ctx.fill();
I have a canvas.
In this canvas, I must draw a grid with red rectangles :
-firstly, I draw vertical rectangles,
-then, I draw horizontal rectangles
Every rectangle have the same opacity (0.3).
Normally, the color in intersection of 2 rectangles must be more red because of the superposition.
So the render must be like this :
But my code doesn't work because the color in intersection isn't more red, the color is the same than a rectangle (you can try it : https://jsfiddle.net/6urj27ua/) :
<canvas id="canvas"></canvas>
<script type="text/javascript">
//The canvas :
c = document.getElementById("canvas");
c.style.border = "solid #000000 1px";
//Size of canvas :
c.width = 300;
c.height = 300;
//The canvas context :
ctx = c.getContext("2d");
//Drawing function :
function draw()
{
//Clear the drawing :
ctx.clearRect(0, 0, c.width, c.height);
/*Define size of a rect :*/
width_rect = 20;
height_rect = 200;
/*Fill color for rect :*/
ctx.fillStyle = "rgba(255, 0, 0, 0.3)";
/*Draw 5 vertical rectangles :*/
for(i = 0; i <= 5 ; i++)
{
ctx.rect(i*(width_rect*2), 0, width_rect, height_rect);
}
/*Draw 5 horizontal rectangles :*/
for(i = 0; i <= 5 ; i++)
{
ctx.rect(0, i*(width_rect*2), height_rect, width_rect);
}
ctx.fill();
}
//Draw :
setInterval("draw()", 300);
</script>
So what's the problem ?
You are almost there. But by using ctx.rect() and ctx.fill(), the whole shape is drawn at once and no 'superposition' is applied.
You can easily fix it by:
replacing ctx.rect() calls with ctx.fillRect()
removing the ctx.fill() which becomes irrelevant
Here is a fixed JSFiddle.
Alternate method
You could also use two distinct paths, but you'd need to clearly circumscribe them with the .beginPath() method: like this.
I know I can draw arc with arc function of canvas but when I increase the size of that arc it changes its start and end x,y points. So I was thinking if we can draw arc with some other way keeping its start end points fixed while increasing it size.
Edit
Below is the images showing what I am looking for. First image shows a rectangle. when its side is stretched it changed to circle(2nd image). when side is stretched further it changed to big circle. At in all images you can see that end points of circle are joined to corners of rectangle. This is what I want to do.
1st Image
2nd Image
3rd Image
Or you can see this video to understand what I am looking to do.
What I have done
This fiddle shows result of my work.
To draw rectangle just click and drag your mouse.
Here is the code
I believe you're looking for something like this:
draw(0);
$('#range').on('change', function(){
range = parseInt($(this).val());
draw(range)
})
function draw(val){
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d'),
x = 100,
y = 50,
d;
context.clearRect(0, 0, canvas.width, canvas.height);
d = Math.sqrt(Math.pow(val,2) + Math.pow(50,2));
context.beginPath();
context.lineWidth = 1;
context.arc(x,y+val,d,0,2*Math.PI);
// line color
context.strokeStyle = 'black';
context.stroke();
// Cut off the top of the circle.
context.clearRect(0, 0, canvas.width, y);
// This stuff's just to show some dots
context.fillStyle = 'red';
context.fillRect(x-1,y-1,2,2); // Middle
context.fillRect(x-52,y-2,4,4);//Target point 1
context.fillRect(x+48,y-2,4,4);// Target point 2
context.fillRect(x-2,y+d+val-2,4,4); // Point on circle
context.fillStyle = 'black';
}
Working sample
There are a couple disadvantages to this is that it gets "slower" the closer you get to the circle, because the circle's getting exponentially larger in the hidden section (The slider's controlling it's size), and that it doesn't work for diagonal lines as it is right now.
Other than that, it works like expected.
Check this : http://jsfiddle.net/KZBzq/4/
Updated answer with bezierCurveTo
HTML
<label>Range :</label>
<input type="range" name="points" value="0" min="0" step="1" max="100" id="range">
<canvas id="myCanvas" width="578" height="250"></canvas>
JS
draw(100);
$('#range').on('change', function(){
range = parseInt($(this).val());
draw(100-range)
})
function draw(val){
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d'),
x = 100,
y = 100,
cp1x = x/2,
cp2x = y/2,
cp1y = val,
cp2y = val;
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.lineWidth = 1;
context.moveTo(25 , x);
context.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y);
// line color
context.strokeStyle = 'black';
context.stroke();
}
Now x and Y are fixed. Is this was your requirement?