CanvasRenderingContext2D.fill() does not seem to work - javascript
I'm making a simply polygon draw method in HTML5 canvas. It will successfully outline the shape, but it will not fill it, even though it has been instructed to:
function PhysicsObj(Position /*Vector*/,Vertices /*Vector Array*/)
{
this.Position = Position
this.Vertices = Vertices
this.Velocity = new Vector(0,0);
this.Colour = "rgb(100,100,100)";
this.draw = function()
{
ctx.beginPath();
for(var point=0; point<Vertices.length-1; point++)
{
ctx.moveTo(Vertices[point].X+Position.X,Vertices[point].Y+Position.Y);
ctx.lineTo(Vertices[point+1].X+Position.X,Vertices[point+1].Y+Position.Y);
}
ctx.moveTo(Vertices[point].X+Position.X,Vertices[point].Y+Position.Y);
ctx.lineTo(Vertices[0].X+Position.X,Vertices[0].Y+Position.Y);
ctx.closePath();
ctx.fillStyle = this.Colour;
ctx.fill();
ctx.strokeStyle = this.Colour;
ctx.stroke();
}
}
var Polygon = new PhysicsObj(new Vector(100,100),[new Vector(50,50),new Vector(-50,50), new Vector(0,125)]);
Polygon.draw();
The method simply takes several vertices, and connects them into a path. It simply will not fill; I cannot figure out how to use the fill method.
Eliminate the extra moveTo commands:
var PositionX=50;
var PositionY=50;
var Vertices=[
{X:10,Y:10},
{X:100,Y:10},
{X:50,Y:50}
];
ctx.beginPath();
ctx.moveTo(Vertices[0].X+PositionX,Vertices[0].Y+PositionY);
for(var point=1; point<Vertices.length; point++)
{
ctx.lineTo(Vertices[point].X+PositionX,Vertices[point].Y+PositionY);
}
ctx.lineTo(Vertices[0].X+PositionX,Vertices[0].Y+PositionY);
ctx.closePath();
ctx.fillStyle = "red";
ctx.fill();
ctx.strokeStyle = "black";
ctx.stroke();
Related
How to style closePath() in canvas?
I am trying to style the closePath() on my canvas but am at a loss on how to do that, as a matter of fact I would actually like all the lines to have a different style, for this question lets stick to getting different colors. ! As you can see I have a triangle, how can I have differing strokestyles for each line? Here is link to the Code Pen const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); ctx.beginPath(); ctx.strokeStyle = "red"; ctx.moveTo(20, 140); ctx.lineTo(120, 10); ctx.strokeStyle = "green"; ctx.lineTo(220, 140); ctx.closePath(); ctx.strokeStyle = "blue"; ctx.stroke(); <canvas id="canvas"></canvas>
You would need to have the three lines as three separate paths. const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); ctx.beginPath(); ctx.moveTo(20, 140); ctx.lineTo(120, 10); ctx.strokeStyle = "red"; ctx.stroke(); ctx.beginPath(); ctx.moveTo(120, 10); ctx.lineTo(220, 140); ctx.strokeStyle = "green"; ctx.stroke(); ctx.beginPath(); ctx.moveTo(220, 140); ctx.lineTo(20, 140); ctx.strokeStyle = "blue"; ctx.stroke(); <canvas id="canvas"></canvas>
Each segment needs to be coloured. function qsa(sel,par=document){return par.querySelectorAll(sel)} window.addEventListener('load', onLoaded, false); function onLoaded(evt) { draw(); } class vec2d { constructor(x=0,y=0) { this.x = x; this.y = y; } } function draw() { var verts = [ new vec2d(20,140), new vec2d(120, 10), new vec2d(220,140) ]; var colours = ['red', 'green', 'blue']; let can = qsa('canvas')[0]; let ctx = can.getContext('2d'); var numLineSegs = verts.length; for (var lineSegIndex=0; lineSegIndex<numLineSegs; lineSegIndex++) { var index1 = lineSegIndex; var index2 = (lineSegIndex+1)%verts.length; ctx.beginPath(); ctx.strokeStyle = colours[index1]; ctx.moveTo(verts[index1].x, verts[index1].y); ctx.lineTo(verts[index2].x, verts[index2].y); ctx.stroke(); } ctx.closePath(); } <canvas width=512 height=512></canvas>
Javascript canvas: Set own color for two different objects (squares)
I'm real frustrated with this problem. So, I have following simple code: var canvas = document.getElementById('canvasField'); var ctx = canvas.getContext('2d'); function Square(x, y, sizeOfSide) { this.draw = function () { ctx.beginPath(); ctx.moveTo(x,y); ctx.lineTo(x + sizeOfSide, y); ctx.lineTo(x + sizeOfSide, y + sizeOfSide); ctx.lineTo(x, y + sizeOfSide); ctx.lineTo(x,y); ctx.closePath(); ctx.stroke(); } this.setColor = function (color) { ctx.fillStyle = color; ctx.fill(); } } Square is my object. I can draw square and probably I can set fill-color for it. So next code works fine. var square1 = new Square(100, 100, 100); var square2 = new Square(250, 200, 100); square1.draw(); square1.setColor('green'); square2.draw(); square2.setColor('yellow'); https://i.stack.imgur.com/gz50K.png But If I change it to this: var square1 = new Square(100, 100, 100); var square2 = new Square(250, 200, 100); square1.draw(); square2.draw(); square1.setColor('green'); square2.setColor('yellow'); it breaks down: https://i.stack.imgur.com/Qeojl.png It seems to me that I understand the reason. Two objects have the same context. And square2 sets color yellow for context and square1 loses his color. Maybe I am not right. I expected that they will be two independent objects and I'll be able to manipulate their conditions at any place in the code. I have no idea what to do next. Please help!
DEMO var canvas = document.getElementById('canvasField'); var ctx = canvas.getContext('2d'); function Square(x, y, sizeOfSide) { this.draw = function () { ctx.beginPath(); ctx.moveTo(x,y); ctx.lineTo(x + sizeOfSide, y); ctx.lineTo(x + sizeOfSide, y + sizeOfSide); ctx.lineTo(x, y + sizeOfSide); ctx.lineTo(x,y); ctx.closePath(); ctx.stroke(); } this.setColor = function (color) { this.draw(); ctx.fillStyle = color; ctx.fill(); } } //var square1 = new Square(100, 100, 100); //var square2 = new Square(250, 200, 100); //square1.draw(); //square1.setColor('green'); //square2.draw(); //square2.setColor('yellow'); var square1 = new Square(100, 100, 100); var square2 = new Square(250, 200, 100); //square1.draw(); //square2.draw(); square1.setColor('green'); square2.setColor('yellow'); canvas { border : 2px dotted blue; } <canvas id='canvasField' width=500 height=500></canvas> Call the draw function of object inside setColor function. So it will draw the square first then will fill it using given color.
Most of time when you want to change something on a canvas, you have to draw it again. Your first block of code is good, if you want to change color of a square, use setColor then draw it again.
How to simplify this code?
I’ve just started learning canvas and have tried so far a couple of exercises, but my code is always way too long and most probably unnecessarily complicated. I have the following code of a four leaf clover drawing and would like to know how to simplify it. Any suggestions? Thank you in advance! var clover = document.getElementById("clover"); var ctx = clover.getContext("2d"); //style: ctx.strokeStyle = "#006600"; ctx.lineWidth = 0.3; ctx.beginPath(); ctx.moveTo(115,80); ctx.bezierCurveTo(20,100,200,100,235,135); ctx.stroke(); //First leaf: ctx.strokeStyle = "black"; ctx.lineWidth = 0.8; ctx.fillStyle = "#7BA32D"; ctx.beginPath(); ctx.moveTo(55,70); ctx.quadraticCurveTo(20,100,115,80); ctx.stroke(); ctx.closePath(); ctx.fill(); ctx.fillStyle = "#7BA32D"; ctx.beginPath(); ctx.moveTo(55,70); ctx.quadraticCurveTo(40,30,115,80); ctx.stroke(); ctx.closePath(); ctx.fill(); // Second leaf: ctx.fillStyle = "#7BA32D"; ctx.beginPath(); ctx.moveTo(115,80); ctx.quadraticCurveTo(80,20,130,50); ctx.stroke(); ctx.closePath(); ctx.fill(); ctx.fillStyle = "#7BA32D"; ctx.beginPath(); ctx.moveTo(115,80); ctx.quadraticCurveTo(200,40,130,50); ctx.stroke(); ctx.closePath(); ctx.fill(); // Third leaf: ctx.fillStyle = "#7BA32D"; ctx.beginPath(); ctx.moveTo(115,80); ctx.quadraticCurveTo(235,60,185,85); ctx.stroke(); ctx.closePath(); ctx.fill(); ctx.fillStyle = "#7BA32D"; ctx.beginPath(); ctx.moveTo(115,80); ctx.quadraticCurveTo(190,115,185,85); ctx.stroke(); ctx.closePath(); ctx.fill(); // Fourth leaf: ctx.fillStyle = "#7BA32D"; ctx.beginPath(); ctx.moveTo(115,80); ctx.quadraticCurveTo(180,135,110,115); ctx.stroke(); ctx.closePath(); ctx.fill(); ctx.fillStyle = "#7BA32D"; ctx.beginPath(); ctx.moveTo(115,80); ctx.quadraticCurveTo(60,130,110,115); ctx.stroke(); ctx.closePath(); ctx.fill(); // lines on the leaves: ctx.strokeStyle = "#006600"; ctx.lineWidth = 0.3; ctx.beginPath(); ctx.moveTo(115, 80); ctx.lineTo(65, 71); ctx.stroke(); ctx.closePath(); ctx.beginPath(); ctx.moveTo(115, 80); ctx.lineTo(127, 55); ctx.stroke(); ctx.closePath(); ctx.beginPath(); ctx.moveTo(115, 80); ctx.lineTo(175, 85); ctx.stroke(); ctx.closePath(); ctx.beginPath(); ctx.moveTo(115, 80); ctx.lineTo(110, 110); ctx.stroke(); ctx.closePath();
Write one or more functions that do the things you are repeating. Figure out what parameters they need to take to be able to handle the slightly different cases. Then call the functions with the right parameters. For example, your code of the form ctx.beginPath(); ctx.moveTo(115, 80); ctx.lineTo(110, 110); ctx.stroke(); ctx.closePath(); would be written as the function function line(x1, y1, x2, y2) { ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.stroke(); ctx.closePath(); } and called as line(115, 80, 110, 110);
Expressive language Javascript is known for its expressive flexibility and can allow coding styles not found in most other languages. Use Path2D The first thought for your code was to use the Path2D object to define the draw commands. It uses similar syntax to SVG path command. But why not create your own command list. Named styles First let's find all the styles and name them var styles = { dGreen : { strokeStyle : "#006600", lineWidth : 0.3, }, black : { strokeStyle : "black", fillStyle : "#7BA32D", lineWidth : 0.8, } } Now you can use the named style and if you name the properties the same as used by the 2D API it is very easy to set a style function setStyle(style){ Object.keys(style).forEach(prop => ctx[prop] = style[prop]); }, setStyle(styles.black); // sets the style black If you want to use properties you did not think of at the time you dont have to code it just set the property and you are done styles.black.lineJoin = "round"; setStyle(styles.black); // sets the style black Custom command list. For the drawing commands you are doing the same set of operations many times. In SVG the commands are single characters "M" for moveto followed by x, y coordinates. We can do the same. The commands will be a string, separated by "," which is then split into an array. You shift from the array each command as needed. First the command object that has a function for each command. In this case M for moveTo and "L" for lineTo. It takes the array which it uses to get the coordinates from. var commands = { M(array){ ctx.moveTo(array.shift(),array.shift()); }, L(array){ ctx.lineTo(array.shift(),array.shift()); } } Then Define a path with our new commands move to 10,10 then line to 100,100 var path = "M,10,10,L,100,100"; Now we just need to parse and interpret the path function drawPath(path){ // split the command string into parts var commandList = path.split(","); // while there are commands while(commandList.length > 0){ // use the next command to index the command // and call the function it names passing the command list so // it can get the data it needs commands[commandList.shift()](commandList); } // do that until there is nothing on the command list } Now all you need to do is supply the command strings to draw what you need. Because you can define the commands you can create commands as complex or simple as you want. A little more complex The following is the command functions and draw function I created to draw your image // define draw commands var drawFuncs = { getN(a,count){ return a.splice(0,count); }, // gets values from array M(a){ ctx.moveTo(...this.getN(a,2)); }, // move to C(a){ ctx.bezierCurveTo(...this.getN(a,6)); }, // bezier curve Q(a){ ctx.quadraticCurveTo(...this.getN(a,4)); },// quad curve S(){ ctx.stroke(); }, // stroke P(){ ctx.closePath(); }, // close path F(){ ctx.fill(); }, // fill B(){ ctx.beginPath(); }, // begin path l(a) { // line segment ctx.beginPath(); ctx.moveTo(...this.getN(a,2)); ctx.lineTo(...this.getN(a,2)); ctx.stroke(); }, St(a){ // set style var style = styles[a.shift()]; Object.keys(style).forEach(prop=>ctx[prop] = style[prop]); }, } // Takes command string and draws what is in it function draw(shape){ var a = shape.split(","); while(a.length > 0){ drawFuncs[a.shift()](a); } } You can put that code into a separate library and forget about it, while you concentrate on the rendering Now you can render via your own custom made declarative language Define the styles // define named styles var styles = { dGreen : { strokeStyle : "#006600", lineWidth : 0.3, }, black : { strokeStyle : "black", fillStyle : "#7BA32D", lineWidth : 0.8, } } Create command list and draw draw([ "St,dGreen,B,M,115,80,C,20,100,200,100,235,135,S", "St,black,B,M,55,70,Q,20,100,115,80", "M,55,70,Q,40,30,115,80", "M,115,80,Q,80,20,130,50", "M,115,80,Q,200,40,130,50", "M,115,80,Q,235,60,185,85", "M,115,80,Q,190,115,185,85", "M,115,80,Q,180,135,110,115", "M,115,80,Q,60,130,110,115,S,P,F", "St,dGreen", "l,115,80,65,71", "l,115,80,127,55", "l,115,80,175,85", "l,115,80,110,110", ].join(",")); Demo Note: All the code is written in ES6 and will need Babel (or similar) to work on legacy browsers. // define draw commands var drawFuncs = { getN(a,count){ return a.splice(0,count); }, // gets values from array M(a){ ctx.moveTo(...this.getN(a,2)); }, // move to C(a){ ctx.bezierCurveTo(...this.getN(a,6)); }, // bezier curve Q(a){ ctx.quadraticCurveTo(...this.getN(a,4)); },// quad curve S(){ ctx.stroke(); }, // stroke P(){ ctx.closePath(); }, // close path F(){ ctx.fill(); }, // fill B(){ ctx.beginPath(); }, // begin path l(a) { // line segment ctx.beginPath(); ctx.moveTo(...this.getN(a,2)); ctx.lineTo(...this.getN(a,2)); ctx.stroke(); }, St(a){ // set style var style = styles[a.shift()]; Object.keys(style).forEach(prop=>ctx[prop] = style[prop]); }, } // Takes command string and draws what is in it function draw(shape){ var a = shape.split(","); while(a.length > 0){ drawFuncs[a.shift()](a); } } // create canvas and add to DOM var canvas = document.createElement("canvas"); canvas.width = 200; canvas.height = 200; var ctx = canvas.getContext("2d"); document.body.appendChild(canvas); // define named styles var styles = { dGreen : { strokeStyle : "#006600", lineWidth : 0.3, }, black : { strokeStyle : "black", fillStyle : "#7BA32D", lineWidth : 0.8, } } // draw it all ctx.clearRect(0,0,canvas.width,canvas.height); draw([ "St,dGreen,B,M,115,80,C,20,100,200,100,235,135,S", "St,black,B,M,55,70,Q,20,100,115,80", "M,55,70,Q,40,30,115,80", "M,115,80,Q,80,20,130,50", "M,115,80,Q,200,40,130,50", "M,115,80,Q,235,60,185,85", "M,115,80,Q,190,115,185,85", "M,115,80,Q,180,135,110,115", "M,115,80,Q,60,130,110,115,S,P,F", "St,dGreen", "l,115,80,65, 71", "l,115,80,127, 55", "l,115,80,175, 85", "l,115,80,110, 110", ].join(","));
Canvas - clipping multiple images
I want to clip a bunch of images into hexagon shapes. I have it sort of working, but the clipping is across all the hexes instead of each image clipping to only one hex. What am I doing wrong? Here's a live demo: http://codepen.io/tev/pen/iJaHB Here's the js in question: function polygon(ctx, x, y, radius, sides, startAngle, anticlockwise, img, imgX, imgY) { if (sides < 3) return; var a = (Math.PI * 2)/sides; a = anticlockwise?-a:a; ctx.save(); ctx.translate(x,y); ctx.rotate(startAngle); ctx.moveTo(radius,0); for (var i = 1; i < sides; i++) { ctx.lineTo(radius*Math.cos(a*i),radius*Math.sin(a*i)); } ctx.closePath(); // add stroke ctx.lineWidth = 5; ctx.strokeStyle = '#056e96'; ctx.stroke(); // add stroke ctx.lineWidth = 4; ctx.strokeStyle = '#47b6c8'; ctx.stroke(); // add stroke ctx.lineWidth = 2; ctx.strokeStyle = '#056e96'; ctx.stroke(); // Clip to the current path ctx.clip(); ctx.drawImage(img, imgX, imgY); ctx.restore(); } // Grab the Canvas and Drawing Context var canvas = document.getElementById('c'); var ctx = canvas.getContext('2d'); // Create an image element var img = document.createElement('IMG'); var img2 = document.createElement('IMG'); // When the image is loaded, draw it img.onload = function () { polygon(ctx, 120,120,100,6, 0,0,img, -120,-170); } img2.onload = function () { polygon(ctx, 280,212,100,6, 0,0,img2, -150,-120); } // Specify the src to load the image img.src = "http://farm8.staticflickr.com/7381/9601443923_051d985646_n.jpg"; img2.src = "http://farm6.staticflickr.com/5496/9585303170_d005d2aaa9_n.jpg";
You need to add this to your polygon() method: ctx.beginPath(); See modified pen here function polygon(ctx, x, y, radius, sides, startAngle, anticlockwise, img, ... if (sides < 3) return; var a = (Math.PI * 2)/sides; a = anticlockwise?-a:a; ctx.save(); ctx.translate(x,y); ctx.rotate(startAngle); ctx.beginPath(); /// for example here, before moveTo/lineTo ctx.moveTo(radius,0); ... If not the lines will accumulate so the second time you call polygon the previous polygon will still exist. That's why you see the image partly inside the first hexagon as well.
Is there a way to streamline canvas code
This is a bit of a strange issue but I have a world map that is produced from lines of canvas code. The canvas code is derived from an SVG file that was automatically converted by a website but it produced 47000 lines of code & a 1.5mb file size. This obviously takes some time to load and occasionally it doesn't appear (it currently resides in a .js file that is remote loaded). Is there a way to streamline this code to reduce the file size. I have thought about transferring all the line coordinates into a sql table and producing it that way but I'm not sure if that would be any better. Example code: function world(scale) { var canvas = document.getElementById("myCanvas"); var ctx = canvas.getContext("2d"); ctx.scale(scale,scale); ctx.save(); ctx.beginPath(); ctx.moveTo(0,0); ctx.lineTo(39960,0); ctx.lineTo(39960,19980); ctx.lineTo(0,19980); ctx.closePath(); ctx.clip(); ctx.strokeStyle = "#ffffff"; ctx.lineCap = "butt"; ctx.lineJoin = "miter"; ctx.miterLimit = 4; ctx.save(); ctx.restore(); ctx.save(); ctx.restore(); ctx.save(); ctx.save(); ctx.fillStyle = "#bcbcbc"; ctx.strokeStyle = "#ffffff"; ctx.lineWidth = 5.5; ctx.lineCap = "round"; ctx.lineJoin = "round"; ctx.miterLimit = 4; ctx.beginPath(); ctx.moveTo(14553,0); ctx.lineTo(14528,2); ctx.lineTo(14502,2); ctx.lineTo(14476,2); ctx.lineTo(14448,4); ctx.lineTo(14464,17); ctx.lineTo(14436,15); ctx.lineTo(14413,11); ctx.lineTo(14395,4);
function CanvasProxy(target){ var Host=new Object; for (var v in target) switch (typeof target[v]) { case "function":Host[v]=addMethod.bind(v);break; case "number":setProperty(v);break; case "string":setProperty(v);break; case "object":Host[v]=target[v];break; default:setSimpleProperty(v) }; return Host; function addMethod(){ var ret=target[this].apply(target,arguments); if (typeof ret==="undefined") return Host; Host[this]=target[this]; return ret; } function setProperty(p){ function property(v){target[p]=v;return Host;} property.toString=property.valueOf=function(){return target[p]} Object.defineProperty(Host, p , {get : function(){return property}, set : function(v){ target[p] = v}}); } function setSimpleProperty(p){ Object.defineProperty(Host, p , {get : function(){return target[p]}, set : function(v){ target[p] = v}}); } } var ctx; function _init(){ var canvas = document.getElementById("myCanvas"); ctx = CanvasProxy(canvas.getContext("2d")); } function world(scale) { ctx.scale(scale,scale) .save() .beginPath() .moveTo(0,0) .lineTo(39960,0) .lineTo(39960,19980) .lineTo(0,19980) .closePath() .clip() .strokeStyle("#ffffff") .lineCap("butt") .lineJoin("miter") .miterLimit(4) .save().restore().save().restore().save().save().fillStyle("#bcbcbc").....