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").....

Categories

Resources