I want to either remove, change the colour of or change the position of the second line.
Also, is there a way to make the colour of an already created line different?
var cvs = document.querySelector('#canvas')
var ctx = cvs.getContext('2d')
cvs.height = window.innerHeight - 40
cvs.width = window.innerWidth - 40
window.addEventListener('resize', () => {
cvs.height = window.innerHeight - 40;
cvs.width = window.innerWidth - 40;
})
ctx.beginPath();
ctx.strokeStyle = 'red'
ctx.lineWidth = '5'
ctx.lineCap = 'round'
ctx.moveTo(200, 600)
ctx.lineto(100,100)
ctx.closePath();
//Second path which is what i want to remove only
ctx.beginPath();
ctx.strokeStyle = 'blue'
ctx.lineWidth = '37px'
ctx.moveTo(100,100)
ctx.lineTo(300, 500)
ctx.stroke()
ctx.closePath()
Objects painted on the canvas cannot be modified, but they can be painted over. Unlike HTML or SVG, the the canvas does not have its own DOM, so there is no way to access objects that have already painted on the canvas.
Approach 1:
The most common approach is to clear the canvas, adjust any attributes (position, color, etc), and repaint the objects onto the canvas.
ctx.clearRect(0, 0, cvs.width, cvs.height) //removes everything from the canvas
ctx.beginPath();
ctx.strokeStyle = 'red'
ctx.lineWidth = '5'
ctx.lineCap = 'round'
ctx.moveTo(200, 600)
ctx.lineTo(100,100)
ctx.stroke();
ctx.closePath(); //repaints the first object, the second path is gone.
Pros:
more robust: will work in all situations (even when objects overlap)
Scales well: better when lots of objects are painted
Cons:
every object has to be repainted on the canvas. (removes everything)
Approach 2:
The closest approach to removing a single object, it paints over the existing object and repaint it somewhere else.
ctx.beginPath();
ctx.strokeStyle = 'white' //background color
ctx.lineWidth = '37px'
ctx.moveTo(100,100);
ctx.lineTo(300, 500);
ctx.stroke();
ctx.closePath(); // covers up the second path
ctx.beginPath();
ctx.strokeStyle = 'blue'
ctx.lineWidth = '37px'
ctx.moveTo(100,100)
ctx.lineTo(600, 500) //repaint this object somewhere else
ctx.stroke()
ctx.closePath()
Pros:
Only removes the second path, leaves everything else
simpler when fewer objects are on the canvas
Cons:
Will not work when objects overlap.
Does not scale well: this solution will get more complex when more objects are being modified.
More resources:
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial
Related
I am trying to using context.clip() to clip a draw arc from other one and fill the clipped result.
But when i clip section & fill it, it gives pixelated fill .
var ctx = document.getElementById("canvas").getContext("2d");
var x = 150 ;
var y = 150 ;
var r = 100 ;
ctx.save() ;
ctx.translate(x,y) ;
ctx.beginPath() ;
ctx.arc(0,0,r,0,2*Math.PI);
ctx.closePath() ;
ctx.fillStyle = "cyan" ;
ctx.fill() ;
ctx.lineWidth = 10;
ctx.stroke();
ctx.restore() ;
ctx.save() ;
ctx.clip() ;
ctx.translate(x,y);
ctx.beginPath();
ctx.moveTo(r,-r-10);
ctx.arc(0,-r-10,r,0,Math.PI*2);
ctx.closePath();
ctx.fillStyle = "#f2f2f2";
ctx.fill();
ctx.lineWidth = 1;
ctx.stroke();
ctx.restore();
https://jsfiddle.net/x0d0n40z/1/
An alternative approach which eliminates the need for clip()/save()/restore() is to use a few steps of compositing.
Clipping mask is anti-aliased in some browsers while in other not. To obtain consistency (and in some cases also performance since save-clip-restore are relative expensive operations) using composition is preferred if possible.
In this case:
Fill main arc in target color
Define a clipping arc
Change composite mode to destination-out and fill (will cut main)
Change composite mode to source-atop and stroke (will outline cut)
Change composite mode to source-over and stroke outline of main circle
Example
Update: Simplified steps (with the last step merged into the process, ref. comments). I also chose to demonstrate use of the Path2D since we can reuse the object without interfering with the path on the ordinary context -
var ctx = c.getContext("2d"),
p = new Path2D(), // this will store main shape for reuse
x = 75, y = 75, radius = 70;
// main arc
p.arc(x, y, radius, 0, 6.28); // store to path object
ctx.fillStyle = "cyan";
ctx.fill(p); // fill path object
// clip top arc
ctx.globalCompositeOperation = "source-atop";
ctx.arc(x, y - radius, radius, 0, 6.28);
ctx.fillStyle = "#09f";
ctx.fill();
ctx.lineWidth = 5;
ctx.stroke();
// stroke main arc
ctx.globalCompositeOperation = "source-over";
ctx.stroke(p); // stroke path object
body {background:#e9e9e9}
<canvas id=c></canvas>
Old version:
var ctx = c.getContext("2d"),
x = 75, y = 75, radius = 70;
// main arc
ctx.arc(x, y, radius, 0, 6.28);
ctx.fillStyle = "cyan";
ctx.fill();
// clipping arc
ctx.beginPath();
ctx.arc(x, y - radius, radius, 0, 6.28);
// cut step
ctx.globalCompositeOperation = "destination-out";
ctx.fill();
// stroke gap step
ctx.globalCompositeOperation = "source-atop";
ctx.lineWidth = 10;
ctx.stroke();
// stroke whole outline
ctx.globalCompositeOperation = "source-over";
ctx.beginPath();
ctx.arc(x, y, radius, 0, 6.28);
ctx.lineWidth = 5;
ctx.stroke();
// if you want to color the clip then use this:
ctx.globalCompositeOperation = "destination-atop";
ctx.fillStyle = "#09f";
ctx.fill();
body {background:#e9e9e9}
<canvas id=c></canvas>
The problem is that the clip boundary is not being anti alised.
To solve you can render the shape without using the clip. The ctx.arc method lets you set the start and end angles so you can get the inset by filling two arcs.
You will need to get the angles where the clip circle and the inset circle intercept.
For this case it is very simple. First get the distance between the circles, and the angle from one to the other. This works only for two circles of same radius.
var c = {x:?,y:?}; // circle one location
var c1 = {x:?,y:?}; // circle two location
var radius = ?; // radius of both
var angle = Math.atan2(c1.y - c.y, c1.x - c.x); // get the angle from one to the next
var dist = Math.hypot(c1.x - c.x, c1.y - c.y); // get the distance. NOTE IE does not have hypot so do it the normal way with Math.sqrt....
Now you have the angle and distance the intercepts are a simple relationship between the distance and the radius
var iAngle = Math.acos(dist / 2 / radius); // the angle from the line between the circles
// to the intercepts
Now you have that angle you can draw the two arcs
ctx.beginPath();
ctx.arc(c.x,c.y,radius,angle - iAngle , angle + iAngle); // first arc
ctx.arc(c1.x,c1.y, radius, angle + Math.PI - iAngle, angle + Math.PI + iAngle); // second arc
ctx.fill();
ctx.stroke();
There is not much you can do to prevent the jaggies from effecting the clip area. Another way to achieve clipping is to use ctx.globalCompositeOperation to render a mask. You can mask in and out, and many more options. This will be a better solution when the clipping area becomes more complex.
I finally figured the right way to correct the bug .
Heres the clean result of what i wanted https://jsfiddle.net/x0d0n40z/6/
Code :
var ctx = document.getElementById("canvas").getContext("2d");
var r = 50
x = ctx.canvas.width/2;
y = ctx.canvas.height/2;
var offset = 60;
ctx.save();
ctx.setTransform(1,0,0,1.5,x,y);
ctx.beginPath();
ctx.arc(0,0,r,0,2*Math.PI);
ctx.stroke();
ctx.clip();
ctx.beginPath();
ctx.arc(0,0,r,0,2*Math.PI,false);
ctx.fillStyle = "cyan";
ctx.fill();
ctx.setTransform(1, 0, 0, 1, x, y);
ctx.beginPath();
ctx.arc(0,-offset,r,0,2*Math.PI,false);
ctx.fillStyle = "#f2f2f2";
ctx.fill();
ctx.lineWidth = 1 ;
ctx.stroke();
ctx.setTransform(1,0,0,1.5,x,y);
ctx.beginPath();
ctx.arc(0,0,r,0,2*Math.PI,false);
ctx.lineWidth = 3 ;
ctx.stroke();
ctx.restore();
Source from were i learned to use clip : http://www.html5canvastutorials.com/advanced/html5-canvas-clipping-region-tutorial/
When I apply the shadow effect to the Sun variable, it automatically applies to the Neptune one as well. I don't want this. Do I give ctx a different name to fix this or does the issue lie somewhere else?
I've been told to keep just one context reference, so I'm terribly confused. I feel I need to shuffle some lines around to be able to give each variable unique properties.
Here's my code for you to see:
function initCanvas(){
var ctx = document.getElementById('my_canvas').getContext('2d');
var cW = ctx.canvas.width, cH = ctx.canvas.height;
var rectW = 40;
var rectH = 0;
var rectX = (ctx.canvas.width * .5) - (Math.PI* 1 * .5);
var rectY = (ctx.canvas.height * .5) - (rectH * .5);
var Starsystem = {
Neptune: {
x: 180,
y: 300,
render: function(){
Starsystem.Neptune.x = Starsystem.Neptune.x + 0;
Starsystem.Neptune.y = Starsystem.Neptune.y - 0;
ctx.fillStyle = "rgb(65,105,225)";
ctx.beginPath();
ctx.arc( Starsystem.Neptune.x , Starsystem.Neptune.y, 10, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
}
}, Sun: {
render: function(){
ctx.fillStyle = "rgb(255,255,51)";
ctx.beginPath();
ctx.arc(rectX, rectY, rectW, rectH, Math.PI*2, true); //alligns the sun in center
ctx.closePath();
ctx.fill();
ctx.shadowColor = 'yellow';
ctx.shadowBlur = 50;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
//Still applied to all
}
}
}
I only found 2 issues, and that was that you were setting it globally and you had the shadow AFTER you created the sun. You can move it wherever you'd like, but if you wanted it applied to your "Sun", then it would be best done like this.
The way I've found best to stop that problem is by wrapping my change code in save() and restore() functions. Like this:
}, Sun: {
render: function(){
ctx.fillStyle = "rgb(255,255,51)";
ctx.save(); //store ctx so it can be later reused.
ctx.shadowColor = 'yellow';
ctx.shadowBlur = 50;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.beginPath();
ctx.arc(rectX, rectY, rectW, rectH, Math.PI*2, true); //alligns the sun in center
ctx.closePath();
ctx.fill();
ctx.restore(); //ctx at time of save.
Working Fiddle: https://jsfiddle.net/gregborbonus/g1d6jkh7/
Keep one context, yes, but you have to bear in mind that any properties you set on the drawing context, will be remembered for future draw operations. There's a couple of ways round this.
a) ensure that you always set the desired properties prior to any draw operations. Especially ones that may have been overwritten by another object's draw operations.
b) Use context.save() and context.restore(). You can save the state of the context, make changes, do your draw operations and then restore back to its previous state, ready for the next draw operation, ensuring that you have not messed about with any other draw properties for other objects.
http://www.tutorialspoint.com/html5/canvas_states.htm
You need to set the shadow properties before using the fill() method.
ctx.shadowColor = 'yellow';
ctx.shadowBlur = 50;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.fill();
I'm trying to use globalCompositeOperation on an object within a <canvas> element but my goal is to blend with an object outside of the canvas - a plain html markup element like a paragraph.
My end goal will be inverting the content on the page using difference like so
My existing code is below. Is this even possible?
var canvas = document.getElementById('canvas');
window.onresize=function(){
"use strict";
var winMin = Math.min(window.innerWidth,window.innerHeight);
canvas.width = winMin;
canvas.height = winMin;
var w = winMin / 3;
var ctx = canvas.getContext('2d');
ctx.globalCompositeOperation = 'multiply';
ctx.globalAlpha = .5;
//magenta
ctx.fillStyle = 'rgb(255,0,255)';
ctx.beginPath();
ctx.arc(w, w, w, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
//cyan
ctx.fillStyle = 'rgb(0,255,255)';
ctx.beginPath();
ctx.arc(w*2, w, w, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
};
window.onresize();
Codepen: http://codepen.io/jeremypbeasley/pen/NqwGoO
The globalCompositeOperation blending operations define how pixels backed by the canvas element blend with fragments to be written to that backing. That has nothing to do with pixels that live in some other dimension of the web page, like the DOM. Total rasterization of the canvas occurs and some other graphics system composites the pixels of the canvas onto the pixels of the rest of the web page. Reflow of the web page could happen at any time, but that does not mean that the canvas would be re-rasterized, just re-composited, in which case the globalCompositeOperations would have no effect and you wouldn't see the photo negative effect you desire.
Does the beginPath and the closePath have to be included for this line to draw or for all of the graphics. I have the new HTML 5 Canvas book but this I was not completely certain of. I commented out the two lines and the line still displayed. What is the significance of these two lines.
Question: What does the beginPath() and closePath() do?
Code:
context.lineJoin='round';
context.lineCap='butt';
context.beginPath();
context.moveTo(10, 100);
context.lineTo(35, 100);
context.lineTo(35,125);
context.stroke();
context.closePath();
No, beginPath and closePath are not necessary.
A canvas context has a current path. You can add instructions to that path with methods such as moveTo and lineTo, among others. When you're done constructing the path, you can use methods such as stroke and fill, which draw on the canvas using the current path.
closePath is just another instruction you can add. You may not notice its effect when using fill, but when using stroke, it will (essentially) do a line back to the starting position, ‘closing’ the path. Compare and contrast:
ctx.moveTo(10, 10); ctx.moveTo(10, 10);
ctx.lineTo(90, 10); ctx.lineTo(90, 10);
ctx.lineTo(90, 90); ctx.lineTo(90, 90);
ctx.closePath();
ctx.stroke(); ctx.stroke();
beginPath, on the other hand, discards the previous path and lets you start a new one. Without it, you'd be appending more and more to the previous path, which may be undesirable. Compare and contrast:
ctx.moveTo(10, 10); ctx.moveTo(10, 10);
ctx.lineTo(90, 10); ctx.lineTo(90, 10);
ctx.lineWidth = 4; ctx.lineWidth = 4;
ctx.strokeStyle = "red"; ctx.strokeStyle = "red";
ctx.stroke(); ctx.stroke();
ctx.beginPath();
ctx.moveTo(10, 20); ctx.moveTo(10, 20);
ctx.lineTo(90, 20); ctx.lineTo(90, 20);
ctx.lineWidth = 2; ctx.lineWidth = 2;
ctx.strokeStyle = "blue"; ctx.strokeStyle = "blue";
ctx.stroke(); ctx.stroke();
beginPath() clears the old path so you can define a new one.
closePath() connects the first point with the last point and is not needed in your example. In any case it would have be used before stroking of filling to have an effect on the rasterized result.
I am trying to change the color of line drawn on canvas dynamically...
ctx.moveTo(0, 0);
ctx.lineTo(0, 200);
ctx.strokeStyle = "Grey"
It could be mouseover event or press botton or mouse click event, I want to change the color of line or make it bold. Is it possible to change the color by adding event or is it possible to give style on an event on particular element?
Very close. In a sense, you can't really "change" the color of an element on the canvas because it has no scene graph, or, in other words, it has no history of what has been drawn on the canvas. To change the color of a line, you would have to redraw the line.
ctx.moveTo(0, 0);
ctx.lineTo(0, 200);
ctx.strokeStyle = "Grey";
ctx.stroke();
// To make the line bold and red
ctx.moveTo(0, 0);
ctx.lineTo(0, 200);
ctx.strokeStyle = "Red";
ctx.lineWidth = 5;
ctx.stroke();
If the canvas had a more complex scene going on, you would have to redraw the entire scene. There are numerous Javascript libraries that extend the base features of the canvas tag, and provide other drawing capabilities. You may want to take a look at Processing, it looks quite impressive.
I was having the same problem, I did it by moving another line with another color of different canvas element, so it gives appearance of line changing its color dynamically.
function drawGreyLine() {
ctx1.clearRect(0, 0, WIDTH, HEIGHT);
ctx1.strokeStyle = "Grey"; // line color
ctx1.moveTo(0, 0);
ctx1.moveTo(0, 200);
ctx1.lineTo(200, 200);
}
function drawColorLine() {
x += dx;
if (x <= 200) {
ctx2.beginPath();
ctx2.lineWidth = 5;
ctx2.lineCap = "round";
ctx2.strokeStyle = "sienna"; // line color
ctx2.moveTo(0, 200);
ctx2.lineTo(x, 200);
ctx2.moveTo(200, 200);
ctx2.stroke();
}
}
Hope this solves your problem.... :)
var canvas = document.getElementById('canvas');
var ctx=canvas.getContext('2d');
var line1={x:10,y:10, l:40, h:1}
var down=false;
var mouse={x:0,y:0}
canvas.onmousemove=function(e){ mouse={x:e.pageX-this.offsetLeft,y:e.pageY-this.offsetTop};
this.onmousedown=function(){down=true};
this.onmouseup=function(){down=false} ;
}
setInterval(function(){
ctx.clearRect(0,0,canvas.width,canvas.height)
ctx.beginPath()
ctx.moveTo(line1.x,line1.y)
ctx.lineTo(line1.x +line1.l,line1.y)
ctx.lineTo(line1.x +line1.l,line1.y+line1.h)
ctx.lineTo(line1.x,line1.y+line1.h)
ctx.isPointInPath(mouse.x,mouse.y)? (ctx.fillStyle ="red",line1.x=down?mouse.x:line1.x,line1.y=down?mouse.y:line1.y):(ctx.fillStyle ="blue")
ctx.fill()
},100)