How to Clear A Circular Area - JavaScript Canvas [duplicate] - javascript

Let's say I have the following code.
// Find out window height and width
wwidth = $(window).width();
wheight = $(window).height();
// Place Canvas over current Window
$("body").append($("<canvas id='test' style='position:absolute; top:0; left:0;'></canvas>"));
var context = document.getElementById("test").getContext("2d");
context.canvas.width = wwidth;
context.canvas.height = wheight;
// Paint the canvas black.
context.fillStyle = '#000';
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
context.fillRect(0, 0, context.canvas.width, context.canvas.height);
// On Mousemove, create "Flashlight" around the mouse, to see through the canvas
$(window).mousemove(function(event){
x = event.pageX;
y = event.pageY;
radius = 50;
context = document.getElementById("test").getContext("2d");
// Paint the canvas black. Instead it will draw it white?!
//context.fillStyle = '#000';
//context.clearRect(0, 0, context.canvas.width, context.canvas.height);
//context.fillRect(0, 0, context.canvas.width, context.canvas.height);
context.beginPath();
radialGradient = context.createRadialGradient(x, y, 1, x, y, radius);
radialGradient.addColorStop(0, 'rgba(255,255,255,1)');
radialGradient.addColorStop(1, 'rgba(0,0,0,0)');
context.globalCompositeOperation = "destination-out";
context.fillStyle = radialGradient;
context.arc(x, y, radius, 0, Math.PI*2, false);
context.fill();
context.closePath();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>Test</div>
which generates the following effect on mousemove:
How do I refill the canvas with black before the spotlight is drawn? I have already tried with what is in the commented-out code block, but it paints everything white.
EDIT: I dont want this effect over an image. Instead i would like to place the Canvas over the whole Webpage. ALso I want the Canvas to be always black and the mouse generates a Spotlight over its position, to see what is under the Canvas just as u can see in the picture, or in the Snippet where a div was placed in an empty html page with "Test" in it.

You can use compositing to create your flashlight effect:
Clear the canvas
Create a radial gradient to use as a reveal.
Fill the radial gradient.
Use source-atop compositing to draw the background image. The image will display only inside the radial gradient.
Use destination-over compositing to fill the canvas with black. The black will fill "behind" the existing radial-gradient-image.
Here's example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }
$("#canvas").mousemove(function(e){handleMouseMove(e);});
var radius=30;
var img=new Image();
img.onload=function(){
draw(150,150,30);
}
img.src='https://dl.dropboxusercontent.com/u/139992952/multple/annotateMe.jpg'
function draw(cx,cy,radius){
ctx.save();
ctx.clearRect(0,0,cw,ch);
var radialGradient = ctx.createRadialGradient(cx, cy, 1, cx, cy, radius);
radialGradient.addColorStop(0, 'rgba(0,0,0,1)');
radialGradient.addColorStop(.65, 'rgba(0,0,0,1)');
radialGradient.addColorStop(1, 'rgba(0,0,0,0)');
ctx.beginPath();
ctx.arc(cx,cy,radius,0,Math.PI*2);
ctx.fillStyle=radialGradient;
ctx.fill();
ctx.globalCompositeOperation='source-atop';
ctx.drawImage(img,0,0);
ctx.globalCompositeOperation='destination-over';
ctx.fillStyle='black';
ctx.fillRect(0,0,cw,ch);
ctx.restore();
}
function handleMouseMove(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
draw(mouseX,mouseY,30);
}
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>Move mouse to reveal image with "flashlight"</h4>
<canvas id="canvas" width=300 height=300></canvas>
If your spotlight radius will never change, here's a much faster method:
The speed is gained by caching the spotlight to a second canvas and then...
Draw the image on the canvas.
Draw the spotlight on the canvas.
Use fillRect to black out the 4 rectangles outside the spotlight.
Example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }
var radius=50;
var cover=document.createElement('canvas');
var cctx=cover.getContext('2d');
var size=radius*2+10;
cover.width=size;
cover.height=size;
cctx.fillRect(0,0,size,size);
var radialGradient = cctx.createRadialGradient(size/2, size/2, 1, size/2, size/2, radius);
radialGradient.addColorStop(0, 'rgba(0,0,0,1)');
radialGradient.addColorStop(.65, 'rgba(0,0,0,1)');
radialGradient.addColorStop(1, 'rgba(0,0,0,0)');
cctx.beginPath();
cctx.arc(size/2,size/2,size/2,0,Math.PI*2);
cctx.fillStyle=radialGradient;
cctx.globalCompositeOperation='destination-out';
cctx.fill();
var img=new Image();
img.onload=function(){
$("#canvas").mousemove(function(e){handleMouseMove(e);});
ctx.fillRect(0,0,cw,ch);
}
img.src='https://dl.dropboxusercontent.com/u/139992952/multple/annotateMe.jpg'
function drawCover(cx,cy){
var s=size/2;
ctx.clearRect(0,0,cw,ch);
ctx.drawImage(img,0,0);
ctx.drawImage(cover,cx-size/2,cy-size/2);
ctx.fillStyle='black';
ctx.fillRect(0,0,cx-s,ch);
ctx.fillRect(0,0,cw,cy-s);
ctx.fillRect(cx+s,0,cw-cx,ch);
ctx.fillRect(0,cy+s,cw,ch-cy);
}
function handleMouseMove(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
drawCover(mouseX,mouseY);
}
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>Move mouse to reveal image with "flashlight"</h4>
<canvas id="canvas" width=300 height=300></canvas>

You can achieve the spotlight effect by positioning a canvas directly over the image. Make the canvas the same size as the image and set the compositing operation to xor so that two black pixels drawn in the same place cancel each other out.
context.globalCompositeOperation = 'xor';
Now you can paint the canvas black and fill a black circle around the mouse cursor. The result is a hole in the black surface, showing the image underneath.
// Paint the canvas black.
context.fillStyle = '#000';
context.clearRect(0, 0, width, height);
context.fillRect(0, 0, width, height);
// Paint a black circle around x, y.
context.beginPath();
context.arc(x, y, spotlightRadius, 0, 2 * Math.PI);
context.fillStyle = '#000';
context.fill();
// With xor compositing, the result is a circular hole.
To make a spotlight with blurry edges, define a radial gradient centered on the mouse position and fill a square around it.
var gradient = context.createRadialGradient(x, y, 0, x, y, spotlightRadius);
gradient.addColorStop(0, 'rgba(0, 0, 0, 1)');
gradient.addColorStop(0.9, 'rgba(0, 0, 0, 1)');
gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
context.fillStyle = gradient;
context.fillRect(x - spotlightRadius, y - spotlightRadius,
2 * spotlightRadius, 2 * spotlightRadius);
The following snippet demonstrates both approaches using pure JavaScript. To change from a crisp-edged spotlight to a blurry-edged spotlight, click on the checkbox above the image.
function getOffset(element, ancestor) {
var left = 0,
top = 0;
while (element != ancestor) {
left += element.offsetLeft;
top += element.offsetTop;
element = element.parentNode;
}
return { left: left, top: top };
}
function getMousePosition(event) {
event = event || window.event;
if (event.pageX !== undefined) {
return { x: event.pageX, y: event.pageY };
}
return {
x: event.clientX + document.body.scrollLeft +
document.documentElement.scrollLeft,
y: event.clientY + document.body.scrollTop +
document.documentElement.scrollTop
};
}
window.onload = function () {
var spotlightRadius = 60,
container = document.getElementById('container'),
canvas = document.createElement('canvas'),
image = container.getElementsByTagName('img')[0],
width = canvas.width = image.width,
height = canvas.height = image.height,
context = canvas.getContext('2d');
context.globalCompositeOperation = 'xor';
container.insertBefore(canvas, image.nextSibling);
container.style.width = width + 'px';
container.style.height = height + 'px';
var offset = getOffset(canvas, document.body);
clear = function () {
context.fillStyle = '#000';
context.clearRect(0, 0, width, height);
context.fillRect(0, 0, width, height);
};
clear();
image.style.visibility = 'visible';
canvas.onmouseout = clear;
canvas.onmouseover = canvas.onmousemove = function (event) {
var mouse = getMousePosition(event),
x = mouse.x - offset.left,
y = mouse.y - offset.top;
clear();
if (document.getElementById('blurry').checked) {
var gradient = context.createRadialGradient(x, y, 0, x, y, spotlightRadius);
gradient.addColorStop(0, 'rgba(0, 0, 0, 1)');
gradient.addColorStop(0.875, 'rgba(0, 0, 0, 1)');
gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
context.fillStyle = gradient;
context.fillRect(x - spotlightRadius, y - spotlightRadius,
2 * spotlightRadius, 2 * spotlightRadius);
} else {
context.beginPath();
context.arc(x, y, spotlightRadius, 0, 2 * Math.PI);
context.fillStyle = '#000';
context.fill();
}
};
};
* {
margin: 0;
padding: 0;
}
.control {
font-family: sans-serif;
font-size: 15px;
padding: 10px;
}
#container {
position: relative;
}
#container img, #container canvas {
position: absolute;
left: 0;
top: 0;
}
#container img {
visibility: hidden;
}
#container canvas {
cursor: none;
}
<p class="control">
<input type="checkbox" id="blurry" /> blurry edges
</p>
<div id="container">
<img src="https://dl.dropboxusercontent.com/u/139992952/multple/annotateMe.jpg" />
</div>

this code works for me:
x = event.pageX;
y = event.pageY;
radius = 10;
context = canvas.getContext("2d");
context.fillStyle = "black";
context.fillRect(0, 0, context.canvas.width, context.canvas.height);
context.beginPath();
var radialGradient= context.createRadialGradient(x,y,1,x,y,radius);
radialGradient.addColorStop(0,"rgba(255,255,255,1");
radialGradient.addColorStop(1,"rgba(0,0,0,1)");
//context.globalCompositeOperation = "destination-out";
context.fillStyle = radialGradient;
context.arc(x, y, radius, 0, Math.PI*2, false);
context.fill();
context.closePath();
it seems that this line was messing it context.globalCompositeOperation = "destination-out";
there were also pointless lines in your code like beginnig path before filling rect and fill() function after filling path

Related

Marker shaped canvas pen - Issues with two silmutaneous strokes

I am trying to create a marker style pencil with which one can draw on a canvas: a slightly rotated rectangle as pencil tip. My idea was to simply have to simultaneous strokes on mousedown, one of which is moved to the right and to the top a little bit so that is appears like a marker.
This is my code:
function draw(e) {
if (e.buttons !== 1) return;
ctx.beginPath();
ctx.lineWidth = 5;
ctx.lineCap = 'round';
ctx.moveTo(pos.x, pos.y);
setPosition(e);
ctx.lineTo(pos.x, pos.y);
ctx.moveTo(pos.x + 2, pos.y + 2);
setPosition(e);
ctx.lineTo(pos.x + 2, pos.y+1);
ctx.stroke();
}
When I draw slowly, it works perfectly. When I speed up the cursor, however, one of the two strokes is not ecxecuted properly:
Does someone have an idea how I can get both strokes to work?
Thank you!
Edit: Removing some redundancy produced a new issue with the line:
function draw(e) {
if (e.buttons !== 1) return;
ctx.beginPath();
ctx.lineWidth = 5;
ctx.lineCap = 'round';
ctx.moveTo(pos.x, pos.y);
setPosition(e);
ctx.moveTo(pos.x + 2, pos.y + 2);
ctx.stroke();
}
EDIT: example:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
resize();
var pos = { x: 0, y: 0 };
window.addEventListener('resize', resize);
document.addEventListener('mousemove', draw);
document.addEventListener('mousedown', setPosition);
document.addEventListener('mouseenter', setPosition);
function setPosition(e) {
pos.x = e.clientX + $(document).scrollLeft();
pos.y = e.clientY + $(document).scrollTop();
}
function resize() {
ctx.canvas.width = $('#canvas').width();
ctx.canvas.height = $('#canvas').height();
}
function draw(e) {
if (e.buttons !== 1) return;
ctx.beginPath();
ctx.lineWidth = 5;
ctx.lineCap = 'round';
ctx.strokeStyle = '#000000';
ctx.moveTo(pos.x, pos.y);
setPosition(e);
ctx.lineTo(pos.x + 2, pos.y+2);
ctx.stroke();
}
#canvas {
width: 200px;
height: 200px;
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<canvas id="canvas"></canvas>

How to get pixel range in scaled canvas?

In the following code, I am drawing a circle on the center, but because it is scaled, the circle is in bottom right corner!
var canvas = document.getElementById('mycanvas');
context = canvas.getContext("2d");
context.canvas.width = 400;
context.canvas.height = 200;
context.clearRect(0, 0, context.canvas.width, context.canvas.height); // Clears the canvas
var scale = 2;
context.scale(scale, scale);
context.beginPath();
context.fillStyle = "#ff2626"; // Red color
context.arc(context.canvas.width / 2, context.canvas.height / 2, 10, 0, Math.PI * 2); //center
context.fill();
context.closePath();
canvas {
border: solid 1px #ccc;
}
<HTML>
<body>
<canvas id="mycanvas"></canvas>
</body>
</HTML>
As you probably know, canvas width and height is not related to what is drawing inside it, especially when you scale it. in other word, when you scale a canvas using context.scale(scale_x, scale_y); it will scale all shapes inside the canvas. I am wondering to know, is there any way to get the canvas pixel range?
I want to know the X on left edge and right edge and the Y on top and bottom edges when a canvas is scaled.
Dividing the coordinates by scale should do the trick:
var canvas = document.getElementById('mycanvas');
context = canvas.getContext("2d");
context.canvas.width = 400;
context.canvas.height = 200;
context.clearRect(0, 0, context.canvas.width, context.canvas.height); // Clears the canvas
var scale = 2;
context.scale(scale, scale);
context.beginPath();
context.fillStyle = "#ff2626"; // Red color
context.arc(context.canvas.width /2/scale, context.canvas.height / 2/scale, 10, 0, Math.PI * 2); //center
context.fill();
context.closePath();
canvas {
border: solid 1px #ccc;
}
<HTML>
<body>
<canvas id="mycanvas"></canvas>
</body>
</HTML>
And on a side note, context.canvas.width /(2*scale) is cleaner than context.canvas.width /2/scale, but I kept it like that just to show the division by scale.
You don't need this pixel range.
You have to understand how canvas transformations work and embrace it rather than trying to do the math yourself.
If we take the canvas as a real canvas, or as a sheet of paper, then we can say that the transformation matrix controls the position of this sheet of paper relative to a fixed camera.
The key point is that, the coordinates you provide to the canvas drawing methods are still the ones that are on the un-transformed sheet of paper, no matter how you did rotate, translate or scale it.
Also, initially, we do hold this sheet of paper by its top left corner, this is known as the transformation-origin; all the transformations like rotate and scale will be done from this point, and this is why when you did scale by 2, the center coordinates are now in the bottom right corner of what the camera sees.
So if you wish to scale to the center of your canvas, you need to first move the transformation-origin so it's at the center of our camera, then you can scale your canvas, and finally you just go back again by half the size of the canvas so your drawings are in the center. This can be achieved quite easily by two calls: one to the absolute setTransform, which is able to apply both the scaling and the initial translation required to set our transformation origin, and one to translate, which is relative to the current transform matrix:
const canvas = document.getElementById( 'canvas' );
const ctx = canvas.getContext( '2d' );
const scale = 2;
const cx = canvas.width / 2;
const cy = canvas.height / 2;
// before transformation in red
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc( cx, cy, 30, 0, Math.PI * 2);
ctx.fill();
// with transformation in semi-opaque green
ctx.globalAlpha = 0.5;
ctx.fillStyle = 'green';
// scale with origin set to the center of canvas
ctx.setTransform(scale, 0, 0, scale, cx, cy);
// move back origin to the new top left corner of the visible area
ctx.translate( -cx, -cy );
/* the two previous lines are effectively the same as
ctx.translate( cx, cy );
ctx.scale( scale, scale );
ctx.translate( -cx, -cy );
and as
ctx.setTransform(
scale, 0, 0,
scale, cx - (cx * scale) , cy - (cy * scale)
);
*/
ctx.beginPath();
ctx.arc( cx, cy, 30, 0, Math.PI * 2);
ctx.fill();
canvas { border: 1px solid }
<canvas id="canvas"></canvas>
As you can already see in this little example, you are not limited to a single transformation matrix per frame, you can very well compose your image by layering a few different transformations:
const canvas = document.getElementById( 'canvas' );
const ctx = canvas.getContext( '2d' );
const { width, height } = canvas;
const cx = width / 2;
const cy = height / 2;
const transform = {
angle: 0,
scale: 1,
x: 0,
y: 0
};
const img = new Image();
img.onload = anim;
img.src = "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png";
function anim() {
updateTransform();
draw();
requestAnimationFrame( anim );
}
function updateTransform() {
transform.x += Math.cos( transform.angle );
transform.y += Math.sin( transform.angle );
transform.scale = (Math.sin( transform.angle ) / 3) + 1;
transform.angle += Math.PI / 180;
}
function draw() {
// reset the transformation matrix
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect( 0, 0, width, height );
drawImage();
drawCircleOnImage();
drawCentralSquare();
drawCameraCross();
}
// the image is moved by our 'tranform' object
function drawImage() {
ctx.setTransform( transform.scale, 0, 0, transform.scale, transform.x, transform.y );
ctx.drawImage( img, 0, 0 );
}
// A circle which will be moved with the image
function drawCircleOnImage() {
ctx.setTransform( transform.scale, 0, 0, transform.scale, transform.x, transform.y );
ctx.beginPath();
ctx.arc( cx, cy, 50, 0, Math.PI * 2 );
ctx.stroke()
}
// a square, always at the center of view,
// but which scale follows our transform object
function drawCentralSquare() {
ctx.setTransform( transform.scale, 0, 0, transform.scale, cx, cy );
ctx.translate( -cx, -cy );
ctx.strokeRect( cx - 50, cy - 50, 100, 100 );
}
// a cross, always at the center of the view, and untransformed
function drawCameraCross() {
ctx.setTransform( 1, 0, 0, 1, 0, 0 );
ctx.beginPath();
ctx.moveTo( cx - 10, cy );
ctx.lineTo( cx + 10, cy );
ctx.moveTo( cx, cy - 10 );
ctx.lineTo( cx, cy + 10 );
ctx.stroke();
}
canvas { border: 1px solid }
<canvas id="canvas" width="800" height="600"></canvas>
And this with no maths from our part.
You have to consider your scale so ex (width/2)/scale
var canvas = document.getElementById('mycanvas');
context = canvas.getContext("2d");
context.canvas.width = 400;
context.canvas.height = 200;
context.clearRect(context.canvas.width, context.canvas.height, context.canvas.width, context.canvas.height); // Clears the canvas
var scale = 2;
context.scale(scale, scale);
context.beginPath();
context.fillStyle = "#ff2626"; // Red color
context.arc((context.canvas.width/2)/scale , (context.canvas.height/2)/scale, 10, 0, Math.PI * 2); //center
context.fill();
context.closePath();
canvas {
border: solid 1px #ccc;
}
<HTML>
<body>
<canvas id="mycanvas"></canvas>
</body>
</HTML>

How to make a circle appear on any color html5 canvas

I have an arc on a canvas that moves around wherever the mouse is. it is stroked onto the canvas with the color black. if the mouse goes over something black, the circle disappears. i would like it if the circle could change color, depending on what it is being drawn over. could anyone help me?
here is some code:
ctx.beginPath()
ctx.clearRect(0, 0, brush.prePos.x + brush.size*2, brush.prePos.y + brush.size*2)
ctx.arc(pos.x, pos.y, brush.size / 4, 0, Math.PI*2)
ctx.stroke()
ctx.closePath()
You can try different compositing modes, particularly XOR. From the MDN example:
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.globalCompositeOperation = 'xor';
#SamiHult's answer is a good answer. Using globalCompositeOperation will do the trick. Here comes a demo:
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let cw = canvas.width = 300,
cx = cw / 2;
let ch = canvas.height = 300,
cy = ch / 2;
// the mouse
let m = {}
// draw a black circle
ctx.beginPath();
ctx.arc(100,100,45,0,2*Math.PI);
ctx.fill();
canvas.addEventListener("mousemove",(evt)=>{
m = oMousePos(canvas, evt);
ctx.clearRect(0,0,cw,ch);
// draw a circle stroked black following the mouse
drawCircle(m);
// draw a black circle
ctx.beginPath();
ctx.arc(100,100,45,0,2*Math.PI);
ctx.fill();
// the important part:
ctx.globalCompositeOperation = "xor";
})
function drawCircle(p){
ctx.beginPath();
ctx.arc(p.x,p.y,10,0,2*Math.PI);
ctx.stroke();
}
function oMousePos(canvas, evt) {
var ClientRect = canvas.getBoundingClientRect();
return { //objeto
x: Math.round(evt.clientX - ClientRect.left),
y: Math.round(evt.clientY - ClientRect.top)
}
}
canvas {
border:1px solid;
}
<canvas id="canvas"></canvas>

Adjust canvas background in Javascript

I'm trying to drawn a rect on canvas, but I want that canvas has lightly transparent background, but that drawn rect has no background.
What I will is something as follows:
I have code as follows:
var canvas = document.getElementById('canvas');
var img = document.getElementById('photo');
var ctx = canvas.getContext('2d');
var rect = {};
var drag = false;
var update = true; // when true updates canvas
var original_source = img.src;
img.src = original_source;
function init() {
img.addEventListener('load', function(){
canvas.width = img.width;
canvas.height = img.height;
canvas.addEventListener('mousedown', mouseDown, false);
canvas.addEventListener('mouseup', mouseUp, false);
canvas.addEventListener('mousemove', mouseMove, false);
});
// start the rendering loop
requestAnimationFrame(updateCanvas);
}
// main render loop only updates if update is true
function updateCanvas(){
if(update){
drawCanvas();
update = false;
}
requestAnimationFrame(updateCanvas);
}
// draws a rectangle with rotation
function drawRect(){
ctx.setTransform(1,0,0,1,rect.startX + rect.w / 2, rect.startY + rect.h / 2);
ctx.rotate(rect.rotate);
ctx.beginPath();
ctx.rect(-rect.w/2, -rect.h/2, rect.w, rect.h);
/* ctx.fill(); */
ctx.stroke();
}
// clears canvas sets filters and draws rectangles
function drawCanvas(){
// restore the default transform as rectangle rendering does not restore the transform.
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawRect()
}
// create new rect add to array
function mouseDown(e) {
rect = {
startX : e.offsetX,
startY : e.offsetY,
w : 1,
h : 1,
rotate : 0,
};
drag = true;
}
function mouseUp() { drag = false; buttons_shown = true; update = true; }
function mouseMove(e) {
if (drag) {
rect.w = (e.pageX - this.offsetLeft) - rect.startX;
rect.h = (e.pageY - this.offsetTop) - rect.startY;
update = true;
}
}
init();
.hide{
display: none !important;
}
canvas{
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
display:inline-block;
background:rgba(0,0,0,0.3);
}
<div style="position: relative; overflow: hidden;display:inline-block;">
<img id="photo" src="http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg"/>
<canvas id="canvas"></canvas>
</div>
<div id="buttons" class="hide"></div>
In my example I set the background of the canvas to what I will but I cannot remove that background for the drawn rect, it has the same color as the canvas.
Here is the fiddle.
Any idea how to solve it?
This could be achieved in several ways (compositing, clip-path...) but the easiest for such a simple path is probably to use the "evenodd" fill-rule parameter of fill() method which will allow us to draw this rectangle with a hole.
The process is simply to draw a first rect the size of the canvas, then, in the same path declaration, draw your own smaller rectangle. The fill-rule will then exclude this smaller inner rectangle from the bigger one.
function drawRect() {
ctx.beginPath(); // a single path
// the big rectangle, covering the whole canvas
ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height);
// your smaller, inner rectangle
ctx.setTransform(1, 0, 0, 1, rect.startX + rect.w / 2, rect.startY + rect.h / 2);
ctx.rotate(rect.rotate);
ctx.rect(-rect.w / 2, -rect.h / 2, rect.w, rect.h);
// set the fill-rule to evenodd
ctx.fill('evenodd');
// stroke
// start a new Path declaration
ctx.beginPath
// redraw only the small rect
ctx.rect(-rect.w / 2, -rect.h / 2, rect.w, rect.h);
ctx.stroke();
}
var canvas = document.getElementById('canvas');
var img = document.getElementById('photo');
var ctx = canvas.getContext('2d');
var rect = {};
var drag = false;
var update = true; // when true updates canvas
var original_source = img.src;
img.src = original_source;
function init() {
img.addEventListener('load', function() {
canvas.width = img.width;
canvas.height = img.height;
canvas.addEventListener('mousedown', mouseDown, false);
canvas.addEventListener('mouseup', mouseUp, false);
canvas.addEventListener('mousemove', mouseMove, false);
// set our context's styles here
ctx.fillStyle = 'rgba(0,0,0,.5)';
ctx.strokeStyle = 'white';
ctx.lineWidth = 2;
});
// start the rendering loop
requestAnimationFrame(updateCanvas);
}
// main render loop only updates if update is true
function updateCanvas() {
if (update) {
drawCanvas();
update = false;
}
requestAnimationFrame(updateCanvas);
}
// draws a rectangle with rotation
// clears canvas sets filters and draws rectangles
function drawCanvas() {
// restore the default transform as rectangle rendering does not restore the transform.
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawRect()
}
// create new rect add to array
function mouseDown(e) {
rect = {
startX: e.offsetX,
startY: e.offsetY,
w: 1,
h: 1,
rotate: 0,
};
drag = true;
}
function mouseUp() {
drag = false;
buttons_shown = true;
update = true;
}
function mouseMove(e) {
if (drag) {
rect.w = (e.pageX - this.offsetLeft) - rect.startX;
rect.h = (e.pageY - this.offsetTop) - rect.startY;
update = true;
}
}
init();
.hide {
display: none !important;
}
canvas {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
display: inline-block;
background: rgba(0, 0, 0, 0.3);
}
<div style="position: relative; overflow: hidden;display:inline-block;">
<img id="photo" src="http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg" />
<canvas id="canvas"></canvas>
</div>
<div id="buttons" class="hide"></div>
Try filling your rect before calling ctx.stroke(), like this:
ctx.fillStyle = "rgba(255, 255, 255, 0.3)";
ctx.fill();
This will produce similar effect to what you have shown in your question. Now the inside of rectangle has both css and fillStyle effect, so it's not ideal - for even better effect you would have to fill outside of rect with desired style instead of setting background in css.
first, draw a full canvas with semi-transparent background, like
ctx.fillStyle = 'rgba(32, 32, 32, 0.7)';
ctx.fillRect(0, 0, width, height);
Then just clear your rectangular form this canvas
ctx.clearRect(x, y, mini_width, mini_height);

How can I anchor the bottom of a gradient to the container that it fills in HTML5 Canvas?

I'm trying to paint a gradient half-way up the canvas, so that the gradient covers the top half.
The problem is that I can't figure out how to make the gradient line up with the
'container' (the shape controlled by fillRect()) - When I push the container up to the proper position, the gradient stays anchored to the bottom of the canvas; not the bottom of the container that it's filling.
The bottom of the gradient should begin at the bottom of the container that it fills, not the bottom of the canvas. How can I achieve this?
//not sliding gradient container upward
function paintC1() {
var canvas = document.getElementById('canvas-1');
var context = canvas.getContext('2d');
var height = canvas.height;
var width = canvas.width;
var grd = context.createLinearGradient(width, 0, width, height);
grd.addColorStop(0.5, 'rgba(0,174,239,0)');
grd.addColorStop(0.85, 'rgba(0,174,239,0.6)');
grd.addColorStop(1, 'rgba(0,174,239,1)');
context.fillStyle = grd;
context.fillRect(0, 0, width, height);
}
/* for comparison */
//sliding gradient container up
function paintC2() {
var canvas = document.getElementById('canvas-2');
var context = canvas.getContext('2d');
var height = canvas.height;
var width = canvas.width;
var grd = context.createLinearGradient(width, 0, width, height);
grd.addColorStop(0.5, 'rgba(0,174,239,0)');
grd.addColorStop(0.85, 'rgba(0,174,239,0.6)');
grd.addColorStop(1, 'rgba(0,174,239,1)');
context.fillStyle = grd;
context.fillRect(0, -50, width, height);
}
paintC1();
paintC2();
<canvas id="canvas-1" style="width:250px; height:500; border-style:solid; float:left; margin-right:10px;"></canvas>
<canvas id="canvas-2" style="width:250px; height:500; border-style:solid; float:left; margin-right:10px;"></canvas>
The gradient fill is relative to canvas, not to element, so to achieve your task you should do something like this.
var grd = context.createLinearGradient(width, -50, width, height -50);
otherwise you have to translate the context:
var canvas = document.getElementById('c');
var context = canvas.getContext('2d');
var gradient = context.createLinearGradient(0, 0, 0, 30);
var sampleW = 100;
var sampleH = 30;
gradient.addColorStop(0.5, 'rgba(0,174,239,0)');
gradient.addColorStop(0.85, 'rgba(0,174,239,0.6)');
gradient.addColorStop(1, 'rgba(0,174,239,1)');
var translations= [[30, 150],[100, 110],[170, 70],[240, 30]];
context.font = '12px verdana bold';
for (var i = 0; i < translations.length; i++) {
var t= translations[i];
context.save();
// gradient is defined at the origin,
// so we have to move the origin
context.translate(t[0], t[1])
context.fillStyle = gradient;
context.fillRect(0, 0, sampleW, sampleH);
context.strokeRect(0, 0, sampleW, sampleH);
context.restore();
}
<canvas id="c" width="500" height="500"></canvas>

Categories

Resources