How can I draw on top of image using HTML Canvas? - javascript

I am looking to achieve a sort of "color in the image" effect.
I have several images of a giraffes head, all tinted different colours. I would like to be able to "colour in" the image by revealing the tinted image by using your mouse to draw a shape, and only the intersection shows. Where the untinted image of the giraffe is defaulted to show, and if you use your mouse to draw on top of the image, where you drew would reveal a tinted image of the giraffe, revealing a sort of "colouring in" of the image.
Here's what I have so far. I'm using the MDN documentation of Canvas globalCompositeOperation as reference. But I'm stuck at a pretty early stage:
function animate() {
// canvasContext.clearRect(0, 0, canvas.width, canvas.height);
// canvasContext.drawImage(image2, (canvas.width/2) - (image2.width/2), (canvas.height/2) - (image2.height/2), image2.width, image2.height); // if this line is removed, we can draw the image, but theres no starting image
canvasContext.save();
canvasContext.beginPath();
canvasContext.arc(touchX, touchY, 20, 0, Math.PI*2, false);
canvasContext.clip();
canvasContext.drawImage(image1, (canvas.width/2) - (image1.width/2), (canvas.height/2) - (image1.height/2), image1.width, image1.height);
canvasContext.restore();
window.requestAnimationFrame(animate);
}
I can't even get the ellipse 'brush' to stay on the canvas and fill in the shape of the giraffe. Nothing seems to be working for me.
Here's a JSFiddle of it not working. The touchmove is working in my project environment, just not in the Fiddle for some reason.
--
Update: I can get the tinted image to be "painted" onto the screen in the shape of the giraffe, using clip. But when I render the untinted image before, the "colouring in" effect doesn't work.

Figured it out! Looks like clip() with two layered canvas elements was the answer, instead of globalCompositeOperation.
The following code achieves this 'colouring in' effect.
function handleOnPointerDown(event) {
touchX = event.touches[0].clientX;
touchY = event.touches[0].clientY;
}
function animate() {
displayImageOnCanvas(canvas, canvasContext, loadedImage[0]);
canvasContext2.save();
canvasContext2.beginPath();
canvasContext2.globalAlpha = 0.5;
canvasContext2.arc(touchX, touchY, 20, 0, Math.PI*2, false);
canvasContext2.clip();
if (colourState === 1) {
// red tinted
displayImageOnCanvas(canvas2, canvasContext2, loadedImage[1]);
} else if (colourState === 5) {
// blue tinted
displayImageOnCanvas(canvas2, canvasContext2, loadedImage[5]);
}
canvasContext2.restore();
window.requestAnimationFrame(animate);
}

Related

javascript canvas only drawing second image if the browser window is resized

i am using react, I have a canvas with a green square in the middle and a red rectangle drawn to the left of it. The problem is only the first rectangle is being drawn, if I resize the browser the other one gets drawn.
const Canvas = props => {
useEffect(() =>{
var player1 = { x: playerX, y: playerY, draggable: gameStatus }
var enemy1 = { x: enemy1X, y: enemy1Y, width: enemy1Width, height: enemy1Height}
const drawFun = () => {
context.clearRect(0,0, canvasWidth, canvasHeight)
context.beginPath();
context.fillStyle = '#00f4cc'
context.fillRect(player1.x, player1.y, playerWidth, playerHeight)
context.beginPath();
context.fillStyle = 'red'
context.fillRect(enemy1.x, enemy1.y, enemy1Width, enemy1Height)
requestAnimationFrame(drawFun);
}
drawFun();
The second red square gets drawn where it should be, I just need to resize the browser window to confirm it. Im not sure why resizing window helps,any help appreciated!
I would guess that the coordinates of where you are trying to draw the rectangles are out of the browser window (or the dimensions defined by the canvas). However, this if this is being solved by resizing the browser window then this is probably not the case because the canvas would always start at (0,0).
I would suggest printing out the enemy.x
console.log(enemy.x);
If the value is negative, then the rectangle is being cut off.
I'm not sure if this is the actual cause, but hope it helps

FabricJS, change control button image when hover

I am using a custom rotation icon that shows when I select objects, defined as I create the canvas.
function renderRotationIcon(ctx, left, top, styleOverride, fabricObject) {
//#ts-ignore
let size = this.cornerSize
ctx.save()
ctx.translate(left, top)
//#ts-ignore
ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle))
ctx.drawImage(img, -size / 2, -size / 2, size, size)
ctx.restore()
}
However, I would like to change the image img when the user hovers the rotation icon.
I tried this when defining img
img.onmouseover = () => {
img.src = "LibraryHoverTrans.png"
}
As expected, this didn't work, as I suspect that something more specific to fabricJS is needed. I tried searching for a related solution.

How to move ellipse filled with an image to mask similar background?

I am a super early user of coding from Italy.
I came up with an idea to promote a company logo on their website and I almost reached the goal so I am sharing this problem.
The idea is to obtain a sort of clipping mask effect when the mouse/cursor move on the image
I've made so far a code that does the work with a still ellipse.
When I set the position parameters of the ellipse as mouseX and mouseY the effect does not work if not just a bit of a glitch at the start.
How can I make it work as intended?
Here you can find the link of what I have now:
https://editor.p5js.org/francesco.ficini.designer/full/FLBuhggW-
Here the code:
let img;
let imgbg2;
let maskImage;
function preload() {
img = loadImage("NeroP.jpg");
imgbg2 = loadImage("RossoP.jpg");
}
function setup() {
createCanvas(400, 225);
img.mask(img);
}
function draw() {
background(imgbg2, 0, 0);
//Immages
image(imgbg2, 0, 0);
image(img,0,0);
// Ellipse Mask
maskImage = createGraphics(400, 225);
maskImage.ellipse(200, 100, 50, 50);
imgbg2.mask(maskImage);
image(imgbg2, 0, 0);
}
The thing about the p5.Image.mask function is that it modifies the image that is being masked. Which means that any pixels that are cleared by the mask are gone for good. So if you want to dynamically change the mask you will need to make a copy of the original and re-apply the modified mask any time it changes.
Additionally you will want to avoid creating images and graphics objects in your draw() function because this can result in excessive memory allocation. Instead create a single set of graphics/images and re-use them.
let img;
let imgbg2;
let maskImage;
let maskResult;
function preload() {
img = loadImage("https://www.paulwheeler.us/files/NeroP.jpeg");
imgbg2 = loadImage("https://www.paulwheeler.us/files/RossoP.jpeg");
}
function setup() {
createCanvas(400, 225);
// Create graphics and image buffers in setup
maskImage = createGraphics(imgbg2.width, imgbg2.height);
maskResult = createImage(imgbg2.width, imgbg2.height);
}
function mouseMoved() {
if (maskResult) {
maskImage.clear();
// Ellipse
maskImage.ellipse(mouseX, mouseY, 50, 50);
// Copy the original imgbg2 to the maskResult image
maskResult.copy(
imgbg2,
0, 0, imgbg2.width, imgbg2.height,
0, 0, imgbg2.width, imgbg2.height
);
// apply the mask to maskResult
maskResult.mask(maskImage);
}
}
function draw() {
//Immagini
image(img, 0, 0);
// draw the masked version of the image
image(maskResult, 0, 0);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

Changing Pen Color on Canvas after draw

I'm trying to change the color of the contents of the canvas after it is drawn. So if you start drawing a green circle, you could then decide later to make your previously drawn circle into a red a circle.
I'm using the signaturePad plugin here:
https://github.com/szimek/signature_pad
I have some of the functionality built, but the pen color change doesn't change previously drawn elements. Here's a fiddle:
http://jsfiddle.net/Z6g5Z/
Thanks for your help! The fiddle is prob. the best way to see the issue, but the JS and markup are below.
var canvas = $("#can")[0];
var signaturePad = new SignaturePad(canvas, {
minWidth: 2,
maxWidth: 5,
penColor: "rgb(66, 133, 244)"
});
$('#clear').click(function(){
signaturePad.clear();
});
$('.global-color li').click(function(){
$('.on').removeClass('on');
var $t = $(this);
$t.addClass('on');
var selectedColor = $t.data('color');
signaturePad.penColor = hexToRgb(selectedColor);
});
<ul class="global-color">
<li class="yellow-pick" data-color="#f8c90d">yellow</li>
<li class="green-pick" data-color="#3dae49">green</li>
<li class="orange-pick" data-color="#e87425">orange</li>
<li class="blue-pick on" data-color="#009cc5">blue</li>
</ul>
<div>
<input id="clear" type="button" value="clear" />
</div>
<canvas id="can" width="200px" height="200px"></canvas>
In your color change handler, have all canvas change its (non-transparent) pixels to the new color.
For this, most simple is to use globalComposite operation mode 'source-in', and fill over the canvas with the new color :
// set all pixels of the image to this color
function setCurrentColor(canvas, color) {
var context = canvas.getContext('2d');
context.save();
context.fillStyle = color;
context.globalCompositeOperation = 'source-in';
context.fillRect(0,0,canvas.width, canvas.height);
context.restore();
}
i updated your demo :
http://jsfiddle.net/gamealchemist/Z6g5Z/3/
You can by changing composite mode and fill in the color you want on the existing content.
With a library like this you have no guarantee of the inner workings of it so it may work today and may not work tomorrow - if the author decides to change the way it behaves internally.
So with that disclaimer you can use the following code to change the color of the existing drawing - this works in general with all canvas drawings and changes non-transparent pixels to what you draw on top:
function changeColor() {
ctx.save();
ctx.globalCompositeOperation = 'source-atop';
ctx.fillStyle = selectedColor;
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.restore();
}
However, since we are going to tap into the library there are at least one other limitation here. The library seem to use a second canvas on top where it registers the mouse. When pen is up it transfer that drawing to the main canvas. The drawback with this, for us, is that the pen change won't happen until we draw something new; we can't change the pen color visually just by using the above function - for that you would have to patch the library to add for example a method which did all the steps needed internally.
But we do get close by adding the following setting:
var signaturePad = new SignaturePad(canvas, {
minWidth: 2,
maxWidth: 5,
penColor: "rgb(66, 133, 244)",
onBegin: changeColor /// add callback here
});
Live demo here
Hope this helps!

javascript canvas detect click on shape

I have a problem with the click function in javascript. This is my code:
var canvas = document.getElementsByTagName("canvas")[0];
var ctx = canvas.getContext('2d');
BigCircle = function(x, y, color, circleSize) {
ctx.shadowBlur = 10;
ctx.shadowColor = color;
ctx.beginPath();
ctx.arc(x, y, circleSize, 0, Math.PI * 2, true);
ctx.fill();
ctx.closePath();
};
var bigGreen = new BigCircle(1580, 800, '#5eb62b', 180);
function init() {
$("#bigGreen").click(function(e){
alert("test");
});
}
$(document).ready(function() {
init();
});
But the click event is not working! Does anybody know why? Thank you so much in advance!
You can now use hit regions in Chrome and Firefox:
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Hit_regions_and_accessibility#Hit_regions
(Edit: Hit regions are now obsolete)
or just use one of the many canvas APIs:
http://www.fabricjs.com/
http://www.createjs.com/easeljs
http://www.paperjs.org
etc...
without seeing your html this question is a little bit unclear, it seems you would like to draw something on a canvas and use jquery to add click events for the circle, this isn't possible.
you can use jquery to get the click event ON the canvas and from the cursor position you can calculate if the user clicked the circle or not, but jquery won't help you here you have to do the math yourself.
jquery does only work for dom elements.
BigCircle = function(ctx,x, y, color, circleSize) {
ctx.beginPath();
ctx.arc(x, y, circleSize, 0, Math.PI * 2, true);
ctx.fillStyle=color
ctx.fill();
ctx.closePath();
this.clicked=function(){
ctx.fillStyle='#ff0000'
ctx.fill();
}
};
function init() {
var canvas = document.getElementsByTagName("canvas")[0];
var ctx = canvas.getContext('2d');
var bigGreen = new BigCircle(ctx,50, 50, '#5eb62b', 50);
$('#canvas').click(function(e){
var x = e.clientX
, y = e.clientY
if(Math.pow(x-50,2)+Math.pow(y-50,2) < Math.pow(50,2))
bigGreen.clicked()
})
}
$(document).ready(function() {
init();
});
​
jsfiddle is here
http://jsfiddle.net/yXVrk/1/
Canvas API function isPointInPath() can be used to assist with hit detection. This function can at least tell if mouse coordinates are within a complex shape without doing sophisticated math yourself. May work for simple shapes as well but my use case was for on a bezier curve path. However, you need to either incorporate this function in your drawing logic to test while paths are open or keep an array of Path2d objects to test against. I redraw on onClick handler and pass in mouse coords from event args, but I think I could have kept an array of Path2d objects instead.
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/isPointInPath
bigGreen is not in the HTML, so $("#bigGreen") selects nothing. You can't put a click function on things like JavaScript functions; since they don't exist in the DOM, how could you click one? You should replace #bigGreen with #canvas, since "canvas" is your HTML element.
I forked your fiddle to show this here.
Edit: If you want to see that the user clicked on a particular circle, you use the canvas click event, and then, you determine which circle was clicked by the coordinates passed into the click event.

Categories

Resources