Is it possible to mask / crop a html element group dynamically? - html

Is there a way use javascript, css, canvas or svg to create an animated mask (can be just binary).
The closest I've come is using this:
var data = canvas.toDataURL();
$('#masked').css("-webkit-mask-image","url("+data+")");
But this only works in chrome and safari and is a bit buggy.
Is there a way of using css to draw a really simple triangular mask?

If its going to be dynamic you can do it with canvas, draw the path you want and fill it, by default canvas is transparent, so any of the parts not filled will show the element underneath. However if its going to be a static mask I suggest just using a png.
Live Demo
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
canvas.width = canvas.height = 200;
// make a path for a triangle
ctx.beginPath();
ctx.moveTo(0,0);
ctx.lineTo(200,0);
ctx.lineTo(200,200);
ctx.lineTo(100,50);
ctx.lineTo(0,200);
ctx.lineTo(0,0);
ctx.fill();

Related

Building an web based image annotation tool - saving annotations to localStorage

I am building a web application for annotating images. The work flow is as follows:
Select a project - using : action = list all sub-projects
Click on a sub-project : action = fetch all the images within-sub project
Display the images as a horizontal scrollable thumbnail gallery
Onclick image thumbnail from the gallery, display the larger image for annotation.
I am using canvas to display larger image. I have used another canvas as a layer to the first one, and I am able to draw rectangles using mouse over regions of interest. I am saving it locally. However, when I move on to the next image, the rectangle also gets carried to the next image.
My question is, instead of using just one layer, do I have to dynamically create as many canvas layers as I have in the annotation dataset. I am not sure because in each sub project I have around 8000-9000 images. Though I wont be annotating on all of them, still creating as many canvases as layers doesn't really sound good for me.
The following is the code:
HTML Canvas
<div class="body"> <!-- Canvas to display images begins -->
<canvas id="iriscanvas" width=700px height=700px style="position:absolute;margin:50px 0 0 0;z-index:1"></canvas>
<canvas id="regncanvas" onclick="draw(this, event)" width=700px height=700px style="position:absolute;margin:50px 0 0 0;z-index:2"></canvas>
</div> <!-- Canvas to display images ends -->
Step 4 given above: OnClick display thumbnail
function clickedImage(clicked_id) {
var clickedImg = document.getElementById(clicked_id).src;
var clickedImg = clickedImg.replace(/^.*[\\\/]/, '');
localStorage.setItem("clickedImg", clickedImg);
var canvas = document.getElementById("iriscanvas");
var ctx = canvas.getContext("2d");
var thumbNails = document.getElementById("loaded_img_panel");
var pic = new Image();
pic.onload = function() {
ctx.drawImage(pic, 0,0)
}
thumbNails.addEventListener('click', function(event) {
pic.src = event.target.src;
});
}
Draw rectangles on second layer of canvas
window.onload=function(){
c=document.getElementById("regncanvas");
if (c) initCanvas(c);
};
function initCanvas(canvas){
// Load last canvas
loadLastCanvas(canvas);
}
function draw(canvas, event){
// Draw at random place
ctx=c.getContext("2d");
ctx.fillStyle="#ff0000";
ctx.beginPath();
ctx.fillRect (250*Math.random()+1, 220*Math.random()+1, 40, 30);
ctx.closePath();
ctx.fill();
// Save canvas
saveCanvas(canvas);
}
function saveCanvas(c){
localStorage['lastImgURI']=c.toDataURL("image/png");
}
function loadLastCanvas(c){
if (!localStorage['lastImgURI']) return;
img = new Image();
img.onload = function() {
ctx=c.getContext("2d");
ctx.drawImage(img, 0, 0, img.width, img.height);
};
img.src= localStorage['lastImgURI'];
}
Can someone guide me please?
The following is a screen grab of my application:
I have developed OCLAVI which is an image annotation tool with loads of features. It's still in beta but just after 3 weeks of release, it is gaining attraction quickly.
I have few advises for you.
HTML Canvas follow draw and forget strategy and every time redrawing the image is not a good idea. Be it 10 images or 10k, you should have one canvas for drawing the image and one canvas for drawing the shapes. Image canvas need be touched only when the image changes. Different shapes can share the same canvas.
You should integrate a data storage. Local storage is clearly not a good option to store this amount of data (especially if you have a team member who also would be annotating on the same image dataset.)
Isolate the code to a separate-separate file according to the shape. It will be very handy when you will think of adding support for Circle, Polygon, Cuboidal, Point interactions. Trust me following OOPs concepts will relive you from a lot of pain.
In terms of complexity
zooming with coordinates is easy
move with coordinates is of medium level difficulty
but you need to think with pen and paper to implement move on a zoomed image canvas (P.S. take care of the canvas flickering when the image moves
). How much the image can move in each direction also need to be calculated.
Take care of the image to canvas dimension ratio because at the end you need to have the coordinates scaled down to image level.
If your canvas size vs image size ratio is 1:1 then your job is simplified.
But this won't happen always because some images might be very small or very large to directly fit in window screen and you need to scale up and down accordingly.
The complexity increases if you like to use percentage width and height for canvas and your other team member annotating the image has a different screen size. So he drawing something will look something else on your screen.

clearRect with fade to transparent

I've drawn some shapes to my transparent canvas.
Lots of animated circles to be precise.
I would like to fade the bottom edge of my canvas to fully transparent, from transparent (but with the shapes).
Edit
See the image below; I'm after the effect on the right, as opposed to what I currently have which is a cut in half circle using clearRect()
Anyone have an ideas?
You can create a gradient with alpha in it (using rgba() syntax) and apply it to the stroke. Something like:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
gradient.addColorStop(0,"#f00");
gradient.addColorStop(0.75,"rgba(255, 0, 0, 0)");
ctx.strokeStyle = gradient;
ctx.lineWidth = 10;
ctx.beginPath();
ctx.arc(150, 150, 100, 0, Math.PI * 2);
ctx.stroke();
Here's a quick demo for it: http://codepen.io/codingcampbell/pen/wMeowa
Changing the 0.75 value in addColorStop is what would affect where the alpha part of the gradient comes in. Animating it might be tricky, I think you need to create a new gradient for each stop value because you can't modify existing stops (but you can keep those gradients in memory and share them with all your shapes)
The easiest way would be to make a small vertical gradient and apply it as an eraser on the full circle. To make the effect that it's being animated, just move it upwards on every frame (and start from below the circle).

Canvas gradient performance

I am currently programming a little game using canvas. For the game I need some kind of fog which hides the most part of the map and only a small area around the player should be visible. Therfor I use a second canvas to overlay the one where the game takes place and fill it with a gradient (from transparent to black):
function drawFog(){
fogc.clearRect(0,0,700,600);
// Create gradient
var grd=fogc.createRadialGradient(player.getPosX(),player.getPosY(),0,player.getPosX(),player.getPosY(),100);
grd.addColorStop(0,"rgba(50,50,50,0)");
grd.addColorStop(1,"black");
// Fill with gradient
fogc.fillStyle=grd;
fogc.fillRect(0,0,700,600);
}
Unfortunatly this is causing huge perfomance problems since it will be redrawn for every frame.
I wanted to ask if there might be a better solution to achieve the same effect with a better performance.
Cache the gradient to an off-screen canvas then draw in the canvas with drawImage() instead:
Create an off-screen canvas the size of the fog
Draw in the gradient
Use off-screen canvas as an image when you need the fog.
This way the processing creating and calculating the gradient is eliminated. Drawing an image is basically a copy operation (there is a little bit more, but performance is very good).
function createFog(player) {
// Create off-screen canvas and gradient
var fogCanvas = document.createElement('canvas'),
ctx = fogCanvas.getContext('2d'),
grd = fogc.createRadialGradient(player.getPosX(),
player.getPosY(),
0,player.getPosX(),
player.getPosY(),100);
fogCanvas.width = 700;
fogCanvas.height = 700;
grd.addColorStop(0,"rgba(50,50,50,0)");
grd.addColorStop(1,"black");
// Fill with gradient
ctx.fillStyle = grd;
ctx.fillRect(0,0,700,600);
return fogCanvas;
}
Now you can simply draw in the canvas returned from the above function instead of creating the gradient every time:
var fog = createFog(player);
ctx.drawImage(fog, x, y);

How draw lines between and fill on 4 points on an ASP.NET page?

Using GPS, I will know the latitude/longitude for 4 corners of a lot of land (I suppose it could be 5 or 6 so a polygon -- although will usually be a rectangle or parallelogram). Using DIV tags (with runat=server), I can mark the dots on an ASP.NET page pulling the data points from a database.
But, is there a way I can essentially turn these into shapes? I'm trying to map out a, for example, subdivision of plats on a web page. Ultimately, I'd like data to appear on mouseover, have unsold plats a different color, click on a plat and it navigates somewhere, etc).
But right now, I'm just trying to see if I can plot the shapes on a page.
Thanks very much for any guidance.
If you can count on your users having a modern browser, html5 <canvas> is probably what you want. You can just put a canvas on your page with
<canvas id="myCanvas" width="200" height="100"></canvas>
Then draw to it in javascript like so:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.fillStyle = '#f00';
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(0, 100);
ctx.lineTo(50, 100);
ctx.lineTo(50, 0);
ctx.closePath();
ctx.fill();
Adjust or add corners as needed.

How do I make a transparent canvas in html5?

How can I make a canvas transparent? I need to because I want to put two canvases on top of one another.
Canvases are transparent by default.
Try setting a page background image, and then put a canvas over it. If nothing is drawn on the canvas, you can fully see the page background.
Think of a canvas as like painting on a glass plate.
To clear a canvas after having drawn on it, just use clearRect:
const context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
I believe you are trying to do exactly what I just tried to do:
I want two stacked canvases... the bottom one has a static image and the top one contains animated sprites. Because of the animation, you need to clear the background of the top layer to transparent at the start of rendering every new frame. I finally found the answer: it's not using globalAlpha, and it's not using a rgba() color. The simple, effective answer is:
context.clearRect(0,0,width,height);
Iif you want a particular <canvas id="canvasID"> to be always transparent you just have to set
#canvasID{
opacity:0.5;
}
Instead, if you want some particular elements inside the canvas area to be transparent, you have to set transparency when you draw, i.e.
context.fillStyle = "rgba(0, 0, 200, 0.5)";
Just set the background of the canvas to transparent.
#canvasID{
background:transparent;
}
Paint your two canvases onto a third canvas.
I had this same problem and none of the solutions here solved my problem. I had one opaque canvas with another transparent canvas above it. The opaque canvas was completely invisible but the background of the page body was visible. The drawings from the transparent canvas on top were visible while the opaque canvas below it was not.
fillStyle might not be what you are looking for because it can't really clear the canvas; it will either paint it with a solid color or with a transparent color which doesn't paint anything.
The trick that did for me relies on an implementation detail about the <canvas></canvas>. They "reset" when resized (tested on Chrome and Firefox):
canvas.width = canvas.width
This phenomenon initially struck me as a very annoying behavior, but it also became the only way I know to hard reset the canvas.
If you're exporting your canvas, remember to export as png!!
Been there, failed at that xD
Here's a minimal proof of concept of the default transparency of canvases, and using position: absolute to stack them on top of each other:
const canvases = [...Array(4)]
.map(() => document.createElement("canvas"));
canvases.forEach((canvas, i) => {
document.body.appendChild(canvas);
const ctx = canvas.getContext("2d");
const saturation = 100 / canvases.length * (i + 1);
ctx.strokeStyle = `hsl(160, ${saturation}%, 60%)`;
ctx.lineWidth = 10;
ctx.strokeRect(i * 50 + 10, i * 15 + 10, 100, 80);
});
canvas {
position: absolute;
border: 1px solid black;
}
Can't comment the last answer but the fix is relatively easy. Just set the background color of your opaque canvas:
#canvas1 { background-color: black; } //opaque canvas
#canvas2 { ... } //transparent canvas
I'm not sure but it looks like that the background-color is inherited as transparent from the body.