Basically what my project does is to fetch a picture from Database, place it into canvas, move it, zoom in and out, this this are working perfectly.
Next step is to rotate the picture and i have no idea what I am doing wrong. In the picture i described how my document looks like when the canvas is accessed. After I rotate the picture, it goes outside the canvas. My code looks like below and i have no idea what I am doing wrong. Thank you
function drawRotated(degrees) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.translate(image.width*0.15,image.height*0.15);
ctx.rotate(degrees * Math.PI / 180);
ctx.translate(-image.width*0.15,-image.height*0.15);
ctx.drawImage(image, 0, 0, image.width*0.15, image.height*0.15);
}
Maybe this is not the answer you are expecting for. I didn't use your code. I hope it helps.
The main idea is to draw the image with the center in the origin of the canvas.
window.onload = function() {
var canvas = document.getElementsByTagName('canvas')[0];
var ctx = canvas.getContext('2d');
canvas.width = 200;
canvas.height = 200;
var gkhead = gk;
gkhead.src = gk.src;
let w = gkhead.width;
let h = gkhead.height;
let x = -w/2;
let y = -h/2;
ctx.drawImage(gkhead,x,y,w,h);
function translateToThePoint(p){
ctx.save();
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.translate(p.x,p.y);
ctx.scale(.25,.25);
ctx.drawImage(gkhead,x,y,w,h);
ctx.restore();
}
function rotate(angleInRad, p){
ctx.save();
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.translate(p.x,p.y);
ctx.rotate(angleInRad);
ctx.scale(.25,.25);
ctx.drawImage(gkhead,x,y,w,h);
ctx.restore();
}
let p = {x:canvas.width/2,y:canvas.height/2}
//translateToThePoint(p);
rotate(-Math.PI/10,p);
}
canvas {
border:1px solid
}
<canvas id="canvas">
<img id="gk" src='https://www.warrenphotographic.co.uk/photography/cats/38088.jpg' />
</canvas>
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 am trying to figure out how one can detect if the user's mouse hits a line on an HTML 5 canvas with jQuery.
Here is the code that generates the canvas lines:
<canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
<script type="text/javascript" src="js/jquery-1.4.2.min.js"></script>
<script type="text/javascript">
window.onload = function(){
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.moveTo(40,0);
ctx.lineTo(40,360);
ctx.stroke();
ctx.moveTo(80,400);
ctx.lineTo(80,40);
ctx.stroke();
ctx.moveTo(120,0);
ctx.lineTo(120,360);
ctx.stroke();
ctx.moveTo(160,400);
ctx.lineTo(160,40);
ctx.stroke();
};
</script>
I'm using a modified jQuery script that I actually found in another question on here, but now I can't figure out how to detect the line, mainly the difference in color from white to black, in the canvas. I know that this can be done with images, but I haven't seen anyone with something like this.
I guess my real question is, is there a way to detect color changes on a canvas element with jQuery?
Its possible to do with javascript. In fact you aren't using any jQuery in your example above. An easy way to do it is by grabbing the pixel data from the canvas, and checking the alpha at the specified x and y position. If the alpha isn't set to 0, then you have something drawn on the canvas. Below is a function I put together real quick that does that.
Live Demo
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
width = 400;
height = 400;
canvas.width = canvas.height = 200;
// draw
ctx.moveTo(40, 0);
ctx.lineTo(40, 360);
ctx.stroke();
ctx.moveTo(80, 400);
ctx.lineTo(80, 40);
ctx.stroke();
ctx.moveTo(120, 0);
ctx.lineTo(120, 360);
ctx.stroke();
ctx.moveTo(160, 400);
ctx.lineTo(160, 40);
ctx.stroke();
function detectLine(x, y) {
var imageData = ctx.getImageData(0, 0, width, height),
inputData = imageData.data,
pData = (~~x + (~~y * width)) * 4;
if (inputData[pData + 3]) {
return true;
}
return false;
}
canvas.addEventListener("mousemove", function(e){
var x = e.pageX,
y = e.pageY;
console.log(detectLine(x, y));
});
console.log(detectLine(40, 100));
console.log(detectLine(200, 200));
I am trying to do a simple animation with html5. Please take a look at the link below, through a touch screen device.
https://dl.dropbox.com/u/41627/wipe.html
The problem is as follows : Every time the user touches the screen , a box gets drawn around his finger which animates from small to big. I want just the outer most boundary to be visible and not the rest. I do not want to clear the canvas as I want the state of the rest of the canvas to be preserved.
Images to illustrate the issue:
My code is as follows :
function init() {
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
var img = document.createElement('IMG');
img.onload = function () {
ctx.beginPath();
ctx.drawImage(img, 0, 0);
ctx.closePath();
ctx.globalCompositeOperation = 'destination-out';
}
img.src = "https://dl.dropbox.com/u/41627/6.jpg";
function drawPoint(pointX,pointY){
var grd = ctx.createRadialGradient(pointX, pointY, 0, pointX, pointY, 30);
grd.addColorStop(0, "rgba(255,255,255,.6)");
grd.addColorStop(1, "transparent");
ctx.fillStyle = grd;
ctx.beginPath();
ctx.arc(pointX,pointY,50,0,Math.PI*2,true);
ctx.fill();
ctx.closePath();
}
var a = 0;
var b = 0;
function boxAround(pointX,pointY, a, b) {
ctx.globalCompositeOperation = 'source-over';
ctx.strokeStyle = "black";
ctx.strokeRect(pointX-a, pointY-b, (2*a), (2*b));
ctx.globalCompositeOperation = 'destination-out';
if(a < 100) {
setTimeout(function() {
boxAround(pointX,pointY, a+5, b+5);
}, 20);
}
}
canvas.addEventListener('touchstart',function(e){
drawPoint(e.touches[0].screenX,e.touches[0].screenY);
boxAround(e.touches[0].screenX,e.touches[0].screenY,0 , 0);
},false);
canvas.addEventListener('touchmove',function(e){
e.preventDefault();
drawPoint(e.touches[0].screenX,e.touches[0].screenY);
},false);
You can achieve this effect by either using a second canvas, or even just having the box be a plain <div> element that is positioned over the canvas. Otherwise, there is no way around redrawing your canvas.
Hi I'm doing a drawing app using canvas but i dunno how to clear canvas.
I've tried clearRect and other functions but they don't work.
The last two function of the script should clear canvas but they don't work...
(sorry for my bad english)
Here the code:
function clear_canvas_width ()
{
var s = document.getElementById ("scribbler");
var w = s.width;
s.width = 10;
s.width = w;
}
function clear_canvas_rectangle ()
{
var canvas = $('#canvas')[0]; // or document.getElementById('canvas');
canvas.width = canvas.width;
}
Need a bit more code to really see what the problem is. Here is something really simple that you can go off of to maybe narrow it down. Also for performance reasons its better to use clearRect over resetting the width of the canvas. How you clear your canvas matters
Live Demo
var clearBut = document.getElementById("clearCan"),
canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
canvas.width = canvas.height = 300;
ctx.fillRect(10,10,280,280);
function clearCanvas(){
ctx.clearRect(0,0,canvas.width, canvas.height);
}
clearBut.addEventListener("click", clearCanvas);
Have you checked whether you are clearing the right canvas? Maybe if you provide us with more code we can help you further.
Also make sure you don't draw over it after you have cleared it.
However, when it comes to clearing canvases this is the easiest way I know.
function clear_canvas( canvas ){
canvas.width = canvas.width;
}
But you can also use
function clear_canvas (ctx, canvas){
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
from;
How to clear the canvas for redrawing
// 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();
I am making an HTML5 canvas game, and I wish to rotate one of the images.
var link = new Image();
link.src='img/link.png';
link.onload=function(){
ctx.drawImage(link,x,y,20,20); // draws a chain link or dagger
}
I wish to rotate this image. The standard way of rotating image was to set a rotation on the canvas context object. However, that rotates the entire game! I don't want to do that, and only wish to rotate this one sprite. How do I do that?
Use .save() and .restore() (more information):
link.onload=function(){
ctx.save(); // save current state
ctx.rotate(Math.PI); // rotate
ctx.drawImage(link,x,y,20,20); // draws a chain link or dagger
ctx.restore(); // restore original states (no rotation etc)
}
You might want to put a translate(); there because the image is going to rotate around the origin and that is in the top left corner by default so you use the translate(); to change the origin.
link.onload=function(){
ctx.save();
ctx.translate(x, y); // change origin
ctx.rotate(Math.PI);
ctx.drawImage(link,-10,-10,10,10);
ctx.restore()
}
Your original "solution" was:
ctx.save();
ctx.translate(x,y);
ctx.rotate(-this.angle + Math.PI/2.0);
ctx.translate(-x, -y);
ctx.drawImage(this.daggerImage,x,y,20,20);
ctx.restore();
However, it can be made more efficient (with no save or restore) by using this code:
ctx.translate(x,y);
ctx.rotate(-this.angle + Math.PI/2.0);
ctx.drawImage(this.daggerImage,x,y,20,20);
ctx.rotate(this.angle - Math.PI/2.0);
ctx.translate(-x, -y);
Look at my solution. It's full example and the easiest to understand.
var drawRotate = (clockwise) => {
const degrees = clockwise == true? 90: -90;
let canvas = $('<canvas />')[0];
let img = $(".img-view")[0];
const iw = img.naturalWidth;
const ih = img.naturalHeight;
canvas.width = ih;
canvas.height = iw;
let ctx = canvas.getContext('2d');
if(clockwise){
ctx.translate(ih, 0);
} else {
ctx.translate(0, iw);
}
ctx.rotate(degrees*Math.PI/180);
ctx.drawImage(img, 0, 0);
let rotated = canvas.toDataURL();
img.src = rotated;
}
Here i made a working example from one of my games. u can get the image from Here.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset=utf-8 />
<title>Test</title>
</head>
<body>
<canvas id="canvas" width="100" height="100"></canvas>
<script type="text/javascript">
var ctx = document.getElementById('canvas').getContext('2d');
var play = setInterval('Rotate()',16);
var i = 0;
var shipImg = new Image();
shipImg.src = 'ship.png';
function Rotate() {
ctx.fillStyle = '#000';
ctx.fillRect(0,0,100,100);
ctx.save();
ctx.translate(50, 50);
ctx.rotate(i / 180 / Math.PI);
ctx.drawImage(shipImg, -16, -16);
ctx.restore();
i += 10;
};
</script>
</body>
</html>
I ended up having to do:
ctx.save();
ctx.translate(x,y);
ctx.rotate(-this.angle + Math.PI/2.0);
ctx.translate(-x, -y);
ctx.drawImage(this.daggerImage,x,y,20,20);
ctx.restore();