I have followed the answer in this post; fill image with texture pattern, and it is working perfectly.
Is there a way to do the same with KonvaJS?
AFAIK, KonvaJS does not yet support the compositing required to create your texture overlay. But a Konva.Image can take a native html5 canvas element as its image source, so just do your overlay on an html5 canvas element and then feed it to Konva: var textureImage = new Konva.Image({ image:myCanvasElement })
Example annotated code and a Demo:
About Microsoft: Requires Edge -- IE doesn't allow compositing
var stage;
// Attributions of code that applies textures using compositing:
// Indirectly from your SO Q&A: http://stackoverflow.com/questions/36097859/add-texture-to-image-object-in-konvajs
// Directly from this SO Q&A: http://stackoverflow.com/questions/28545747/fill-image-with-texture-pattern/28552076#28552076
// image loading for demo (ignore)
var img1 = new Image;
var img2 = new Image;
var cnt = 2;
img1.onload = img2.onload = function() {
if (!--cnt) go()
};
img1.src = "http://i.imgur.com/8WqH9v4.png"; // sofa
img2.src = "http://i.stack.imgur.com/sQlu8.png"; // pattern
//
function createCompositedCanvas(img1, img2) {
// create canvas
canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d");
canvas.width = img1.width;
canvas.height = img1.height;
// create a pattern
ctx.fillStyle = ctx.createPattern(img2, "repeat");
// fill canvas with pattern
ctx.fillRect(0, 0, canvas.width, canvas.height);
// use blending mode multiply
ctx.globalCompositeOperation = "multiply";
// draw sofa on top
ctx.drawImage(img1, 0, 0, img1.width * .5, img1.height * .5);
// change composition mode (blending mode is automatically set to normal)
ctx.globalCompositeOperation = "destination-in";
// draw to cut-out sofa
ctx.drawImage(img1, 0, 0, img1.width * .5, img1.height * .5);
//
document.body.appendChild(canvas);
return (canvas);
}
// end attibuted code
function go() {
// create stage
stage = new Konva.Stage({
container: 'container',
width: img1.width,
height: img1.height
});
var layer = new Konva.Layer();
stage.add(layer);
// create composited canvas
var canvas = createCompositedCanvas(img1, img2);
// use the in-memory canvas as an image source for Konva.Image
var img = new Konva.Image({
x: -200,
y: -50,
image: canvas,
draggable: true
});
layer.add(img);
layer.draw();
}
body{padding:20px;}
#container{
border:solid 1px #ccc;
margin-top: 10px;
}
canvas{border:solid 1px red;}
<script src="https://cdn.rawgit.com/konvajs/konva/0.9.0/konva.min.js"></script>
<div id="container"></div>
<h4>Native canvas element used to do compositing</h4>
Related
I am working on a task to apply a pattern to multiple parts of shirt. Shirt image cannot be a single image. It is making with small images like left arm, right arm, collar etc...
Now I want to apply a pattern or color to all the parts inside the canvas.
<canvas id="canvas" width=250 height=250 style="margin-left: 100px; background: pink"></canvas>
<script type="text/javascript">
var img1 = new Image, img2 = new Image, cnt = 2, img3 = new Image,
canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
// image loading for demo (ignore)
img1.onload = img2.onload = function() {if (!--cnt) go()};
// Left Arm
img1.src = "left_arm.png";
//Right Arm
img3.src = "right_arm.png";
// Pattern Image
img2.src = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQlncafGCzVapWvTID6msFfk7OWtQSCEEnbKSLQhzVk1cPqe9CQ"; //
function go() {
// create a pattern
ctx.fillStyle = ctx.createPattern(img2, "repeat");
// // fill canvas with pattern
ctx.fillRect(0, 0, canvas.width, canvas.height);
// // use blending mode multiply
ctx.globalCompositeOperation = "multiply";
// // draw sofa on top
ctx.drawImage(img1, 0, 0, img1.width*.5, img1.height*.5);
ctx.drawImage(img1, 0, 0, img1.width*.5, img1.height*.5);
}
</script>
How can I achieve a blur behind a transparent box (fillStyle = 'rgba(255, 255, 255, 0.2)') in JavaScript canvas? Here's what I've got so far:
var canvas = document.getElementById('draw');
var c = canvas.getContext('2d');
function main() {
c.fillStyle = '#222';
c.fillRect(0, 0, canvas.width, canvas.height);
c.fillStyle = '#000';
c.fillRect(32, 32, 64, 64);
c.fillStyle = 'rgba(255, 255, 255, 0.2)';
c.filter = 'blur(5px)';
c.fillRect(16, 16, 128, 24);
}
But what happens, is instead of blurring the background behind the rectangle, is the rectangle itself is blurred, kind of obviously.
In the final script, I will probably use paths instead of rects.
Context2D filters will be applied only on your new drawings, so to also blur the background, you would actually have to redraw the part of the background you want to be blurred.
Fortunately, canvas can drawImage itself.
var blurredRect = {
x: 80,
y: 80,
height: 200,
width: 200,
spread: 10
};
var ctx = canvas.getContext('2d');
var img = new Image();
img.onload = draw;
img.src = 'https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg';
function draw() {
canvas.width = img.width / 2;
canvas.height = img.height / 2;
// first pass draw everything
ctx.drawImage(img, 0,0, canvas.width, canvas.height);
// next drawings will be blurred
ctx.filter = 'blur('+ blurredRect.spread +'px)';
// draw the canvas over itself, cropping to our required rect
ctx.drawImage(canvas,
blurredRect.x, blurredRect.y, blurredRect.width, blurredRect.height,
blurredRect.x, blurredRect.y, blurredRect.width, blurredRect.height
);
// draw the coloring (white-ish) layer, without blur
ctx.filter = 'none'; // remove filter
ctx.fillStyle = 'rgba(255,255,255,0.2)';
ctx.fillRect(blurredRect.x, blurredRect.y, blurredRect.width, blurredRect.height);
}
<canvas id="canvas"></canvas>
But, canvas blur filter is a bit different than CSS one in that it will make the spreading stay inside the drawn area. This means that in our case, we have a 5px border around our rectangle that is less blurred than the center.
To workaround, we can take the whole thing in a different order and play with globalCompositeOperation property*:
var blurredRect = {
x: 80,
y: 80,
height: 200,
width: 200,
spread: 10
};
var ctx = canvas.getContext('2d');
var img = new Image();
img.onload = draw;
img.src = 'https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg';
function draw() {
var spread = blurredRect.spread,
ratio = 0.5,
// make our blurred rect spreads
x = blurredRect.x - spread,
y = blurredRect.y - spread,
w = blurredRect.width + (spread * 2),
h = blurredRect.height + (spread * 2);
canvas.width = img.width * ratio;
canvas.height = img.height * ratio;
// this time we will first draw the blurred rect
ctx.filter = 'blur('+ spread +'px)';
// this time we draw from the img directly
ctx.drawImage(img,
x / ratio, y / ratio, w / ratio, h / ratio,
x, y, w, h
);
// now we will want to crop the resulting blurred image to the required one, so we get a clear-cut
ctx.filter = 'none'; // remove filter
// with this mode, previous drawings will be kept where new drawings are made
ctx.globalCompositeOperation = 'destination-in';
ctx.fillStyle = '#000'; // make it opaque
ctx.rect(blurredRect.x, blurredRect.y, blurredRect.width, blurredRect.height);
ctx.fill(); // clear-cut done
// reuse our rect to make the white-ish overlay
ctx.fillStyle = 'rgba(255,255,255,0.2)';
// reset gCO to its default
ctx.globalCompositeOperation = 'source-over';
ctx.fill();
// now we will draw behind the our blurred rect
ctx.globalCompositeOperation = 'destination-over';
ctx.drawImage(img, 0,0, canvas.width, canvas.height);
// reset to defaults
ctx.globalCompositeOperation = 'source-over';
}
<canvas id="canvas"></canvas>
But this approach requires that we keep access to the whole background as a drawable thing, in the example above that was just an image, but in real life, this might mean you'd have to do this operation on a second offscreen canvas.
var blurredRect = {
x: 80,
y: 80,
height: 200,
width: 200,
spread: 2
};
var ctx = canvas.getContext('2d');
// create an off-screen canvas
var bCanvas = canvas.cloneNode();
var bCtx = bCanvas.getContext('2d');
var img = new Image();
img.onload = draw;
img.src = 'https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg';
function draw() {
var spread = blurredRect.spread;
canvas.width = bCanvas.width = img.width / 2;
canvas.height = bCanvas.height = img.height / 2;
// now we have a composed background
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
ctx.font = '40px Impact';
ctx.fillStyle = 'white';
ctx.fillText('..SO BLUR ME..', 120, 282);
// make our clear-cut on the offscreen canvas
bCtx.filter = 'blur(' + spread +'px)';
bCtx.drawImage(canvas,
blurredRect.x - spread, blurredRect.y - spread, blurredRect.width + spread * 2, blurredRect.height + spread * 2,
blurredRect.x - spread, blurredRect.y - spread, blurredRect.width + spread * 2, blurredRect.height + spread * 2
);
// clear-cut
bCtx.filter = 'none';
bCtx.globalCompositeOperation = 'destination-in';
bCtx.beginPath();
bCtx.rect(blurredRect.x, blurredRect.y, blurredRect.width, blurredRect.height);
bCtx.fillStyle = '#000';
bCtx.fill();
// white-ish layer
bCtx.globalCompositeOperation = 'source-over';
bCtx.fillStyle = 'rgba(255,255,255,0.2)';
bCtx.fillRect(blurredRect.x, blurredRect.y, blurredRect.width, blurredRect.height);
// now just redraw on the visible canvas
ctx.drawImage(bCanvas, 0,0);
}
<canvas id="canvas"></canvas>
*One may say that instead of an offscreen canvas and gCO we could have used ctx.clip(), but since you said it might a more complex Path than a rect, I will not advise to do so. Indeed, while it would require less code, and maybe use less memory, clipping is just bad with antialiasing, and since you are doing blurring, that will just look plain ugly.
I am trying to figure out a way I can fill a shape in PIXI.js using a texture created from a canvas.
The reason for this is I wanna be able to create a gradient on a normal html canvas, and they make a texture out of it and add it to the pixi stage. Now I can do that, that was the first thing I tested, it works. But the end goal is to create shapes in PIXI.js using the Graphics class and then fill them with my gradient. I do not know how to accomplish this, as the .beginFill() method only accepts a color. How do I fill a shape with a texture?
Here is my code. I know the auxillary canvas creation is a little verbose, but that is a problem for later.
$(document).ready(function() {
var stage = new PIXI.Container();
var renderer = PIXI.autoDetectRenderer(800, 600);
document.body.appendChild(renderer.view);
//Aliases
var Sprite = PIXI.Sprite;
var TextureCache = PIXI.utils.TextureCache;
var resources = PIXI. loader.resources;
function AuxCanvas(id, w, h, color1, color2) {
this.id = id;
this.w = w;
this.h = h;
this.color1 = color1;
this.color2 = color2;
}
// create and append the canvas to body
AuxCanvas.prototype.create = function() {
$('body').append('<canvas id="'+
this.id+'" width="'+
this.w+'" height="'+
this.h+'"></canvas>');
}
// draw gradient
AuxCanvas.prototype.drawGradient = function() {
var canvas = document.getElementById(this.id);
var ctx = canvas.getContext('2d');
var gradient = ctx.createLinearGradient(0, 0, 800, 0);
gradient.addColorStop(0, this.color1);
gradient.addColorStop(1, this.color2);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, this.w, this.h);
}
function setup() {
var graphics = new PIXI.Graphics();
graphics.beginFill(PIXI.Texture.fromCanvas(can1)); //This doesn't work obviously
graphics.drawCircle(60, 185, 40);
graphics.endFill();
stage.addChild(graphics);
renderer.render(stage);
}
var can1 = new AuxCanvas("can1", 800, 600, "green", "yellow");
can1.create();
can1.drawGradient();
var can2 = new AuxCanvas("can2", 800, 600, "blue", "red");
can2.create();
can2.drawGradient();
setup();
})
Allright I figured out a way, actually it was easy.
Just make the Graphics object a mask for the sprite created from the html canvas.
function setup() {
var can2 = document.getElementById('can2');
var sprite = new Sprite(PIXI.Texture.fromCanvas(can2))
var graphics = new PIXI.Graphics();
graphics.beginFill();
graphics.drawCircle(300, 300, 200);
graphics.endFill();
sprite.mask = graphics;
stage.addChild(sprite);
renderer.render(stage);
}
Moreover, appending the graphic as a child of the sprite is the best way to go, just need to make sure that they are the same dimentions. With this done, I can move the sprite freely, and it's gradient texture doesn't change, or more precisely, it moves with the sprite. Of course everything has to be equal in dimentions.
var graphics = new PIXI.Graphics();
graphics.beginFill();
graphics.drawCircle(100, 100, 100);
graphics.endFill();
sprite.addChild(graphics);
sprite.mask = graphics;
..Hi there, After a long gap,i again got the opportunity to work with JavaScript and canvas.
here i am trying to draw, transparent image using canvas [ globalCompositeOperation ] helps me alot,
i got success on draw image[img1] and removed a overlapped part of image[ img2 ].
search alot but failed : Wanna try to drop shadow on output of canvas,look like below,
Please check out and give me your valuable suggestion OR solution.
$('.bg').one("load", function() {
var canvas = document.getElementById('canva'),
context = canvas.getContext('2d'),
img1 = $('.bg')[0],
img2 = $('.bgover')[0];
context.drawImage(img1, 0, 0);
context.globalCompositeOperation = 'destination-out';
context.beginPath();
context.drawImage(img2, 10, 10);
context.closePath();
//drop shadow -> Doesn't work
context.shadowBlur = 5;
context.shadowOffsetX = 10;
context.shadowOffsetY = 10;
context.shadowColor = "black";
});
body {
background: #E7FF00
}
.bg {
background: url() center;
width: 300px;
height: 300px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<img class='bg' src="http://www.qdtricks.org/wp-content/uploads/2015/02/hd-wallpapers-for-mobile.jpg" style='display:none'>
<img class='bgover' src="http://spotremoval.coit.com/sites/spotremoval.coit.com/files/styles/stain_sidebar/public/Feces%20Stain%20Removal%20-%20SPOT%20REMOVAL%20GUIDE.png?itok=j6f96IHQ" style='display:none'>
<canvas id="canva" width="400" height="400" style="position:absolute;left:0;top:0"></canvas>
Inner shadow with destination-out & source-atop
This is my solution. Load the image then create a copy with a 2D context so it can be drawn to. Then create a second image a little bigger to accommodate the shadow, offsets, and blur. Make it an inverted mask with comp destination-out. Set the original image's shadow settings. Then draw the mask image on top with comp source-atop
Now the image has the shadow and can be draw where you want it.
The function innerShadow(image,col,offX,offY,blur) does the work. Code is commented so enjoy :)
/** CanvasCtx.js begin **/
var canvas = document.getElementById("canV");
var ctx = canvas.getContext("2d");
/** CanvasCtx.js end **/
// copies an image adding the 2d context
function copyImage(img){
var image = document.createElement("canvas");
image.width = img.width;
image.height = img.height;
image.ctx = image.getContext("2d");
image.ctx.drawImage(img,0,0);
return image;
}
// creates a blank image with 2d context
var createImage = function(w,h){
var image = document.createElement("canvas");
image.width = w;
image.height =h;
image.ctx = image.getContext("2d");
return image;
}
// load an image from URL. Create a editable copy and then
// call the function ready
var loadImage = function(url,ready){
function onload(){
this.removeEventListener("load",onload);
image = copyImage(this);
ready(image);
}
var image = new Image();
image.src = url;
image.addEventListener("load",onload);
}
function innerShadow(image,shadowCol,offX,offY,blur){
var mx, my, img1;
// create a mask image, with pixel alpha the invers of original
// Needs to be bigger so that the shadow is consistant at edges
img1 = createImage(image.width+Math.abs(offX)+blur,image.height+Math.abs(offY)+blur);
// set the shadow colur to requiered but only for alising the edge
img1.ctx.fillStyle = shadowCol;
img1.ctx.fillRect(0,0,img1.width,img1.height); // fill the mask
img1.ctx.globalCompositeOperation = "destination-out"; // remove dest pixels
mx = img1.width/2- image.width/2; // recalculate offsets
my = img1.height/2- image.height/2;
// draw it 3 times to remove the slight alpha edge bleading
img1.ctx.drawImage(image,mx,my); // cut out the images shape from mask
img1.ctx.drawImage(image,mx,my); // cut out the images shape from mask
img1.ctx.drawImage(image,mx,my); // cut out the images shape from mask
// set up shadow settings
image.ctx.shadowColor = shadowCol;
image.ctx.shadowOffsetX = offX;
image.ctx.shadowOffsetY = offY;
image.ctx.shadowBlur = blur;
// draw the mask with the shadow on original image
image.ctx.globalCompositeOperation = "source-atop"; // only visible pixels
image.ctx.drawImage(img1,-mx,-my); // draw the shadow
}
// clear the canvas
ctx.clearRect(0,0,canvas.width,canvas.height)
// load and add shadow.
var imageWithInnerShadow;
var shadowOffX = 10;
var shadowOffY = 10;
var shadowBlur = 10;
var shadowCol = "Black";
// load the image
loadImage("http://i.stack.imgur.com/Jafta.png",function(img){
// add the shadow
innerShadow(img,shadowCol,shadowOffX,shadowOffY,shadowBlur);
ctx.drawImage(img,20,20); // show that it worked
imageWithInnerShadow = img; // hold the image for use
})
.canC { width:500px; height:500px;}
<canvas class="canC" id="canV" width=500 height=500></canvas>
Here's a dropShadow function that has the desired effect.
$('.bg').one("load", function() {
var canvas = document.getElementById('canva'),
context = canvas.getContext('2d'),
img1 = $('.bg')[0],
img2 = $('.bgover')[0];
context.drawImage(img1, 0, 0);
context.save();
context.globalCompositeOperation = 'destination-out';
context.drawImage(img2, 10, 10);
context.restore();
dropShadow(canvas, "red", 5, 2, 2);
});
// This function draws the image to left of canvas
// leaving only the shadow then draws the shadow in the
// empty space.
function dropShadow(can, color, blur, offsetX, offsetY) {
var s_can = document.createElement('canvas');
var s_ctx = s_can.getContext('2d');
var ctx = can.getContext('2d');
var w = can.width;
var h = can.height;
s_can.width = w;
s_can.height = h;
s_ctx.shadowBlur = blur;
s_ctx.shadowColor = color;
s_ctx.shadowOffsetX = w;
s_ctx.shadowOffsetY = 0;
s_ctx.drawImage(can, 0, 0, w, h,-w,0,w,h);
ctx.save();
ctx.globalCompositeOperation = 'destination-over';
ctx.drawImage(s_can, offsetX, offsetY);
ctx.restore();
}
body {
background: #E7FF00
}
.bg {
background: url() center;
width: 300px;
height: 300px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<img class='bg' src="http://www.qdtricks.org/wp-content/uploads/2015/02/hd-wallpapers-for-mobile.jpg" style='display:none'>
<img class='bgover' src="http://spotremoval.coit.com/sites/spotremoval.coit.com/files/styles/stain_sidebar/public/Feces%20Stain%20Removal%20-%20SPOT%20REMOVAL%20GUIDE.png?itok=j6f96IHQ" style='display:none'>
<canvas id="canva" width="400" height="400" style="position:absolute;left:0;top:0"></canvas>
I'm playing with canvas in HTML5 and Javascript and I have a problem:
I'd like to apply transformations used on the current image to multiple images.
What I did:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var img = new Image();
img.onload = function() {
//transformation stuff like:
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img, -img.width / 2, -img.height / 2, img.width, img.height);
ctx.beginPath();
ctx.lineTo(42, 42);
ctx.stroke();
ctx.lineTo(42, 24);
ctx.stroke();
...
ctx.rotate(Math.PI / 2);
...
};
img.src = //base64Img;
So I will apply a lot of transformations like draw some lines, crop, zoomIn etc...
How can I apply this to multiple files (more than 200) once (when these transformations are done) ?
Obviously, it will be done in multiples functions like a function to rotate, to draw a line etc.
Thank you for your help.
Put your transformations, path drawings & image drawing into a function with arguments that tell the function how each image will be treated:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house32x32transparent.png";
function start(){
// Note: img coordinates are [centerX,centerY] rather than the usual [left,top]
drawTransformedImage(img,25,50,0,.75);
drawTransformedImage(img,75,50,Math.PI*1/6,1);
drawTransformedImage(img,150,50,Math.PI*2/6,2);
drawTransformedImage(img,225,50,Math.PI*3/6,1);
drawTransformedImage(img,275,50,Math.PI*4/6,.5);
}
function drawTransformedImage(img,cx,cy,radAngle,scale){
// save incoming styling
var lw=ctx.lineWidth;
var ss=ctx.strokeStyle;
// cache often used half-sizes
var iwHalf=img.width/2;
var ihHalf=img.height/2;
ctx.lineWidth=2;
// do the specified transformations
ctx.translate(cx,cy);
ctx.rotate(radAngle);
ctx.scale(scale,scale);
// draw the image
ctx.drawImage(img,-iwHalf,-ihHalf);
// stroke some paths
ctx.beginPath();
ctx.moveTo(-iwHalf,ihHalf);
ctx.lineTo(-iwHalf,-ihHalf);
ctx.strokeStyle='orange';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(-iwHalf,-ihHalf);
ctx.lineTo(+iwHalf,-ihHalf);
ctx.strokeStyle='blue';
ctx.stroke();
// clean up: reset transformations and stylings
ctx.setTransform(1,0,0,1,0,0);
ctx.lineWidth=lw;
ctx.strokeStyle=ss;
}
body{ background-color: white; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=150></canvas>
Transforming an Image
Your example does not show a image being transformed, making your question unclear.
The transform is independent of the image, it is used to transform pixel coordinates drawn onto the canvas. It does not affect the image. You can set the transform and then draw the 200 images and they will all have the same transformation applied when their content is rendered to the canvas.
Code example
To transform the image you must create a canvas, set the transform, then render the image onto that canvas. The canvas is now the transformed image.
An example of transforming an image.
var mirrorImage = function (image, vertical, horizontal) {
var imageResult, ctx, vF, hF, posX, posY;
// create new canvas
imageResult = document.createElement("canvas");
// set the pixels size to match the image
imageResult.width = image.width;
imageResult.height = image.height;
// create a drawable surface
ctx = imageResult.getContext("2d");
// create the mirror transformation
hF = horizontal ? -1, 0;
vF = vertical ? -1 : 0;
posX = horizontal ? image.width, 0;
posY = vertical ? image.height : 0;
// Apply the transform to the new image
ctx.setTransform(hF, 0, 0, vF, posX, posY);
// transform the original image by drawing it onto the new
ctx.drawImage(image, 0, 0);
// return the new image.
return imageResult;
}
// create image
var img = new Image();
img.src = "ship.png";
// when loaded transform the image
img.onload = function () {
img = mirrorImage(img, true, true);
// the image has been transformed.
}
To do that to 200 images you have to call mirrorImage (or what ever you are doing) for each image.