Is it possible to skew an image on a canvas to make it appear as a reflection of another image?
Here is an illustration:
I need to flip and skew the image to get the desired effect, but I have not been able to get it to look like that. How can this be achieved?
I have found this example:
http://jsfiddle.net/jpnrzc9y/
ctx.save();
ctx.transform(1,0,0.3,-1,0,0);
ctx.drawImage(tree1,74,-117,20,40);
ctx.restore();
They seem to set the transform based on some random values.
But I cannot make sense of it. The values seem very random to me. Im trying to create a dynamic function that allows me to determine the amount of skew and that works for all images.
You can use context.scale to flip an image vertically:
// flip the canvas vertically
context.scale(1,-1);
Here are the steps to create a skewed reflection of an image:
Move (move==translate) the 0,0 origin to the desired x,y: ctx.translate(x,y);
Draw the original image image: ctx.drawImage(img,0,0);
Move to the bottom of the original image: ctx.translate(0,img.height);
Scale to vertically flip the image: ctx.scale(1,-1);
Apply a skew: ctx.transform(1,0,skewAngle,1,0,0);
Shrink the reflected image (just for aesthetics): ctx.scale(1,0.50);
Make the reflected image 50% opacity: ctx.globalAlpha=0.50;
Draw the reflected image: ctx.drawImage(img,0,-img.height);
Clean up by setting all transforms to default: ctx.setTransform(1,0,0,1,0,0);
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;
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/character1.png";
function start(){
// 60x110
skewedReflection(img,25,25)
}
function skewedReflection(img,x,y){
// calc the 45 degree skew angle needed by ctx.transform
var skewAngle=-Math.tan(Math.PI/4);
// move the 0,0 origin to x,y
ctx.translate(x,y);
// draw the original image image
ctx.drawImage(img,0,0);
// move to the bottom of the original image
ctx.translate(0,img.height);
// use scale to flip the image
ctx.scale(1,-1);
// apply a skew
ctx.transform(1,0,skewAngle,1,0,0);
// shrink the reflected image
ctx.scale(1,0.50);
// make the reflected image 50% opacity
ctx.globalAlpha=0.50;
// draw the reflected image
ctx.drawImage(img,0,-img.height);
// clean up by setting all transforms to default
ctx.setTransform(1,0,0,1,0,0);
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=300></canvas>
Related
I have a p5.js 3D sketch. I have to set its background image. I know that we can easily implement it with same background() function in a 2D sketch, but it is not possible in a 3D sketch.
I googled for some hacks and found that I can have a plane with the dimensions of the canvas at the back and then use texture() to set its background image like the below example.
var img;
function preload() {
img = loadImage("img.png");
}
function setup() {
createCanvas(600, 600, WEBGL);
}
function draw() {
background(220);
push();
texture(img);
plane(600, 600);
pop();
}
But the issue is - I have to use orbitControl() in my sketch. Due to this, the background (plane) also moves when I drag around. I want my background to be static (it should not move when I move other objects).
Finally, I resorted to css background-image property to set the background image of the canvas and remove background(255) in the draw() function. But due to this, the background (previous frames) were not overlapped when I dragged objects and were also visible.
How can I implement background image in this case?
My sketch is available at https://editor.p5js.org/praneetdixit1234/sketches/9pN4lA8KB
I think your CSS approach is good, just not sure why use cover on the size ...
Your image is 1365px wide but your canvas is only 600px, with cover most of it will be hidden, and the text will not be fully seen, but in the future if you change the image using cover might be fine.
Here is what I used
canvas {
display: block;
background-image: url("https://static.vecteezy.com/system/resources/previews/000/118/983/original/free-vector-cityscape.jpg");
background-size: contain;
background-repeat: no-repeat;
}
...and don't remove background, set the alpha background(0,0,0,0);
Here is the working sample:
https://editor.p5js.org/heldersepu/sketches/ll8txfcs0
I also added smooth() on my sample:
The difference is noticeable, read more about it here:
https://p5js.org/reference/#/p5/smooth
You should render your background and 3d objects to different image surfaces, and then combine them on the canvas.
Here's an example:
https://editor.p5js.org/space-haze/sketches/alWGuuXXe
var w = window.innerWidth;
var h = window.innerHeight;
function setup() {
// create the primary drawing surface
createCanvas(w, h);
// create a 3d off-screen surface
img3d = createGraphics(w,h,WEBGL);
}
function draw() {
// draw our background color/image to canvas
background(color('purple'));
// render our 3d objects to off-screen surface
let x = w/3;
img3d.fill(color('yellow'));
img3d.stroke(color('black'));
img3d.strokeWeight(2);
img3d.camera(x*2,x*2,x*2,0,0,0,0,1,0);
img3d.box(x);
// draw our off-screen surface to canvas
image(img3d,0,0);
}
I have overlay image with rectangle transparent hole. Beneath is scalable and draggable image. How to cut only visible part of image ?
How to determine size and position of transparent rectangle ? Is it possible to do pixel by pixel alpha channel search only on overlay image ?
Any other ideas ?
EDIT:
Solution to another quoted problem is usefull, although it works only on whole canvas not on individual items like background, overlay or added images or shapes. Is it possible to read pixel values on individual fabric elements ?
I use overlay image as external png files.
The FabricJS API does not include a method to fetch the pixel values of an image.
You will have to draw your overlay image onto an html5 canvas and use getImageData to fetch the pixel data from that canvas.
getImageData().data contains the red, green, blue & alpha information for each pixel on the canvas.
You can test each pixel's alpha value and determine the minimum & maximum boundary of the transparent rectangle.
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;
var img=new Image();
img.crossOrigin='anonymous';
img.onload=start;
img.src="http://masterblocks.co.in/static/img/logo.png";
function start(){
cw=canvas.width=img.width;
ch=canvas.height=img.height
ctx.drawImage(img,0,0);
// clear a test rectangle
ctx.clearRect(100,100,100,75);
// get clear rect bounds
var bounds=findCutoutBounds();
// test: stroke the bounds
ctx.lineWidth=2;
ctx.strokeStyle='red';
ctx.strokeRect(bounds.x,bounds.y,bounds.width,bounds.height);
}
// Get the imageData of that canvas
function findCutoutBounds(){
var minX=1000000;
var minY=1000000;
var maxX=-1;
var maxY=-1;
var data=ctx.getImageData(0,0,canvas.width,canvas.height).data;
for(var y=0;y<ch;y++){
for(var x=0;x<cw;x++){
var n=(y*cw+x)*4;
if(data[n+3]<5){
if(y<minY){minY=y;}
if(y>maxY){maxY=y;}
if(x<minX){minX=x;}
if(x>maxX){maxX=x;}
}
}}
return({x:minX,y:minY,width:maxX-minX,height:maxY-minY});
}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<h4>The bounding box of the transparent rectangle is stroked in red</h4>
<canvas id="canvas" width=300 height=300></canvas>
I have a jpeg image that has been decoded, and I am writing it to canvas with putImageData. Is it possible to then move this image around? I cannot find any documentation on it.
The idea is that I will crop a certain part of the decoded image with dirtyX and dirtyY, and now I would like to crop another part of the image. I'm using the decoded image like a spritesheet.
Use the clipping version of drawImage to draw your individual sprites to the canvas
(Don't use getImageData & putImageData for clipping since drawImage is easier and faster.)
If you want to use your decoded image as a spritesheet then you can use the canvas's clipping version of context.drawImage to clip a particular sub-section of your spritesheet and draw it to the canvas.
Here's a previous SO post about the clipping version of drawImage.
HTML / Java Script Canvas - how to draw an image between source and destination points?
Here's example code and a Demo:
Note that the way to "move" sprites on the canvas is to clear the canvas and redraw the desired sprite in it's new position. (You can't "move" an existing drawing on the canvas).
$(window).load(function(){
// canvas related variables
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
// animation related variables
var lastFlap,lastMove;
// define a bird object
// x,y are the position of the bird on the canvas
// spriteX,spriteY is the position of the first desired
// sprite image on the spritesheet
// width,height is the size of 1 sprite image
// currentFrame is the index of which of the sprite images to display
// currentDirection. The sprite plays forward and then backward to
// accomplish 1 flap. This determines if the next frame index will
// be increased (play forward) or decreased (play backward)
var bird={
x:30,
y:30,
spriteX:0,
spriteY:52,
width:51,
height:51,
frames:4,
currentFrame:0,
currentDirection:1
}
// load the spritesheet and start the animation
var spritesheet=new Image();
spritesheet.onload=start;
spritesheet.src="https://dl.dropboxusercontent.com/u/139992952/multple/birdSpritesheet.png";
function start(){
requestAnimationFrame(animate);
}
function animate(time){
// request another animation frame
if(bird.x<canvas.width){
requestAnimationFrame(animate);
}
// if the lastFlap or lastMove times don't aren't set, then set them
if(!lastFlap){lastFlap=time;}
if(!lastMove){lastMove=time;}
// calculate the elapsed times since the last flap and the last move
var elapsedFlap=time-lastFlap;
var elapsedMove=time-lastMove;
// if 50ms have elapsed, advance to the next image in this sprite
if(elapsedFlap>50){
// advance to next sprite on the spritesheet (flap)
bird.currentFrame+=bird.currentDirection;
// clamp bird.currentFrame between 0-3 (0,1,2,3)
// (because there are 4 images that make up the whole bird sprite)
if(bird.currentFrame<0 || bird.currentFrame>bird.frames-1){
bird.currentDirection*=-1;
bird.currentFrame+=bird.currentDirection;
}
// reset the flap timer
lastFlap=time;
}
// locate the current sprite from the spritesheet
var sx=bird.spriteX+bird.currentFrame*bird.width;
var sy=bird.spriteY;
// if 100ms have elapsed, move the bird across the canvas
if(elapsedMove>100){
bird.x+=3;
lastMove=time;
}
// clear the whole canvas
ctx.clearRect(0,0,canvas.width,canvas.height);
// draw the current part of the bird sprite at the current bird.x
ctx.drawImage(spritesheet,
sx,sy,bird.width,bird.height,
bird.x,bird.y,bird.width,bird.height
);
}
}); // end $(function(){});
body{ background-color: white; }
canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>The canvas animating a clipped sprite</h4>
<canvas id="canvas" width=300 height=100></canvas>
<br>
<h4>The spritesheet</h4>
<img id=spritesheet src='https://dl.dropboxusercontent.com/u/139992952/multple/birdSpritesheet.png'>
Yes and No :-)
Since canvas draws in immediate-mode it doesn't know that you have drawn an image on the canvas. So, you cannot draw it on the canvas one single time and move the painted image around. You will have to manually redraw the complete image in each animationframe at a new postion.
You can do that using the requestAnimationFrame() as Jonas Grumann explains.
It's my first time to use html5 canvas and I have no idea yet how it works.
My problem is, I have to modify the colors of the images in the canvas. It's easy if there's only one image. But, I will have more than one, in other words, overlapping images.
To further understand my question, I created an illustration. There will be 2 image files only, Image 1 and Image 2:
This is my current code (with fiddle here too):
HTML:
<canvas id="canvas1" width="600" height="600"></canvas>
JS:
var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');
var ctx2 = can.getContext('2d');
ctx.fillStyle = 'yellow'; // background color. box in the middle is transparent. try changing this to see the effect
ctx.fillRect(40,0,250,300); // not sure if there's other way to fill in the tranparent area. but I created a box behind the image
var img = new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0);
}
img.src = "http://s7.postimg.org/aruxhs8mz/pink.png"; //image 1
// I want to fill in the paw image too
/*ctx2.fillStyle = 'purple';
ctx2.fillRect(40,0,500,500); */
//should I declare something like this again?
var img2 = new Image();
img2.onload = function() {
ctx2.drawImage(img2, 0, 0);
}
img2.src = "http://s7.postimg.org/69smposl7/paw.png"; //image 2
//paw initially colored light blue. i would like to customize the color of this too
I should be able to fill the paw image in the middle as well and not just the main image. How to do that?
I have created a fiddle just to enlighten you with my question.
Hope someone can help me for any advice.
Thanks a bunch!
You can do your task with Compositing.
Compositing tells the canvas what to do when drawing additional new drawings (pixels) on the canvas.
In your case, 3 compositing modes are useful to learn.
Source-over Compositing
The default method of compositing is 'source-over' where new drawings are drawn over existing pixels.
// first draw a blue destination rectangle
ctx.fillStyle='blue';
ctx.fillRect(30,30,50,50);
// second draw a red source rectangle
ctx.fillStyle='red';
ctx.fillRect(60,60,50,50);
then results
Source-atop Compositing
'source-atop' compositing will draw new pixels only where the new pixels overlap the existing canvas pixels.
// first draw a blue destination rectangle
ctx.fillStyle='blue';
ctx.fillRect(30,30,50,50);
// set compositing to 'source-atop'
// (the new red pixels will only be drawn where
// they overlap the existing blue pixels)
ctx.globalCompositeOperation='source-atop';
// second draw a red source rectangle
// (red will overwrite only where it overlapped the blue)
ctx.fillStyle='red';
ctx.fillRect(60,60,50,50);
then results
Destination-over Compositing
'destination-over' compositing will draw new pixels under the existing canvas pixels.
// first draw a blue destination rectangle
ctx.fillStyle='blue';
ctx.fillRect(30,30,50,50);
// set compositing to 'source-atop'
// (the new red pixels will only be drawn where
// they overlap the existing blue pixels)
ctx.globalCompositeOperation='destination-over';
// second draw a red source rectangle
// (red will appear under the blue)
ctx.fillStyle='red';
ctx.fillRect(60,60,50,50);
then results
Here's how to use compositing to change the color of your paw.
Clear the canvas. You cannot directly change the color of anything previously drawn on the canvas, so the typical workflow of canvas is to erase it and redraw items in their new positions & colors.
Draw the paw image.
Set compositing to source-atop so new drawings will only be drawn where the existing paw pixels exist.
Fill the canvas with your new paw color using fillStyle & fillRect. This causes your paw to be recolored because the newly colored rectangle pixels will only appear where your paw pixels currently exist.
Set compositing to destination-over so new drawings will be drawn under existing pixels.
Fill the yellow box. Your paw will not be overwritten because new (yellow) pixels will be drawn "under" your paw.
Set compositing back to the default source-over so new drawings will be drawn on top of existing drawings.
Draw your frame that's transparent in the center. Your paw and the yellow background will show through the transparent center of your frame.
Here's example code and a Demo:
var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');
var ctx2 = can.getContext('2d');
var images=[];
var urls=[];
urls.push('http://s7.postimg.org/aruxhs8mz/pink.png');
urls.push('http://s7.postimg.org/69smposl7/paw.png');
var imgCount=urls.length;
document.getElementById('recolor').onclick=function(){
redrawWithNewPawColor();
};
for(var i=0;i<urls.length;i++){
images[i]=new Image();
images[i].onload=myOnload;
images[i].src=urls[i];
}
function myOnload(){
imgCount--;
if(imgCount>0){return;}
start();
}
function start(){
redrawWithNewPawColor()
}
function drawWithRecoloredPaw(newPawColor){
// clear the canvas
ctx.clearRect(0,0,can.width,can.height);
// draw the paw
ctx.drawImage(images[1], 0, 0);
// set compositing to source-atop
// so only existing pixels will be overwritten
ctx.globalCompositeOperation='source-atop';
// fill with new color
ctx.fillStyle=newPawColor;
// Because of compositing, only the paw is being color filled
ctx.fillRect(0,0,can.width,can.height);
// set compositing to destination-over
// so new pixels will be draw behind existing (paw) pixels
ctx.globalCompositeOperation='destination-over';
// change the fill color to yellow
ctx.fillStyle='yellow';
// fill the yellow box
ctx.fillRect(40,0,250,300);
// set compositing to the default of source-over
ctx.globalCompositeOperation='source-over';
// draw the transparent frame
ctx.drawImage(images[0],0,0);
}
function redrawWithNewPawColor(){
drawWithRecoloredPaw(randomColor());
}
function randomColor(){
return('#'+Math.floor(Math.random()*16777215).toString(16));
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<button id='recolor'>Recolor Paw</button>
<br>
<canvas id="canvas1" width=600 height=600></canvas>
the problem is that canvas does not allow you to adjust an image, but you could create a block over the paw wich has an opacity of 0.5 and than fill in the transparent part again, you could also use this:
http://www.w3schools.com/tags/canvas_globalcompositeoperation.asp
If the image is in your own web folder you can use it with an img tag and get the data an change it
much like the W3S example Changing image in canvas
after this step it's just a matter of redrawing the canvas with the new data
This will not work if the image comes from another source, becaus of security reasons
Let say I got the sprite to the left and I want to apply a red filter over the image before drawing it in a canvas so it looks like the sprite to the right. http://puu.sh/6dQ0E.png
Is there a way to do that?
It could be done in two steps, but I don't know if it's possible to draw the geometric figure that has the exact same shape than the image. (Ex: Drawing a rectangle over it won't match.)
Note: I want the filter to only be applied to this image, not the whole canvas.
You can combine 2 filtering operations to draw the red filter only on top of your existing sprite.
globalCompositeOperation="source-atop" will cause new drawings to only draw on top of existing non-transparent pixels.
globalAlpha will make your new drawings semi-transparent.
Taken together, you can draw a semi-transparent filter that only fills where your non-transparent ghost exsits.
Then just draw a red rectangle over your existing sprite (the rectangle will only be visible inside the existing ghost).
ctx.drawImage(ghostSprites,0,0);
ctx.globalAlpha=0.62;
ctx.globalCompositeOperation="source-atop";
ctx.fillStyle="red";
ctx.fillRect(spriteX,spriteY,spriteWidth,spriteHeight);
Demo: http://jsfiddle.net/m1erickson/W4XrG/
From here...
Notice the black outlines of your sprite become washed from the red filter.
You could also use context.getImageData to grab only the black pixels of your ghost. Then redraw those black pixels over your red-filtered ghost so the black outlines are not washed. If you feel ambitious, you could give that a try!
Good luck with your project!
Here’s code
<style>
body{ background-color: ivory; padding:20px;}
#canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var img=new Image();
img.onload=start;
img.crossOrigin="anonymous";
img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/temp0a.png";
function start(){}
var spriteX=0;
var spriteY=0;
var spriteWidth=133;
var spriteHeight=161
$("#recolor").click(function(){
ctx.drawImage(img,0,0);
ctx.globalAlpha=0.62;
ctx.globalCompositeOperation="source-atop";
ctx.fillStyle="red";
ctx.fillRect(spriteX,spriteY,spriteWidth,spriteHeight);
});
}); // end $(function(){});
</script>
</head>
<body>
<p>Before</p>
<img src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/temp0a.png">
<p>After</p>
<button id="recolor">Click to recolor the green sprite</button><br>
<canvas id="canvas" width=300 height=161></canvas>
</body>
</html>