Partially filling a rectangle in HTML5 - javascript

I have a canvas in which I want to draw multiple rectangles (of varying sizes).
These rectangles have to be filled or emptied over time, be it automatically or by input from the user.
The problem is that I have never programmed HTML5 before and don't know how to partially fill a rectangle or increase the fill (through something like rectangle.backgroundHeight++).
Here is my code:
<canvas id="myCanvas" height="1000" width="1000" />
<script>
window.addEventListener('load', function () {
var ctx = document.getElementById('myCanvas').getContext('2d');
var interval = setInterval(function() {
return function () {
// Draw large fuel tanks
var fuelA = ctx.rect(150, 60, 110, 130);
var fuelB = ctx.rect(600, 60, 110, 130);
// Draw small fuel tanks
var fuelC = ctx.rect(40, 300, 60, 130);
var fuelD = ctx.rect(300, 300, 60, 130);
var fuelE = ctx.rect(500, 300, 60, 130);
var fuelF = ctx.rect(750, 300, 60, 130);
ctx.stroke();
};
}(), 1000/25);
}, false);

While programming the canvas is a great skill to have, an animation like this can be done completely in CSS, using transitions:
var liquid= document.querySelector('.liquid');
document.querySelector('#bfill').onclick= function() {
liquid.style.height = '100%';
};
document.querySelector('#bempty').onclick= function() {
liquid.style.height = '0%';
};
.tank {
position: absolute;
left: 20px;
top: 40px;
height: 100px;
width: 50px;
border: 1px solid black;
}
.liquid {
transition: height 3s;
position: absolute;
left: 0px;
bottom: 0px;
height: 20%;
width: 100%;
background: blue;
}
<button id="bfill">Fill the tank</button>
<button id="bempty">Empty the tank</button>
<div class="tank">
<div class="liquid"></div>
</div>

I do not think that is possible by using one rectangle, as it can only be filled by a single color, or a gradient - and there is no option for partial fill.
However you may draw a rectangle with only strokes and no fill, and another on top of it, which acts as a fill for the first rectangle. You can size this however you want to.
Edit: Well it seems, you can achieve this with gradients, however I can't really tell which is the better solution.

You could use a gradient to get an effect of partially filling e.g filling a rectangle 50%.
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var rec = ctx.rect(20, 20, 150, 100);
var grd = ctx.createLinearGradient(0, 0, 190, 0);
grd.addColorStop(0, "black");
grd.addColorStop(0.5, "black");
grd.addColorStop(0.5, "white");
grd.addColorStop(1, "white");
ctx.fillStyle = grd;
ctx.fill();
ctx.strokeStyle = 'black';
ctx.stroke();
<canvas id="myCanvas" />

Related

Canvas radial gradient to match css radial gradient

I've created two radial gradients that I would expect to be very similar: one using canvas operations and one using the css linear gradient. However, they end up looking very different. Is there any way to get the canvas gradient to match the css gradient, or is this simply an implementation difference that is too hard to work around?
const canvas = document.getElementById('bar');
const context = canvas.getContext('2d');
canvas.width = 500;
canvas.height = 500;
const gradient = context.createRadialGradient(250, 250, 0, 250, 250, 250);
// Add the color stops
gradient.addColorStop(0, 'rgba(246,238,25,1)');
gradient.addColorStop(1, 'rgba(17,94,222,0.3)');
// Set the fill style and draw a rectangle
context.fillStyle = gradient;
context.fillRect(0, 0, 500, 500);
#foo {
width: 500px;
height: 500px;
background: radial-gradient(circle, rgba(246, 238, 25, 1) 0, rgba(17, 94, 222, 0.3) 100%);
}
<div id="foo">
</div>
<canvas id="bar"></canvas>

Change canvas background color to transparent

I have a text under canvas, i want to show it when i am erasing background color of canvas. Now it is red, when i wrote transparent it does not work. I need to show that text when i draw with the mouse. I tried with rgba too, but is was not working.
please help me if you can
enter code here
var cont = document.getElementById("spots"), // UI elements
canvas = document.getElementById("canvas"),
alpha = document.getElementById("alpha"),
modes = document.getElementById("modes"),
ctx = canvas.getContext("2d"),
isDown = false, // defaults
color = "red";
// set up color palette using a custom "Spot" object
// This will use a callback function when it is clicked, to
// change current color
function Spot(color, cont, callback) {
var div = document.createElement("div");
div.style.cssText = "width:50px;height:50px;border:1px solid #000;margin:0 1px 1px 0;background:" + color;
div.onclick = function() {callback(color)};
cont.appendChild(div);
}
// add some spot colors to our palette container
new Spot(color, cont, setColor);
// this will set current fill style based on spot clicked
function setColor(c) {ctx.fillStyle = c}
// setup defaults
ctx.fillStyle = color;
ctx.globalAlpha = 1;
// events
canvas.onmousedown = function() {isDown = true};
window.onmouseup = function() {isDown = false};
window.onmousemove = function(e) {
if (!isDown) return;
var r = canvas.getBoundingClientRect(),
x = e.clientX - r.left,
y = e.clientY - r.top;
ctx.beginPath();
ctx.arc(x, y, 25, 0, 2*Math.PI);
ctx.fill();
};
.main-content{
position: relative;
width: 300px;
height: 300px;
}
.main-text{
position: absolute;
right: 0;
left: 0;
text-align: center;
z-index: 8;
font-size: 35px;
}
#canvas{
background-color: green;
position: absolute;
z-index: 9;
}
<div class="main-content">
<p class="main-text">You Won!!!</p>
<canvas id="canvas" width=300 height=300 style="border: 1px solid green;"></canvas>
<div id="spots"></div>
</div>
I think you would like to get something like the solution in the snippet below.
var cont = document.getElementById("spots"), // UI elements
canvas = document.getElementById("canvas"),
alpha = document.getElementById("alpha"),
modes = document.getElementById("modes"),
ctx = canvas.getContext("2d"),
isDown = false, // defaults
color = "green";
// set up color palette using a custom "Spot" object
// This will use a callback function when it is clicked, to
// change current color
function Spot(color, cont, callback) {
var div = document.createElement("div");
div.style.cssText = "width:50px;height:50px;border:1px solid #000;margin:0 1px 1px 0;background:" + color;
div.onclick = function() {
callback(color)
};
cont.appendChild(div);
}
// add some spot colors to our palette container
new Spot(color, cont, setColor);
// this will set current fill style based on spot clicked
function setColor(c) {
ctx.fillStyle = c
}
// setup defaults
ctx.fillStyle = color;
ctx.globalAlpha = 1;
// create a rectangle using canvas functions, not CSS
// background color.
const createRect = (ctx, width, height) => {
ctx.fillRect(0, 0, width, height)
}
createRect(ctx, 300, 300)
// events
canvas.onmousedown = function() {
isDown = true
};
window.onmouseup = function() {
isDown = false
};
window.onmousemove = function(e) {
if (!isDown) return;
var r = canvas.getBoundingClientRect(),
x = e.clientX - r.left,
y = e.clientY - r.top;
// you needed a bit more code here:
ctx.fillStyle = "rgba(255, 255, 255, 0.5)"
ctx.save();
ctx.globalCompositeOperation = 'destination-out';
ctx.beginPath();
ctx.arc(x, y, 25, 0, 2 * Math.PI, false);
ctx.fill();
ctx.restore();
};
.main-content {
position: relative;
width: 300px;
height: 300px;
}
.main-text {
position: absolute;
right: 0;
left: 0;
text-align: center;
z-index: 8;
font-size: 35px;
}
#canvas {
/*background-color: green;*/
position: absolute;
z-index: 9;
}
<div class="main-content">
<p class="main-text">You Won!!!</p>
<canvas id="canvas" width=300 height=300 style="border: 1px solid green;"></canvas>
<div id="spots"></div>
</div>
About canvas global compositions: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
You are halfway there, but there are a couple of things to change.
What you're trying to do is to change the appearance of a css property through the canvas, which does not work like that. You also can not alter the transparency of the canvas, however there are solutions to your case, and it is very simple.
You need to apply a background color to your canvas, then remove the pixels by using the exact same drawing function you have, then you set an extra property called globalCompositeOperation, which is basically the "BlendMode" of photoshop.
So here's your updated code:
var cont = document.getElementById("spots"), // UI elements
canvas = document.getElementById("canvas"),
alpha = document.getElementById("alpha"),
modes = document.getElementById("modes"),
ctx = canvas.getContext("2d"),
isDown = false, // defaults
color = "red";
// set up color palette using a custom "Spot" object
// This will use a callback function when it is clicked, to
// change current color
function Spot(color, cont, callback) {
var div = document.createElement("div");
div.style.cssText = "width:50px;height:50px;border:1px solid #000;margin:0 1px 1px 0;background:" + color;
div.onclick = function() {callback(color)};
cont.appendChild(div);
}
// add some spot colors to our palette container
//new Spot(color, cont, setColor);
// this will set current fill style based on spot clicked
function setColor(c) {ctx.fillStyle = c}
// setup defaults
ctx.fillStyle = color;
ctx.globalAlpha = 1;
// draw the background
ctx.fillRect(0, 0, 300, 300);
// add the 'blend mode effect'
ctx.globalCompositeOperation = 'destination-out';
// events
canvas.onmousedown = function() {isDown = true};
window.onmouseup = function() {isDown = false};
window.onmousemove = function(e) {
if (!isDown) return;
var r = canvas.getBoundingClientRect(),
x = e.clientX - r.left,
y = e.clientY - r.top;
ctx.beginPath();
ctx.arc(x, y, 25, 0, 2*Math.PI);
ctx.fill();
};
.main-content{
position: relative;
width: 300px;
height: 300px;
background: blue;
}
.main-text{
position: absolute;
right: 0;
left: 0;
text-align: center;
z-index: 8;
font-size: 35px
}
#canvas{
position: absolute;
z-index: 9;
}
<div class="main-content">
<p class="main-text">You Won!!!</p>
<canvas id="canvas" width=300 height=300 style="border: 1px solid green;"></canvas>
<div id="spots"></div>
</div>
Apart from that tiny change on the JS, I also changed the order of z-index on the CSS. Good luck

deleting a circle made in HTML5 Canvas [duplicate]

This question already has answers here:
Erasing previously drawn lines on an HTML5 canvas
(5 answers)
Closed 4 years ago.
I've made a canvas where I draw shapes on. When I want to delete them, I basically create the same shape again but it's white, so I don't delete any other shapes (saved the x and y coordinate, so nothing to worry there)
ctx.fillStyle="#FFFFFF";
ctx.strokeStyle="#FFFFFF";
ctx.beginPath();
ctx.arc(x, y, 40, 0, 2 * Math.PI);
ctx.fill();
The problem is that on some shapes there is still a remaining black rest, that I can't get rid of (it's even worse on other shapes)
What Am I missing?
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(50,50 , 40, 0, 2 * Math.PI);
ctx.fill();
ctx.fillStyle="#FFFFFF";
ctx.strokeStyle="#FFFFFF";
ctx.beginPath();
ctx.arc(50,50 , 40, 0, 2 * Math.PI);
ctx.fill();
<canvas id="myCanvas" width="1000" height=600 style="border: 1px solid #000000;">
</canvas>
EDIT: https://jsfiddle.net/sfj5y091/3/
EDIT 2:
I solved this problem in the end, by completely redrawing all the shapes after one shape was deleted in the system, which even enabled the deletion of shapes that overlapped without destroying the other shape
The residue is caused by smoothing or anti-aliasing.
To draw the black circle on the initially white background, an "aura" of gray pixels is drawn at the edge of the 40-pixel-radius circle to give it a smooth appearance, and that "aura" is a tiny bit bigger than what you planned to draw.
If you then draw a white 40-pixel-radius circle on top of that, it blends the new white edge pixels with what is now a non-white background. The result is lighter gray pixels, and not white pixels.
If your only option is still to paint over old pixels, then you'll have to use a slightly bigger radius for the white circle:
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(50, 50, 40, 0, 2 * Math.PI); // radius of 40
ctx.fill();
ctx.fillStyle = "#FFFFFF";
ctx.strokeStyle = "#FFFFFF";
ctx.beginPath();
ctx.arc(50, 50, 41, 0, 2 * Math.PI); // radius of 41
ctx.fill();
<canvas id="myCanvas" width="150" height="150" style="border: 1px solid #000000;">
</canvas>
<br /> Nothing to see here ;-)
For more on anti-aliasing, see e.g. Can I turn off antialiasing on an HTML <canvas> element?
Double buffering in action
Try
step1();
setTimeout(function () {
step2();
setTimeout(function () {
step3();
}, 1000);
}, 1000);
function step1() {
clearCanvas('myCanvas1');
drawShape('myCanvas1'
,{type:"circle", strokeStyle:"#000000", fillStyle:"#000000", radious:40, x:50, y:50});
};
function step2() {
clearCanvas('myCanvas2');
showOtherCanvas('myCanvas2', 'myCanvas1');
};
function step3() {
clearCanvas('myCanvas1');
drawShape('myCanvas1'
,{type:"circle", strokeStyle:"#000000", fillStyle:"#000000", radious:40, x:50, y:50});
showOtherCanvas('myCanvas1', 'myCanvas2');
};
function drawCircle (canvasID, info) {
var canvas = document.getElementById(canvasID);
var ctx = canvas.getContext('2d');
ctx.fillStyle=info.fillStyle;
ctx.strokeStyle=info.strokeStyle;
ctx.beginPath();
ctx.arc(info.x, info.y, info.radious, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.arc(info.x, info.y, info.radious, 0, 2 * Math.PI);
ctx.stroke();
}
function showOtherCanvas(cnv1, cnv2) {
var c1 = document.getElementById(cnv1);
var c2 = document.getElementById(cnv2);
c1.style['z-index'] = 3;
c2.style['z-index'] = 1;
c1.style['z-index'] = 2;
}
function clearCanvas(canvasID) {
var canvas = document.getElementById(canvasID);
var ctx = canvas.getContext('2d');
ctx.fillStyle="#FFFFFF";
ctx.strokeStyle="#FFFFFF";
ctx.fillRect(0,0,640,400);
}
function drawShape (canvasID, info) {
switch (info.type) {
case "circle" : drawCircle(canvasID, info);
}
}
<canvas id="myCanvas2" width="640" height="400"
style="border: 1px solid #000000; position: absolute; top: 10; left: 10; z-index:1">
</canvas>
<canvas id="myCanvas1" width="640" height="400"
style="border: 1px solid #000000; position: absolute; top: 10; left: 10; z-index:2">
</canvas>
The change is so fast you won't see any flicker.

Make canvas with shape responsive

I created a canvas with a circle inside and filled it with a gradient.
I am not sure how to make the canvas as well as the circle inside the canvas responsive to the screen size.
I tried using vh and vw for the width and height of the canvas. But when I change the height or width of the window, the circle looks either too long or too wide.
Question:
I want the entire circle to decrease in size when the window size is reduced. I am not sure how to do this.
Code:
var c = document.getElementById('canvassun');
var ctx = c.getContext('2d');
var grd = ctx.createRadialGradient(85, 85, 20, 85, 85, 70);
grd.addColorStop(0, 'red');
grd.addColorStop(0.5, 'orange');
grd.addColorStop(0.8, 'yellow');
grd.addColorStop(1, 'white');
ctx.beginPath();
ctx.fillStyle = grd;
ctx.arc(90, 90, 70, 0, 2 * Math.PI);
ctx.fill();
ctx.closePath;
#canvassun {
height: 30vh;
width: 14vw;
border: 1px solid black;
margin: 0 auto;
display: block;
margin-top: 18%;
}
<canvas id="canvassun" width=170 height=170></canvas>
Listen to window resize event and redraw the canvas
function draw() {
var c = document.getElementById('canvassun');
var ctx = c.getContext('2d');
var grd = ctx.createRadialGradient(85, 85, 20, 85, 85, 70);
grd.addColorStop(0, 'red');
grd.addColorStop(0.5, 'orange');
grd.addColorStop(0.8, 'yellow');
grd.addColorStop(1, 'white');
ctx.beginPath();
ctx.fillStyle = grd;
ctx.arc(90, 90, 70, 0, 2 * Math.PI);
ctx.fill();
ctx.closePath;
}
window.addEventListener("resize", draw);
Note Probably you need to debounce the draw function for performance

How to apply canvas to whole document or how to draw on whole document?

I have .aspx page and i want to draw on whole document using JS.I'm newbie in JS...
For example this code(in my aspx) allow me to draw on 200x200 area:
<canvas id="canvas" width="200" height="200"></canvas>
<script type ="text/javascript">
$(document).ready(function () {
ctx = document.getElementById('canvas').getContext('2d');
ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
ctx.fillRect(30, 30, 55, 50);
});
</script>
How to draw on whole document?Because I can't write something like:
<canvas id="canvas" $(document).width() $(document).height()></canvas>
I want to draw a transparent rectangle on whole document and see the page content behind.
Here is the solution(thnx to kirilloid):
CSS:
#canvas {
position: absolute;
left: 0;
top: 0;
}
JS:
function updateCanvas(width,height) {
var $canvas = $("#canvas");
$canvas.attr('width', $(document).width())
$canvas.attr('height', $(document).height());
var ctx = $canvas.get(0).getContext('2d');
ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
ctx.fillRect(0, 0, width, height/2);
ctx.fillStyle = "rgba(0, 200, 0, 0.5)";
ctx.fillRect(0, height/2, width, height);
}
$(document).ready(function () {
updateCanvas($(document).width(), $(document).height())
});
There's no way to draw on the page itself. You'll need to make canvas fit whole window and update its sizes (and redraw) on window resize.
If you change its sizes in CSS, canvas is only stretched like images do. If you change width and height attributes, then it's cleared.
CSS:
#canvas {
position: fixed;
left: 0;
top: 0;
}
The code:
function updateCanvas() {
var $canvas = $("#canvas");
$canvas.attr('width', $(document).width())
$canvas.attr('height', $(document).height());
var ctx = $canvas.get(0).getContext('2d');
ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
ctx.fillRect(30, 30, 55, 50);
}
$(window).on('resize', updateCanvas); // maybe, add some thresholding
updateCanvas();
UPD: Just came up with more performant solution: set canvas sizes from window.screen and put it into overflow: hidden container fitting whole window. Then you won't need to redraw or resize canvas at all (multi-display users can still be a problem).

Categories