html code :
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript">
function start() {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var image = new Image();
image.onload = function () {
ctx.drawImage(image, 69, 50);
//draw a circle
ctx.beginPath();
ctx.arc(100, 75, 10, 0, Math.PI * 2, true);
ctx.stroke();
};
image.src = 'xy-coordinates.jpg';
}
</script>
</head>
<img id="myImgId" alt="" src="xy-coordinates.jpg" />
<input type="button" value="submit" onclick="start()">
<canvas id="myCanvas" width="700" height="393"></canvas>
</body>
</html>
I'm trying to draw a circle on an image dynamically once I click the button.
The problem is after clicking the button, I'm getting an extra image (with the circle drawn) displayed on the screen rather drawing on original image.
My requirement is to draw a circle on an image which is already displayed.
UPDATED
<script>
function Draw(){
var img = document.getElementById("theImg");
var cnvs = document.getElementById("myCanvas");
cnvs.style.position = "absolute";
cnvs.style.left = img.offsetLeft + "px";
cnvs.style.top = img.offsetTop + "px";
var ctx = cnvs.getContext("2d");
ctx.beginPath();
ctx.arc(250, 210, 10, 0, 2 * Math.PI, false);
ctx.lineWidth = 3;
ctx.stroke();
}
</script>
<img id='theImg' src='xy-coordinates.jpg'>
<input type='button' value='Draw' onclick='draw()' ><br>
<canvas id='myCanvas' width="700" height="393"></canvas>
when < br > is used in html tag befor or after canvas there comes a huge distance betweein image and button r any tags i place in there. how to rectify it ?
You don't need to create one more image because canvas in fact is an image by itself. Place your canvas on top of the source image by setting its position to absolute, left and top same as the source image:
var img = document.getElementById("myImgId");
var c = document.getElementById("myCanvas");
c.style.position = "absolute";
c.style.left = img.offsetLeft;
c.style.top = img.offsetTop;
Then just draw into canvas:
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.arc(100, 75, 10, 0, Math.PI * 2, true);
ctx.stroke();
UPDATE:
function Draw(){
var img = document.getElementById("theImg");
var cnvs = document.getElementById("myCanvas");
cnvs.style.position = "absolute";
cnvs.style.left = img.offsetLeft + "px";
cnvs.style.top = img.offsetTop + "px";
var ctx = cnvs.getContext("2d");
ctx.beginPath();
ctx.arc(250, 210, 200, 0, 2 * Math.PI, false);
ctx.lineWidth = 3;
ctx.strokeStyle = '#00ff00';
ctx.stroke();
}
#draw-btn {
font-size: 14px;
padding: 2px 16px 3px 16px;
margin-bottom: 8px;
}
<div>
<input id='draw-btn' type='button' value='Draw' onclick='Draw()' />
</div>
<img id='theImg' src='http://i.telegraph.co.uk/multimedia/archive/02351/cross-eyed-cat_2351472k.jpg'>
<canvas id='myCanvas' width='536px' height='536px'></canvas>
You are drawing one image on canvas, and second image with "img" tag (already displayed). you can't draw circle with canvas, on HTML tag.
Related
I'm working on an app that allows the user to select an image and define a custom clipping region for that image. The user can place blue dots on the canvas containing the image, which will represent the clipping path. These points are stored in the coordinates array. This is the function that clips the image to the selected area:
function crop()
{
ctx.beginPath();
ctx.moveTo(coordinates[0].x, coordinates[0].y);
for(let i=1; i<coordinates.length; i++)
{
ctx.lineTo(coordinates[i].x, coordinates[i].y);
}
ctx.lineTo(coordinates[0].x, coordinates[0].y);
ctx.stroke();
ctx.save();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.restore();
ctx.clip();
//redraw the image in the cropped area
let pattern = ctx.createPattern(image, "repeat");
ctx.fillStyle = pattern;
ctx.fill();
ctx.closePath();
}
This works perfectly fine when I let the image keep its natural dimensions (in this case 1200x600):
But if I give the image max-height and max-width properties, each set to 80vmin, the cropping stops working correctly:
Instead of cropping the image around the eye area, like in the previous image, it clips a different part of the image to the selected area.
I suspect createPattern is for some reason not loading the image properly, with its updated dimensions. So I tried passing it a new canvas object (which is basically a copy of the canvas I'm working on in its original form, with the resized image painted on and nothing else) instead of the image itself, but that didn't seem to work either, I was getting the same result. Is there anything else I could try?
Update: here is a JSFiddle containing the whole code
const image = document.getElementById("pic");
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
//draw the image on the canvas
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0, image.width, image.height);
//get mouse coordinates
let coordinates = [];
canvas.onclick = function clickEvent(e)
{
let rect = e.target.getBoundingClientRect();
let x = e.clientX - rect.left;
let y = e.clientY - rect.top;
let point = {
"x": x,
"y": y
};
coordinates.push(point);
ctx.fillStyle = "blue";
ctx.fillRect(x, y, 10, 10);
}
//debugging
console.log(coordinates);
//crop the image
function crop()
{
ctx.beginPath();
ctx.moveTo(coordinates[0].x, coordinates[0].y);
for(let i=1; i<coordinates.length; i++)
{
ctx.lineTo(coordinates[i].x, coordinates[i].y);
}
ctx.lineTo(coordinates[0].x, coordinates[0].y);
ctx.stroke();
ctx.save();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.restore();
ctx.clip();
//redraw the image in the cropped area
let pattern = ctx.createPattern(image, "repeat");
ctx.fillStyle = pattern;
ctx.fill();
ctx.closePath();
}
function save()
{
let newCanvas = document.getElementById('canvas2');
//get new dimensions
let top = canvas.height;
let left = canvas.width;
let right = -canvas.width;
let bottom = -canvas.height;
for(let i=0; i<coordinates.length; i++)
{
if(coordinates[i].y < top)
top = coordinates[i].y;
if(coordinates[i].x < left)
left = coordinates[i].x;
if(coordinates[i].x > right)
right = coordinates[i].x;
if(coordinates[i].y > bottom)
bottom = coordinates[i].y;
}
newCanvas.width = right-left;
newCanvas.height = bottom-top;
//save the new image
newCanvas.getContext("2d").drawImage(canvas, left, top, newCanvas.width, newCanvas.height, 0, 0, newCanvas.width, newCanvas.height);
let downloadLink = document.createElement("a");
downloadLink.download = "crop.png";
downloadLink.href = newCanvas.toDataURL("image/png");
downloadLink.click();
}
h1 {
color: blue;
margin-bottom: 0px;
margin-top: 0px;
}
.image-div img {
max-width: 80vmin;
max-height: 80vmin;
border: 1px solid black;
}
.canvas-div {
margin-top: 240px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test</title>
</head>
<body>
<h1>Image:</h1>
<div class="image-div">
<img id="pic" src="https://icatcare.org/app/uploads/2018/07/Helping-your-new-cat-or-kitten-settle-in-1.png">
</div>
<div class="canvas-div">
<h1>Canvas:</h1>
<canvas id="canvas" style="border: 1px solid blue;"></canvas>
</div>
<!-- for exporting the final result -->
<canvas id="canvas2" style="display: none;"></canvas>
<button onclick="crop()">Crop</button>
<button onclick="save()">Save</button>
<br><br><br>
<!-- script -->
<script src="script.js"></script>
</body>
</html>
Before reading the explanation try the following:
ctx.beginPath();
ctx.moveTo(coordinates[0].x, coordinates[0].y);
for(let i=1; i<coordinates.length; i++)
{
ctx.lineTo(coordinates[i].x, coordinates[i].y);
}
ctx.lineTo(coordinates[0].x, coordinates[0].y);
ctx.stroke();
ctx.closePath();
ctx.clip();
ctx.save();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.restore();
You will see that it does an invert cropping, that is, it clears the points you want to not clear and preserves the points you wanted to clear. So, to solve your problem, assuming that the crop will be a convex shape, you need the following:
get all the lines involved
create a polygon of the "outside" of the polygons
clear
Since you can have many different types of shapes and they can be convex or concave alike, it would be far-fetched for the purpose of a stackoverflow answer to implement the full logic, but this is how you can do it relatively easily:
Make sure that the lines are continuous, so if you draw the lines by hand from coordinates[0] to coordinates1 and then coordinates1 to coordinates[2], etc. then you will end up drawing the correct polygon (so each line between coordinates[i-1] and coordinates[i]) is a side of your polygon rather than a diagonal.
moveTo coordinates[0] and have a lineto for all coordinates, almost like you did, except for the very last one. So, you are effectively almost drawing the polygon except the very last line.
continue with lineto to a side of the canvas (careful though, not to cross the internal of the polygon) and then to all the edges of the canvas (the corners), so you are effectively defining a polygon that's a. outside your polygon b. ends at your last coordinate c. does not include your last line
clear
moveTo your penultimate coordinate, lineTo your last coordinate and then proceed with lineTo calls to draw the
The snippet below is a proof-of-concept that is a good starting point, but it is not a proper solution yet.
const image = document.getElementById("pic");
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
//draw the image on the canvas
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0, image.width, image.height);
//get mouse coordinates
let coordinates = [];
canvas.onclick = function clickEvent(e)
{
let rect = e.target.getBoundingClientRect();
let x = e.clientX - rect.left;
let y = e.clientY - rect.top;
let point = {
"x": x,
"y": y
};
coordinates.push(point);
ctx.fillStyle = "blue";
ctx.fillRect(x, y, 10, 10);
}
//debugging
console.log(coordinates);
//crop the image
function crop()
{
ctx.beginPath();
for(let i=0; i<coordinates.length - 1; i++)
{
ctx.lineTo(coordinates[i].x, coordinates[i].y);
}
ctx.lineTo(canvas.width, canvas.height);
ctx.lineTo(canvas.width, 0);
ctx.lineTo(0, 0);
ctx.lineTo(0, canvas.height);
ctx.lineTo(coordinates[0].x, coordinates[0].y);
ctx.stroke();
ctx.closePath();
ctx.clip();
ctx.save();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.restore();
}
function save()
{
let newCanvas = document.getElementById('canvas2');
//get new dimensions
let top = canvas.height;
let left = canvas.width;
let right = -canvas.width;
let bottom = -canvas.height;
for(let i=0; i<coordinates.length; i++)
{
if(coordinates[i].y < top)
top = coordinates[i].y;
if(coordinates[i].x < left)
left = coordinates[i].x;
if(coordinates[i].x > right)
right = coordinates[i].x;
if(coordinates[i].y > bottom)
bottom = coordinates[i].y;
}
newCanvas.width = right-left;
newCanvas.height = bottom-top;
//save the new image
newCanvas.getContext("2d").drawImage(canvas, left, top, newCanvas.width, newCanvas.height, 0, 0, newCanvas.width, newCanvas.height);
let downloadLink = document.createElement("a");
downloadLink.download = "crop.png";
downloadLink.href = newCanvas.toDataURL("image/png");
downloadLink.click();
}
h1 {
color: blue;
margin-bottom: 0px;
margin-top: 0px;
}
.image-div img {
max-width: 80vmin;
max-height: 80vmin;
border: 1px solid black;
}
.canvas-div {
margin-top: 240px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test</title>
</head>
<body>
<h1>Image:</h1>
<div class="image-div">
<img id="pic" src="https://icatcare.org/app/uploads/2018/07/Helping-your-new-cat-or-kitten-settle-in-1.png">
</div>
<div class="canvas-div">
<h1>Canvas:</h1>
<canvas id="canvas" style="border: 1px solid blue;"></canvas>
</div>
<!-- for exporting the final result -->
<canvas id="canvas2" style="display: none;"></canvas>
<button onclick="crop()">Crop</button>
<button onclick="save()">Save</button>
<br><br><br>
<!-- script -->
<script src="script.js"></script>
</body>
</html>
I wrote the following code which is similar to my objective but falls short; because I want to fill inside the text in multiple colors; currently only in #FF00FF.
Playground
I think the problem is that I don't know how to use the text "ABC" as the clipping path. Please show me how to do it, or any answer equivalent for my objective.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>canvas</title>
<script type="text/javascript">
function test() {
var canvas = document.getElementById('sample');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.beginPath();
for(let i=1;i<100;i++){
//mock1: ctx.fillStyle = "rgb(" + [i, i, i] + ")";
ctx.moveTo(0,5*i);
ctx.lineTo(380,5*i);
ctx.lineTo(300,5*i+3);
ctx.lineTo(0,5*i+3);
ctx.lineTo(0,5*i);
}
ctx.closePath();
ctx.clip();
ctx.font = "bold 72px 'Sans-selif'";
ctx.fillStyle = "#FF00FF";
ctx.fillText("ABC", 90, 60);
}
}
</script>
</head>
<body onLoad="test()">
<h2>Canvas</h2>
<canvas width="300" height="150" id="sample" style="background-color:yellow;">
</canvas>
</body>
</html>
The 2DCanvas API unfortunately doesn't expose the text's contour in a way it could be used as a Path2D or in methods like clip() or isPointInPath().
However for what you wish to do, you don't need a path, this can be done by using compositing instead of clipping:
var canvas = document.getElementById('sample');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
// draw the full lines first
ctx.beginPath();
for (let i = 1; i < 100; i++) {
ctx.moveTo(0, 5 * i);
ctx.lineTo(380, 5 * i);
ctx.lineTo(300, 5 * i + 3);
ctx.lineTo(0, 5 * i + 3);
ctx.lineTo(0, 5 * i);
}
ctx.fillStyle = "#00FF00";
ctx.fill();
// with this mode
// every previous pixel that is not under the next drawing
// will get cleared
ctx.globalCompositeOperation = "destination-atop"
ctx.font = "bold 72px 'Sans-serif'";
ctx.fillStyle = "#FF00FF";
ctx.fillText("ABC", 90, 60);
// reset to default mode
ctx.globalCompositeOperation = "source-over"
}
<canvas width="300" height="150" id="sample" style="background-color:yellow;"></canvas>
Picture 1 is the picture created out of canvas. Picture 2 is the picture I downloaded. I don't know why the shape of the picture changes back to a rectangle. I hope it can be the same shape as the one I see on the web page (picture 1). Thank you for any help!!!!
Picture 1
Picture 2
Here are the codes I used to draw pictures on canvas and convert canvas to an image.
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
<a id="final" href="#" class="btn btn-success btn_lg">Hello<img src = "contacts.png" alt= "your image" width="30" height="30" /> </a>
<img id="scream" width="220" height="277"
src="contacts.png" alt="contacts">
<p>Canvas:</p>
<canvas id="myCanvas" width="240" height="297"
style="border-radius: 15px;">
Your browser does not support the HTML5 canvas tag.
</canvas>
<style>
</style>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.font = "30px Arial";
ctx.fillText("Hello World",70,260);
ctx.fillStyle="#0000ff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
window.onload = function() {
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var img = document.getElementById("scream");
ctx.drawImage(img, 10, 10);
};
function download() {
var canvas = document.getElementById('myCanvas');
dataUrl = canvas.toDataURL();
imageFoo = document.createElement('img');
imageFoo.src = dataUrl;
// Style your image here
imageFoo.style.width = '100px';
imageFoo.style.height = '100px';
imageFoo.style.borderRadius= "12px";
document.body.appendChild(imageFoo);
See the code below:
var mctx = document.getElementById( 'rounded-rect' ).getContext( '2d' );
function roundRect( ctx, x, y, width, height, radius ) {
if ( typeof radius === 'undefined' ) radius = 5;
ctx.beginPath();
ctx.moveTo( x + radius, y );
ctx.lineTo( x + width - radius, y );
ctx.quadraticCurveTo( x + width, y, x + width, y + radius );
ctx.lineTo( x + width, y + height - radius );
ctx.quadraticCurveTo( x + width, y + height, x + width - radius, y + height );
ctx.lineTo( x + radius, y + height );
ctx.quadraticCurveTo( x, y + height, x, y + height - radius );
ctx.lineTo( x, y + radius );
ctx.quadraticCurveTo( x, y, x + radius, y );
ctx.closePath();
}
var img = new Image();
img.src = "https://unsplash.it/3000/3000/?random";
img.onload = function() {
mctx.save();
roundRect( mctx, 10, 10, 200, 200, 20 );
mctx.clip();
mctx.drawImage( img, 10, 10, 200, 200 );
mctx.restore();
roundRect( mctx, 10, 10, 200, 200, 20 );
mctx.lineWidth = 2;
mctx.stroke();
}
#download {
float: left;
cursor: pointer;
color: #555;
padding: 3px
}
#download:hover {
color: #f00
}
<canvas id="rounded-rect" width="300" height="300"></canvas>
I've a <canvas> with an image already loaded and all the graphics coordinates needed for a crop (x, y, w, h) in an array.
What I'm trying to do is to crop the canvas directly, without a temporary other canvas to copy to/from (as suggested in other SO answers).
My idea is to:
1) Draw the selected area on the top-left corner of the canvas
2) Shrink the canvas size to the area
$('#edit').on("click", function() {
var img = $('#canvas');
var c = img[0];
var ctx = c.getContext("2d");
//var imageData = ctx.getImageData(0, 0, 100, 100);
ctx.drawImage(c, 0, 0, 100, 100, 0, 0, 100, 100);
c.width = 100;
c.height = 100;
});
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "green";
ctx.fillRect(0, 0, 350, 350);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas" width="350" height="350"></canvas>
<input id="edit" type="button" value="Edit" />
Seems easy to me, but I'm missing something: when I execute, I get nothing https://jsfiddle.net/qg0znpu7/
What's wrong with my code? how can I fix it to obtain an in-place canvas crop?
Changing the width or height of a canvas will clear it. For that reason you will have to copy the data first.
You can use putImageData() for that:
$('#edit').on("click", function() {
var c = $('#canvas')[0];
var ctx = c.getContext("2d");
var imageData = ctx.getImageData(0, 0, 100, 100);
c.width = 100;
c.height = 100;
ctx.putImageData(imageData, 0, 0);
});
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "green";
ctx.fillRect(0, 0, 350, 350);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas" width="350" height="350"></canvas>
<input id="edit" type="button" value="Edit" />
In My case i have performed path clipping and then draw a image in clipped region. But it does not working.
here my code.`
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = 75;
var offset = 50;
/*
* save() allows us to save the canvas context before
* defining the clipping region so that we can return
* to the default state later on
*/
context.save();
context.beginPath();
context.arc(x, y, radius, 0, 2 * Math.PI, false);
context.clip();
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, 69, 50);
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/darth- vader.jpg';
</script>
</body>
</html>
Now image render without clipping.
Please share your valuable key to do this.