I'm using html5 canvas and JS.
I'm drawing an image on the canvas with CanvasRenderingContext2D.drawImage(). This image is 15x15 black square, with a 1 pixel wide white border: so 16x16 total.
Is there a way to read image data in JS, and only scale up the border width of that image. So, leave the inner black square 15x15, but make the white border width 3 pixels wide?
As far as I can tell, the CanvasRenderingContext2D.getImageData() can only select an area, but you cannot seclude an area, to use the rest.
Thanks :)
You can not change the size of a canvas without clearing its content. The only way to resize the canvas is to create a second canvas, copy the existing content to the new canvas while adding the new content.
The following function will pad a canvas with a boarder by creating a second canvas. Filling it with the padding color, adding the original canvas image to it. Then resize the original and copying the new content to it.
// padCanvas(canvas, padAmount, padColor)
// canvas : HTMLCanvasElement Canvas to pad
// Note all state setting on the input canvas will be reset
// padAmount : Number Amount to pad canvas in pixels
// padColor : String Color of padding as CSS style color string. eg "White"
// Returns the original canvas,
function padCanvas(canvas, padAmount, padColor) {
// create a canvas with the padded size
const can = Object.assign(
document.createElement("canvas"), {
width: canvas.width + padAmount * 2,
height: canvas.height + padAmount * 2
}
);
// add padding color and source (image)
const ctx = can.getContext("2d");
ctx.fillStyle = padColor;
ctx.fillRect(0, 0, can.width, can.height);
ctx.drawImage(canvas, padAmount, padAmount);
// Resize original image and add new content
Object.assign(canvas, {width: can.width, height: can.height});
canvas.getContext("2d").drawImage(can, 0, 0);
return canvas;
}
Related
is there a way to add white spaces to an image in javascript ?
I this the image 1 and I want to edit or create a new image, to add white spaces and the result would be the image 2.
The following code achieves this task. Basically, we create a canvas and set it to the size of the desired output. We then fill it with white before drawing the original image at (0,250) This centers the image (I should have done this with code, but instead looked at your output image in an image editor. (OutputHeight-InputHeight)/2 = Y offset to draw image at.
Since you're not actually adding any detail, it's possible this isn't the best way to go about what you're trying to achieve. It's possible that you should use margin/padding to expand the room the image appears to occupy.
window.addEventListener('load', onLoaded, false);
function onLoaded(evt)
{
let img = document.querySelector('img');
let canvas = document.querySelector('#output');
let ctx = canvas.getContext('2d');
ctx.fillStyle = '#fff';
ctx.fillRect(0,0,759,759);
ctx.drawImage(img, 0, 250);
}
body{
background-color: #ddd;
}
<img src='https://i.stack.imgur.com/bN5hp.jpg'/>
<hr>
<canvas id='output' width=760 height=760/></canvas>
You can do it using the canvas object.. if you have the image for instance
<img src="some.jpeg" id="myImg">
You can copy it to a new canvas and play with the drawImage properties
var img = document.getElementById('myImg');
var canvas = document.createElement('canvas');
canvas.width = img.width; // add here the extra width you want
canvas.height = img.height; // add here the extra height you want
canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height); // play here with the position, 0, 0 are the top x,y axis
Here is the link to mozilla documentation:
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
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 want to clear a stroked rectangle in certain canvas area. My initial thought was that I just need to call context.strokeRect function again with same parameters, with changing strokeStyle to transparent before. However it isn't working. Why and how can I fix it? Note that I want to only clear stroke (border) of rectangle, not everything inside.
Edit: I want to clear only border, not everything inside, so I can't use clearRect() method. Also I can't clear whole canvas and repaint it, because canvas contains animations.
JS fiddle: https://jsfiddle.net/47okok8s/
Example below has two canvas. The background just has some text. Then I fill the foreground canvas with "red" and use globalCompositeOperation "destination-out" to remove pixels. I also set alpha to 0.5 and half remove some pixels.
const ctxB = background.getContext("2d");
const ctxF = foreground.getContext("2d");
ctxB.textAlign = "center";
ctxB.textBaseline = "middle";
ctxB.font ="20px arial";
ctxB.fillText("Some Background text",150,25);
ctxB.fillText("Some Background text",150,75);
ctxB.fillText("Some Background text",150,125);
ctxF.lineJoin = "round";
ctxF.fillStyle = "red";
ctxF.fillRect(0,0,300,150);
ctxF.lineWidth = 8;
ctxF.globalCompositeOperation = "destination-out";
ctxF.strokeRect(25,25,250,100);
ctxF.fillRect(75,50,150,50);
ctxF.globalAlpha = 0.5;
ctxF.fillRect(65,40,170,70);
ctxF.globalCompositeOperation = "source-over";
canvas {
position : absolute;
top : 0px;
left: 0px;
}
<canvas id = "background"></canvas>
<canvas id = "foreground"></canvas>
I want to set a global clipTo in my Fabric-powered Canvas that will affect all user-added layers. I want a background image and an overlay image, which are unaffected by this clip mask.
Example:
Here's what's happening in this photo:
A canvas overlay image makes the t-shirt look naturally wrinkled. This overlay image is mostly transparent
A background image in the exact shape of the t-shirt was added, which is supposed to make the t-shirt look blue
A canvas.clipTo function was added, which clips the canvas to a rectangular shape
A user-added image (the famous Fabric pug) was added
I want the user-added image (the pug) to be limited to the rectangular area.
I do not want the background image (the blue t-shirt shape) affected by the clip area.
Is there a simple way to accomplish this? I really don't want to have to add a clipTo on every single user layer rather than one tidy global clipTo.
You can play with a JS fiddle showing the problem here.
I came here with the same need and ultimately found a solution for what I'm working on. Maybe it helps:
For SVG paths, within the clipTo function you can modify the ctx directly prior to calling render(ctx) and these changes apply outside the clipped path o. Like so:
var clipPath = new fabric.Path("M 10 10 L 100 10 L 100 100 L 10 100", {
fill: 'rgba(0,0,0,0)',
});
var backgroundColor = "rgba(0,0,0, 0.2)";
var opts = {
controlsAboveOverlay: true,
backgroundColor: 'rgb(255,255,255)',
clipTo: function (ctx) {
if (typeof backgroundColor !== 'undefined') {
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, 300, 150);
}
clipPath.render(ctx);
}
}
var canvas = new fabric.Canvas('c', opts);
canvas.add(new fabric.Rect({
width: 50,
height: 50,
left: 30,
top: 30,
fill: 'rgb(255,0,0)'
}));
You can of course add an image instead of a color, or whatever else you want done. The trick I've found is to put it in the clipTo function on the ctx directly.
here's a fiddle
One (sorta hacky) solution: set a CSS background image on your canvas element, as shown in https://jsfiddle.net/qpnvo3cL/
<canvas id="c" width="500" height="500"></canvas>
<style>
background: url('http://fabricjs.com/assets/jail_cell_bars.png') no-repeat;
</style>
<script>
var canvas = window._canvas = new fabric.Canvas('c');
canvas.clipTo = function(ctx) {
ctx.rect(100,100,100,100);
}
</script>
Have you tried clipping a fabric Group? You could make the whole shirt one canvas. The center graphics would be one Group which you clip to where you want it. The white t-shirt and the blue overlay would of course not be part of the clipped group.
Here's an example of clipping a group:
var rect = new fabric.Rect({width:100, height: 100, fill: 'red' });
var circle = new fabric.Circle({ radius: 100, fill: 'green' });
var group1 = new fabric.Group([ circle, rect ], { left: 100, top: 100 });
canvas.add(group1);
group1.clipTo = function(ctx) {
ctx.rect(50,50,200,200);
};
See this jsfiddle I made: https://jsfiddle.net/uvepfag5/4/
I find clip rather slow so I tend to use globalCompositeOperation to do masking.
If you really need to use clip then use it in conjunction with save and restore.
// ctx is canvas context 2d
// pug is the image to be clipped
// draw your background
ctx.save(); // save state
ctx.rect(100,100,100,100); // set the clip area
ctx.clip(); // apply the clip
ctx.drawImage(pug,x,y); // draw the clipped image
ctx.restore(); // remove the clipping
// draw the other layers.
or you can
// draw background
ctx.globalCompositeOperation = "xor"; // set up the mask
ctx.fillRect(100,100,100,100); // draw the mask, could be an image.
// Alpha will effect the amount of masking,
// not available with clip
ctx.globalCompositeOperation = "destination-over";
ctx.drawImage(pug,x,y); // draw the image that is masked
ctx.globalCompositeOperation = "source-over";
// draw the stuff that needs to be over everything.
The advantage of composite operations is you have control over the clipping at a per pixel level, including the amount of clipping via the pixel alpha value
$('#myCanvas').click(function (e) { //Default mouse Position
console.log(e.pageX);
$("#myCanvas2").attr("width", e.pageX );
$("#myCanvas3").attr("width", e.pageX );
var imageData = context.getImageData(0, 0, e.pageX,400);
context2.putImageData(imageData,0,0);
context3.putImageData(imageData,0,0);
context.clearRect ( 0 , 0 , 2000 , 2000 );
$("#myCanvas3").css("scale", "(-1, 1)" );
$("#myCanvas3").css("transform", "scale(-1,1)");
var canvas4 = document.getElementById('myCanvas4');
var context4 = canvas4.getContext('2d');
context4.drawImage( myCanvas2, 0,0 );
context4.drawImage(myCanvas3, e.pageX,0 );
context3.scale(3,3);
});
I am trying to scale my canvas with the scale command.
What am I doing wrong?
I'm not clear what you're trying to do, but these thoughts come to mind:
To get an accurate canvas mouse position, calculate relative to the canvas element:
$('myCanvas').click(function(e){
var BB=canvas.getBoundingClientRect();
var mouseX=parseInt(e.clientX-BB.left);
var mouseY=parseInt(e.clientY-BB.top);
}
About mixing CSS and Canvas:
CSS scale will operate on the center of the element.
Canvas scale will operate from the origin (the origin defaults to 0,0).
CSS scaling will not affect Canvas drawing. So if you "flip" using CSS scaling and then you draw the flipped element to a canvas, the resulting canvas drawing is not flipped.
Changing the canvas element width will also automatically clear the canvas content.
Changing CSS width will retain the canvas content, but will "stretch" the existing pixels into the new width (the content will appear deformed).
Canvas scaling commands only affect drawings done after the scale command. So your context3.scale(3,3) will not affect anything you have previously drawn on myCanvas3.
Here's how to horizontally flip an image on a canvas element:
Example code and a Demo: http://jsfiddle.net/m1erickson/tSPB2/
var img=document.getElementById("theImg");
var iw=img.width;
var ih=img.height;
var canvas2=document.getElementById('myCanvas2');
var context2=canvas2.getContext('2d');
canvas2.width=iw;
canvas2.height=ih;
context2.save();
context2.translate(iw,0);
context2.scale(-1,1);
context2.drawImage(img,0,0);
context2.restore();