Canvas moving shadow after clearRect - javascript

I am new to HTML5 and start learning canvas.
Currently, I am using canvas to make some objects rotate. The rectangle I created do move, however, I suffer from a problem, after the object move, some shadows remain as you can see from the image I captured.
I just want to have the rectangle, and not including the blue background clumsy stuff. I try to use different browsers to view this HTML5 document, but same problem comes out. Is this a problem of my computer, or is it a problem of the code? If so, how can I solve it?
I have also attached my source code of rotating rectangle example in jsFiddle: http://jsfiddle.net/hphchan/ogoj9odf/1/
Here is my key code:
In Javascript:
function canvaScript() {
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.translate(200, 200); // fix the origin as center of the canvas
rotating(context);
}
function rotating(context) {
context.clearRect(-50, -100, 100, 200); // why boundaries, shadows exist??
context.rotate(Math.PI/180);
context.fillStyle = '#0000FF';
context.fillRect(-50, -100, 100, 200);
setTimeout(function() {rotating(context)}, 100);
}
In HTML
<body onload="canvaScript()">
<canvas id="myCanvas" width="400" height="400"></canvas>
</body>
Thanks for answering.

This problem probably comes from the anti-aliasing.
You can see it by clearing directly after you drawn your rotated shape :
function canvaScript() {
var context = canvas.getContext('2d');
context.translate(200, 200); // fix the origin as center of the canvas
context.rotate(Math.PI/4);
rotating(context);
}
function rotating(context) {
context.fillStyle = '#0000FF';
context.fillRect(-50, -100, 100, 200);
context.clearRect(-50, -100, 100, 200);
}
canvaScript();
<canvas id="canvas" width="400" height="400"></canvas>
So one solution to workaround this is to clear a slightly larger clearRect than the rect you just drawn.
function canvaScript() {
var context = canvas.getContext('2d');
context.translate(200, 200); // fix the origin as center of the canvas
rotating(context);
}
function rotating(context) {
// clear one extra pixel in all directions
context.clearRect(-51, -101, 102, 202);
context.rotate(Math.PI/180);
context.fillStyle = '#0000FF';
context.fillRect(-50, -100, 100, 200);
setTimeout(function() {rotating(context)}, 100);
}
canvaScript();
<canvas id="canvas" width="400" height="400"></canvas>

Related

fabric.js disable drawing on black pixel

I am making a drawing app in fabric.js.
I want to ignore the black area of image to be colored in.
Any suggestions please????
Click the link for image
You can use the image as overlay.Please check here:http://jsfiddle.net/mariusturcu93/s5wxbcde/19/
JS
var canvas = new fabric.Canvas('canvas');
canvas.backgroundColor = "blue";
canvas.isDrawingMode=1;
canvas.setOverlayImage('https://vignette.wikia.nocookie.net/fantendo/images/6/6e/Small-mario.png/revision/latest/scale-to-width-down/381?cb=20120718024112', canvas.renderAll.bind(canvas), {
width: canvas.width,
height: canvas.height
});
HTML
<canvas id="canvas" width="800" height="800"></canvas>

How to bring one canvas context over the top of another?

I am using two images in a canvas, now i want to bring one image over another, ie. i want to bring the plane over the sky, how can i do that?
here is my code
var canvas = document.getElementById('canvas');
var skyContext = canvas.getContext('2d');
var planeContext = canvas.getContext('2d');
var sky = new Image();
sky.src = './images/m35.jpeg';
sky.onload = function () {
skyContext.drawImage(sky, 0, 0, 250, 250, 50, 50, 250, 250);
}
var plane = new Image();
plane.src = './images/space-ship.png';
plane.onload = function () {
planeContext.drawImage(plane, 0, 0, 70, 80, 50, 250, 70, 80);
}
In the above code, the sky is coming at the front of the plane making the plane invisible.
I also tried to use the same context like this but i am not able to bring the image at the top of another.
The most effective way to do this (especially as it looks like you're creating a game) is to use two separate canvas elements, positioned on top of each other using CSS.
For example:
var canvasMain = document.getElementById('canvasMain');
var canvasBackground = document.getElementById('canvasBackground');
var skyContext = canvasMain.getContext('2d');
var sky = new Image();
sky.src = 'https://i.stack.imgur.com/xjj19.jpg';
sky.onload = function () {
skyContext.drawImage(sky, 0, 0, 250, 250, 50, 50, 250, 250);
}
var planeContext = canvasBackground.getContext('2d');
var plane = new Image();
plane.src = 'https://i.stack.imgur.com/nHugQ.png';
plane.onload = function () {
planeContext.drawImage(plane, 0, 0, 70, 80, 50, 250, 70, 80);
}
.canvas {
position: absolute;
top: 0;
left: 0;
}
<canvas id="canvasMain" class="canvas" width="700" height="500"></canvas>
<canvas id="canvasBackground" class="canvas" width="700" height="500"></canvas>
The issue with your code is that the images are drawn once loaded and I would suspect that because the sky image is larger than the plane image it takes longer to load so the sky image is being drawn second.
Drawing things on canvas works like layers in photoshop with whatever is drawn last overwriting things drawn before it.
It is possible to use the same canvas for both the sky and the plane, you just need to wait until both images have loaded then draw them in the correct order to the sky is drawn first, then the plane on top.
You can use the same canvas context for both objects if the are on the same canvas, no need to create multiple contexts (2 just point to the same place anyway). Usually when I work with canvases I just create one context variable called ctx as its a lot quicker than typing context.etc all the time.
almcd answer is one way to do it, though to me the background and main are reversed; I would put the sky / galaxy on the background and draw the plane on the main canvas, but there is a better way...
If the sky never changes, 2 canvases is not even needed, just one canvas with a background image behind it; the sky. When it comes time to animate the plane, this method of having the background a static image means there is less to draw each frame of the game loop, so you will get better FPS.
In terms of a solution for you, I think 1 canvas with the sky as a background image positioned by CSS, and only the plane being drawn on the canvas is the best, here is the code for that...
<!doctype HTML>
<html>
<head>
<style>
#canvasContainer {
background-image: url('https://i.stack.imgur.com/xjj19.jpg');
background-repeat: no-repeat;
}
</style>
</head>
<body>
<div id="canvasContainer">
<canvas id="canvas" width="1200" height="600"></canvas>
</div>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// Load the plane and draw it.
var plane = new Image();
plane.src = 'https://i.stack.imgur.com/nHugQ.png';
plane.onload = function () {
ctx.drawImage(plane, 100, 100);
}
</script>
</body>
</html>
You can move the plane by changing the 100, 100 parameters sent to draw image, as these are the left and top used to position the plane image.
Kind regards,
DouG.
Creator of Winwheel.js a feature packed JavaScript library for making spinning prize wheels on HTML canvas. See http://dougtesting.net

FabricJS prevent canvas.clipTo from clipping canvas.backgroundImage

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

In the HTML5 canvas, is there a way to rotate images in different angles?

Currently there are are two images I would like to rotate on the canvas, I tried save and restore but didn't work
function SetCanvas()
{
var canvas = document.getElementById('pic1');
if(canvas.getContext)
{
var ctx = canvas.getContext('2d');
// ctx.save();
ctx.rotate(0.5);
var image = new Image();
image.src ='ME.JPG';
image.onload = function(){
ctx.drawImage(image, 90,0,200,100);
};
}
//ctx.restore();
var canvas2 = document.getElementById("pic2");
var image2 = new Image();
image2.src = 'ME2.JPG';
if(canvas2.getContext)
{
image2.onload = function(){
ctx2=canvas2.getContext('2d');
ctx2.drawImage(image2, 0,0,200,100);
};
}
}
<ul id="picsCanvas" style="overflow:hidden;white-space:nowrap; list-style-type:none;">
<li style=" display:inline; float:left" id="first">
<canvas ID="pic1" width="300" height="360" ></canvas>
</li>
<li id="second" style="margin-top:0px; display:inline; float:left; position:absolute ">
<canvas id="pic2" width="300" height="360" style="position:absolute" ></canvas>
</li>
</ul>
Please note that the code might not be correct as it is something I did a while ago, I just want to get an idea of how to do it and if it is possible... thanks for your help.
The images are loading asynchronously. This means that that entire function (minus the onload handlers for the images happens first. Then when the images are loaded, their handlers are called. This happens in a second pass. By the time this happens, you already rotated and restored the canvas, effectively wiping the rotation out.
The simple fix is to rotate and restore the canvas inside each of the image onload handlers.
These two links give a pretty good explanation and example of how to rotate with HTML5 canvas
https://developer.mozilla.org/en/Drawing_Graphics_with_Canvas
https://developer.mozilla.org/en/Canvas_tutorial/Basic_animations
You set the different angles when you rotate (see code example below).
The general gist of it is:
1) save the context
2) transform to, usually, the center of the image
3) rotate
4) transform back
5) draw image
6) restore
In your case, with two images, you need to transform the origin to the second image before you make the second rotation call. Below is a simplified example rotating one image. Get that sorted and then make the second transform/rotate.
Example:
var canvas = document.getElementById("yourCanvas");
var ctx = canvas.getContext("2d");
var angle = 0;
window.setInterval(function(){
angle = angle+1;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.fillStyle = "#FF0000";
// first image
ctx.translate(150,200);
ctx.rotate( angle*Math.PI/180 ); // rotate 90 degrees
ctx.translate(-150,-200);
ctx.fillStyle = "black";
ctx.fillRect(100, 150, 100, 100);
ctx.fill();
ctx.restore();
}, 5);​

Evaluating java script expression inside function argument

I am trying to define a scale variable (s), where the image scales dynamically according to its value. I don't think the expression s*100 is evaluating as there is no difference in size. What is wrong.
<html>
<body onload="draw();">
<canvas id="canvas" width="150" height="150"></canvas>
</body>
<script type="application/javascript">
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
var s = 10000;
ctx.fillStyle = "rgba(0, 0, 200, 0.1)";
ctx.fillRect (0, 0, s*100, s*100);
}
}
</script>
</html>
The reason you don't see any change is because you are trying to render a gigantic rectangle onto a 150x150 canvas, so the part outside the canvas is ignored. Make your canvas really big and you'll see it working. Here's a working example, it keeps rendering the same square by increasing the scale each time. http://jsfiddle.net/3ZAex/2/
Figured it out.. it was because my canvas was too small.. width="150" height="150"

Categories