I'm trying to do a responsive canvas. All my tests has been doing with a 600x600 canvas and with that height and width it works OK and paint every line correctly. The problem is that I have tried this:
#myCanvas {
background-color: #ffffff;
border:1px solid #000000;
min-height: 600px;
height: 100%;
width: 100%;
}
Just for the record, myCanvas is inside a sm-col-8.
And it looks nice on my laptop and looks nice on my phone but (because of my draw() function, because it was thinking for a square) the draw starts more like in the down-left corner (nearby) and it should start at up-right corner.
So, I don't want to change my draw() function but what I'm looking for is to reescale the canvas size. I mean: If I'm in a laptop/tablet.. with 600x600, show it at that size, but if I'm on my phone which has 384x640 show it like 300x300? I don't know if it could be a good solution.
My draw function:
function drawLines(lines,i,table,xtotal,ytotal){
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var xIni;
var xFin;
var yIni;
var yFin;
xIni = (c.width*parseInt(lines[i][1])/xtotal);
yIni = (c.height*parseInt(lines[i][2])/ytotal);
xFin = (c.width*parseInt(lines[i][3])/xtotal);
yFin = (c.height*parseInt(lines[i][4])/ytotal);
ctx.beginPath();
ctx.moveTo(xIni,c.height-yIni);
ctx.lineTo(xFin,c.height-yFin);
ctx.lineWidth=4;
ctx.strokeStyle = colorAleatorio();
ctx.stroke();
}
With Bootstrap, use:
<canvas id="canvas" class='img-responsive' style="border: 1px solid black;"></canvas>
You can make your html Canvas responsive by using the context.scale command.
The .scale command will scale the internal coordinates system used by canvas.
This means you do not need to change any of your own drawing coordinates because canvas will automatically transform your coordinates into scaled canvas coordinates for you.
// save the original width,height used in drawLines()
var origWidth=600;
var origHeight=600;
var scale=1.00;
// reference to canvas and context
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
// call this after resizing
// send in the new maximum width,height desired
function resizeAndRedraw(newMaxWidth,newMaxHeight){
// calc the global scaling factor that fits into the new size
// and also maintains the original aspect ratio
scale=Math.min((newMaxWidth/origWidth),(newMaxHeight/origHeight))
// resize the canvas while maintaining correct aspect ratio
canvas.width=origWidth*scale;
canvas.height=origHeight*scale;
// Note: changing the canvas element's width or height will
// erase the canvas so you must reissue all your drawing commands
drawLines(lines,i,table,xtotal,ytotal);
}
// call drawLines
function drawLines(lines,i,table,xtotal,ytotal){
// scale the canvas coordinate system to the current scale
// Note: This scales the coordinates used internally
// by canvas. It does not resize the canvas element
ctx.scale(s,s);
// now do your drawing commands
// You do not need to adjust your drawing coordinates because
// the Canvas will do that for you
var xIni;
var xFin;
var yIni;
var yFin;
xIni = (c.width*parseInt(lines[i][1])/xtotal);
yIni = (c.height*parseInt(lines[i][2])/ytotal);
xFin = (c.width*parseInt(lines[i][3])/xtotal);
yFin = (c.height*parseInt(lines[i][4])/ytotal);
ctx.beginPath();
ctx.moveTo(xIni,c.height-yIni);
ctx.lineTo(xFin,c.height-yFin);
ctx.lineWidth=4;
ctx.strokeStyle = colorAleatorio();
ctx.stroke();
// restore the context to it's unscaled state
ctx.scale(-s,-s);
}
Related
I'm drawing an image onto a canvas using drawImage. It's a PNG that is surrounded by transparent pixels, like this:
How can I detect a drawing move path in the transparent part of that image on the canvas? I want to detect if a user draws in a transparent part.
I am trying this tutorial and I did as showing in the tutorial.
var ctx = canvas.getContext('2d'),
img = new Image;
img.onload = draw;
img.src = "http://i.stack.imgur.com/UFBxY.png";
function draw() {
// draw original image in normal mode
ctx.drawImage(img, 10, 10);
}
<canvas id=canvas width=500 height=500></canvas>
Check it out full code on Github
Check it out live demo IonCanvas
To find out is a pixel is transparent get the pixel using ctx.getImageData and look at the alpha value.
Example
// assumes ctx is defined
// returns true if pixel is fully transparent
function isTransparent(x, y) { // x, y coordinate of pixel
return ctx.getImageData(x, y, 1, 1).data[3] === 0; // 4th byte is alpha
}
I'm working on a website that I need to crop images on different shapes .
I found lots of libraries and I've tested some ,but the main problem is they only can crop images on pre defined shapes like rectangle , circle . What I need is to crop images in any shapes
for example ,I've written a code the users can define their shapes ( by using Map Area ) and the exact shape is make ,Now I need to crop image or copy this area and make a new image from it .
I can use php ,jquery and other platforms
Could you help me to manage this problem ?
King Regards
Here's one way to do it using html5 Canvas:
1. Use the area element's coords to draw a path on the canvas.
// assume you've put the `coords` points as {x:,y:} objects into a points[] array:
ctx.beginPath();
ctx.moveTo(points[0].x,points[0].y);
for(var i=1;i<points.length;i++){
var p=points[i];
ctx.lineTo(points[i].x,points[i].y);
}
ctx.closePath();
2. Create a clipping path from the coords path you've just defined:
ctx.clip();
3. Draw the image on the canvas. The image will be clipped into your defined path:
ctx.drawImage(yourImageObject,0,0);
4. Create a second canvas sized to the clipping path size and use the clipping version of context.drawImage to draw just the clipped image onto the second canvas.
// see demo below for details
5. Create a new Image() from the second canvas...Mission Accomplished!
// create a new Image() from the second canvas
var clippedImage=new Image();
clippedImage.onload=function(){
// append the new image to the page
document.body.appendChild(clippedImage);
}
clippedImage.src=secondCanvas.toDataURL();
Annotated example code and a Demo:
// canvas related variables
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw,ch;
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
// set some canvas styles
ctx.strokeStyle='black';
// an array to hold user's click-points that define the clipping area
var points=[];
// load the image
var img=new Image();
img.crossOrigin='anonymous';
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/houses1.jpg";
function start(){
// resize canvas to fit the img
cw=canvas.width=img.width;
ch=canvas.height=img.height;
// draw the image at 25% opacity
drawImage(0.25);
// listen for mousedown and button clicks
$('#canvas').mousedown(function(e){handleMouseDown(e);});
$('#reset').click(function(){ points.length=0; drawImage(0.25); });
}
function handleMouseDown(e){
// tell the browser that we're handling this event
e.preventDefault();
e.stopPropagation();
// calculate mouseX & mouseY
mx=parseInt(e.clientX-offsetX);
my=parseInt(e.clientY-offsetY);
// push the clicked point to the points[] array
points.push({x:mx,y:my});
// show the user an outline of their current clipping path
outlineIt();
// if the user clicked back in the original circle
// then complete the clip
if(points.length>1){
var dx=mx-points[0].x;
var dy=my-points[0].y;
if(dx*dx+dy*dy<10*10){
clipIt();
}
}
}
// redraw the image at the specified opacity
function drawImage(alpha){
ctx.clearRect(0,0,cw,ch);
ctx.globalAlpha=alpha;
ctx.drawImage(img,0,0);
ctx.globalAlpha=1.00;
}
// show the current potential clipping path
function outlineIt(){
drawImage(0.25);
ctx.beginPath();
ctx.moveTo(points[0].x,points[0].y);
for(var i=0;i<points.length;i++){
ctx.lineTo(points[i].x,points[i].y);
}
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.arc(points[0].x,points[0].y,10,0,Math.PI*2);
ctx.closePath();
ctx.stroke();
}
// clip the selected path to a new canvas
function clipIt(){
// calculate the size of the user's clipping area
var minX=10000;
var minY=10000;
var maxX=-10000;
var maxY=-10000;
for(var i=1;i<points.length;i++){
var p=points[i];
if(p.x<minX){minX=p.x;}
if(p.y<minY){minY=p.y;}
if(p.x>maxX){maxX=p.x;}
if(p.y>maxY){maxY=p.y;}
}
var width=maxX-minX;
var height=maxY-minY;
// clip the image into the user's clipping area
ctx.save();
ctx.clearRect(0,0,cw,ch);
ctx.beginPath();
ctx.moveTo(points[0].x,points[0].y);
for(var i=1;i<points.length;i++){
var p=points[i];
ctx.lineTo(points[i].x,points[i].y);
}
ctx.closePath();
ctx.clip();
ctx.drawImage(img,0,0);
ctx.restore();
// create a new canvas
var c=document.createElement('canvas');
var cx=c.getContext('2d');
// resize the new canvas to the size of the clipping area
c.width=width;
c.height=height;
// draw the clipped image from the main canvas to the new canvas
cx.drawImage(canvas, minX,minY,width,height, 0,0,width,height);
// create a new Image() from the new canvas
var clippedImage=new Image();
clippedImage.onload=function(){
// append the new image to the page
document.body.appendChild(clippedImage);
}
clippedImage.src=c.toDataURL();
// clear the previous points
points.length=0;
// redraw the image on the main canvas for further clipping
drawImage(0.25);
}
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>
<h4>Click to outline clipping region.<br>Click back in starting circle to complete the clip.</h4>
<button id=reset>Reset clipping path</button><br>
<canvas id="canvas" width=400 height=300></canvas>
<p>Clipped images by user</p>
One way I can think of is with an animated svg, but there is probably a better way. What would you do if you had to animate these wavy blobs (mobile compatible)
Link to the only pin I've found similar
var wave = document.createElement("div");
wave.className += " wave";
docFrag.appendChild(wave);
wave.style.left = i * waveWidth + "px";
wave.style.webkitAnimationDelay = (i / 100) + "s";
Touch interaction would be nice too. Would there be any problems with canvas stuff ?
Here's an implementation of #DA.'s good answer:
var canvas=document.getElementById('canvas');
var ctx=canvas.getContext('2d');
var cw=canvas.width;
var ch=canvas.height;
ctx.textAlign='center';
ctx.textBaseline='middle';
ctx.font='16px verdana';
ctx.lineWidth=5;
ctx.strokeStyle='white';
ctx.fillStyle='white';
var offsetX=0;
var bk=makeWave(canvas.width,canvas.height-120,10,2,'lightskyblue','cornflowerblue');
requestAnimationFrame(animate);
function animate(time){
ctx.clearRect(0,0,cw,ch);
ctx.drawImage(bk,offsetX,0);
ctx.fillStyle='white';
ctx.font='18px verdana';
ctx.fillText('Multiple Lists',cw/2,30);
ctx.strokeRect(cw/2-50,85,100,50);
ctx.fillStyle='gray';
ctx.font='12px verdana';
ctx.fillText('You can create and save multiple ...',cw/2,250);
offsetX-=1;
if(offsetX< -bk.width/2){offsetX=0;}
requestAnimationFrame(animate);
}
function makeWave(width,midWaveY,amplitude,wavesPerWidth,grad0,grad1){
var PI2=Math.PI*2;
var totValue=PI2*wavesPerWidth;
var c=document.createElement('canvas');
var ctx=c.getContext('2d');
c.width=width*2;
c.height=midWaveY+amplitude;
var grad=ctx.createLinearGradient(0,0,0,midWaveY);
grad.addColorStop(0.00,grad0);
grad.addColorStop(1.00,grad1);
//
ctx.beginPath();
ctx.moveTo(0,0);
for (x=0;x<=200;x++) {
var n=totValue*x/100;
ctx.lineTo(width*x/100,Math.sin(n)*amplitude+midWaveY);
}
ctx.lineTo(c.width,0);
ctx.closePath();
ctx.fillStyle=grad;
ctx.fill();
return(c);
}
body{ background-color:white; }
canvas{border:1px solid red; margin:0 auto; }
<canvas id=canvas width=300 height=300></canvas>
I'd make the wave a PNG (bottom solid gray, top transparent). Place it in a div twice the width of the card, and place that in a div the width of the card (this second div is the 'mask').
Then via CSS, have the nested give transform on the x axis to animate it sideways.
You shouldn't need any JS for this.
I would do this:
on page load create an off-screen canvas (just set display: none)
in a for loop compute the wave:
clear with transparency
paint only the white part because the colored part has a gradient
after each paint, get the PNG data out of the canvas and store it in an array
after the loop you will have an array of PNG images (frames)
cycle through those frames without recomputing the wave over and over again
This requires the wave to have a period that is affine to the number of frames you take (say a 2 second animation at 10 Hz would require 20 frames to be cyclic)
To be honest, you could store that server-side and just download it, without computing it client-side. Those PNG images would be very tiny because there isn't any color involved (just transparent/white/alpha channel). There are optimal settings for this, I guesstimate some 1KB per frame would suffice, that's a tiny 20 KB of images).
I want to plot x and y coordinates on an image by drawing a circle around the point.
I am getting the image from the server as an array buffer. After the image is displayed i need to mark the corners in the image using coordinates sent as a json from a service.
How can i do this using javascript jquery?
I was thinking of doing the same by overlaying the image with a canvas layer.
How can i implement this?
I have tried the below method but the points were getting plotted outside the image
jQuery('#plotCoordinates').on('click',function(){
jQuery.getJSON( "plot.json", function( response ) {
console.log("response >> " ,response);
var imageCanvas = $('#imageCan');
jQuery.each(response,function(i,obj){
console.log('obj >> ',obj);
point = $('<div class="plot-point"></div>');
x = obj.x,
y = obj.y;
point.css({
left: x + "px",
top: y + "px"
});
point.appendTo(imageCanvas);
});
});
});
You can certainly do it using a canvas.Do the following steps-->
Draw image on canvas as image size=canvas size.
Now, overlay another canvas with a higher z-index exactly on the previous canvas(canvas is transparent by default).
Now use the coordinates of the points you fetched using json to mark the corners in the image by plotting them on this overlayed canvas.
Then you can draw circles around these points on the upper canvas using simple context functions.
As canvas size is same as image size these coordinates will exactly coincide and you can achieve want you want :).In short you draw your circles and markings on the upper canvas and simply draw image on the lower canvas.Easy huhh??:)
Note:Both the canvases and the image itself should be of equal dimensions to exactly coincide.
Have a look at this example https://jsfiddle.net/rbrv949d/
<canvas id="c" style="z-index: 1;"></canvas>
<canvas id="cover" style="z-index: 2;"></canvas>
JS onload
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
var canover=document.getElementById('cover');
var ctxover = canvas.getContext('2d');
// Create an image element
var img = new Image();
// When the image is loaded, draw it
img.onload = function () {
ctx.drawImage(img, 0, 0);
ctxover.fillRect(0,0,10,10);
ctxover.fillRect(0,20,10,10);
}
// Specify the src to load the image
img.src = "http://www.experts-exchange.com/images/experts-exchange/experts-exchange-logo.png";
canvas.width=img.width;
canvas.height=img.height;
canover.width=img.width;
canover.height=img.height;
Here , ctxover.fillRect(xposition,yposition,widthinpixels,heightinpixels)
In your case,the xposition and yposition are the one fetched from json.
You can also draw those markings on the same canvas without using the overlayed canvas.Its upto you.Provided if you clear this canvas those markings will also get cleared unlike the other case :)
I loaded picture to canvas and draw couple points within this very simple function:
var addPoint = function($x, $y, $context) {
$context.fillStyle="#FF0000";
$context.fillRect($x, $y, 3, 3);
$("#points").append(
$("<li>").append("Point nr "+ totalPoints()).append(
"<a href='#' class='remove_point' data-x='"+$x+"' data-y='"+$y+"'>[delete]</a>"
)
);
bindRemove($context);
};
I would like to add zoom feature. I suppose that I need to use scale() function on my canvas context to achieve my goal. Problem is I want to keep all points (rectangles) drawn on canvas before. I stored those points on simple ul list. After executing scale() I need to redraw canvas and probably will loose those points. After zoom in/out coords for them are different and needs to be calculated again.
How I can zoom my canvas and keep previously added points?
Yes, you can store all your rectangle definitions using javascript objects inside an array.
var rects=[];
rects.push({x:20,y:20,width:25,height:15,color:'red'});
rects.push({x:75,y:100,width:50,height:35,color:'green'});
rects.push({x:150,y:75,width:40,height:75,color:'blue'});
rects.push({x:100,y:225,width:50,height:50,color:'gold'});
Then to zoom you can:
clear the canvas
scale the canvas with context.scale
use the rectangle objects to redraw all your rectangles
unscale the canvas in preparation for any future drawings
Here's example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var scale=1.00;
var rects=[];
rects.push({x:20,y:20,width:25,height:15,color:'red'});
rects.push({x:75,y:100,width:50,height:35,color:'green'});
rects.push({x:150,y:75,width:40,height:75,color:'blue'});
rects.push({x:100,y:225,width:50,height:50,color:'gold'});
drawRects();
//
function drawRects(){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.scale(scale,scale);
for(var i=0;i<rects.length;i++){
var r=rects[i];
ctx.fillStyle=r.color;
ctx.fillRect(r.x,r.y,r.width*scale,r.height*scale);
}
ctx.setTransform(1,0,0,1,0,0);
}
//
canvas.addEventListener('DOMMouseScroll',handleScroll,false);
canvas.addEventListener('mousewheel',handleScroll,false);
//
function handleScroll(e){
e.preventDefault();
e.stopPropagation();
var delta=e.wheelDelta?e.wheelDelta/30:e.detail?-e.detail:0;
if (delta){
scale+=(delta>=0)?.01:-.01;
drawRects();
}
};
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>
<h4>Click in Canvas then use mousewheel to zoom</h4>
<canvas id="canvas" width=400 height=400></canvas><br>