Graphic selection-graphic modularization

Posted May 26, 20205 min read

Before talking about the modularization of graphics, let ’s review the graphics we drew before. It is a polygon. Although it is not closed, it is not important.

image.png

Next, we will encapsulate this graphic as a class object Poly
Poly objects are the encapsulation of the path, we can consider from two aspects:

  • Graphics:all graphics that can be drawn by the path, it can be one graphic, or multiple graphics, as long as they are all in a path collection;
  • Style:all styles that the path should have.

Next we look at the default properties of Poly objects:

const defAttr = {
    crtPath:crtLinePath,
    vertices:[],

    close:false,
    fill:false,
    stroke:false,
    shadow:false,

    fillStyle:'# 000',
    strokeStyle:'# 000',
    lineWidth:1,
    lineDash:[],
    lineDashOffset:0,
    lineCap:'butt',
    lineJoin:'miter',
    miterLimit:10,
    shadowColor:'rgba(0,0,0,0)',
    shadowBlur:0,
    shadowOffsetX:0,
    shadowOffsetY:0,

    position:new Vector2(0,0),
    rotation:0,
    scale:new Vector2(1,1),
};

Explain these properties in detail:
crtPath is a method for establishing a path. The default is to give a method for drawing polygons. This method can also be overridden.

/* Draw polygon * /
function crtLinePath(ctx) {
const {vertices} = this;
/* Connect dots to form a line * /
ctx.beginPath();
ctx.moveTo(vertices [0].x, vertices [0].y);
const len ​​= vertices.length;
for(let i = 1; i <len; i ++) {
ctx.lineTo(vertices [i].x, vertices [i].y);
}
}

vertices is a collection of vertices of a polygon.
I haven't written about the relevant attributes of other graphics, and I can expand it later if needed, such as the center position, radius, starting radian, ending radian, etc.

Related attributes related to drawing methods:

  • close:whether to close the path
  • fill:whether to draw graphics by filling
  • strtoke:whether to draw graphics by stroke
  • shadow:whether to add projection to the image

Style-related attributes:fillStyle fill style, strokeStyle stroke style ... These style names are the same as the styles in canvas, I will not say more.

Transform related properties:the same as the transform in canvas, namely displacement, rotation, and scaling.

Poly's method:

  • draw(ctx):drawing method
  • checkPointInPath(ctx, {x, y}):check whether the point is in the path

Overall code:

import Vector2 from "./Vector2.js";

/* Polygon default properties * /
const defAttr = {
crtPath:crtLinePath,
vertices:[],
close:false,
fill:false,
stroke:false,
shadow:false,
fillStyle:'# 000',
strokeStyle:'# 000',
lineWidth:1,
lineDash:[],
lineDashOffset:0,
lineCap:'butt',
lineJoin:'miter',
miterLimit:10,
shadowColor:'rgba(0,0,0,0)',
shadowBlur:0,
shadowOffsetX:0,
shadowOffsetY:0,
scale:new Vector2(1,1),
position:new Vector2(0,0),
rotation:0,
};
/* Draw polygon * /
function crtLinePath(ctx) {
const {vertices} = this;
/* Connect dots to form a line * /
ctx.beginPath();
ctx.moveTo(vertices [0].x, vertices [0].y);
const len ​​= vertices.length;
for(let i = 1; i <len; i ++) {
ctx.lineTo(vertices [i].x, vertices [i].y);
}
}

/* Poly * /
export default class Poly {
constructor(param = {}) {
Object.assign(this, defAttr, param);
}
draw(ctx) {
const {
shadow, shadowColor, shadowBlur, shadowOffsetX, shadowOffsetY,
stroke, close, strokeStyle, lineWidth, lineCap, lineJoin, miterLimit, lineDash, lineDashOffset,
fill, fillStyle,
scale, position, rotation
} = this;
ctx.save();
/projection*/
if(shadow) {
ctx.shadowColor = shadowColor;
ctx.shadowBlur = shadowBlur;
ctx.shadowOffsetX = shadowOffsetX;
ctx.shadowOffsetY = shadowOffsetY;
}
/
Transform * /
ctx.translate(position.x, position.y);
ctx.rotate(rotation);
ctx.scale(scale.x, scale.y);
/* Create path * /
this.crtPath(ctx);
/* Stroke * /
if(stroke) {
ctx.strokeStyle = strokeStyle;
ctx.lineWidth = lineWidth;
ctx.lineCap = lineCap;
ctx.lineJoin = lineJoin;
ctx.miterLimit = miterLimit;
ctx.lineDashOffset = lineDashOffset;
ctx.setLineDash(lineDash);
close && ctx.closePath();
ctx.stroke();
}
/filling/
if(fill) {
ctx.fillStyle = fillStyle;
ctx.fill();
}
ctx.restore();
}
checkPointInPath(ctx, {x, y}) {
this.crtPath(ctx);
const bool = ctx.isPointInPath(x, y);
}
}

Note:In the Poly object, I used a Vector2 object for the definition of its points.
Vector2 is a two-dimensional vector object, which stores basic x and y position information, and encapsulates the arithmetic methods of points, such as addition, subtraction, multiplication, and division.
Here I will not explain the Vector2 object in detail. We first know that it represents a {x, y} point, which function we use later, and then explain which function.

After the Poly object is created, let's try to draw a triangle.
Instantiate the Poly object:

const poly = new Poly({
    stroke:true,
    close:true,
    vertices:[
        new Vector2(50,50),
        new Vector2(450,50),
        new Vector2(250,250),
   ]
});
poly.draw(ctx);

effect:

image.png

Add in and out effects for triangles:

let hover = false;
canvas.addEventListener('mousemove', mousemoveFn);
function mousemoveFn(event) {
    const mousePos = getMousePos(event);
    poly.crtPath(ctx);
    const bool = ctx.isPointInPath(mousePos.x, mousePos.y);
    if(hover! == bool) {
        poly.fill = bool;
        ctx.clearRect(0,0, canvas.width, canvas.height);
        poly.draw(ctx);
        hover = bool;
    }
}

Mouse selection logic:

  1. Declare the hover variable outside the event and store the mouse in and out state.
  2. Use canvas to monitor mouse movement events to get the mouse position in canvas
  3. Use the poly.crtPath(ctx) method to establish the path
  4. Use isPointInPath() to determine whether the mouse point is in the path
  5. The selection state of the mouse has changed, so that the fill style of the graph is also changed accordingly, and drawing.

Mouse in effect:

image.png

Draw svg polygon into canvas

With Poly objects, we can also draw and select based on the polygon data in svg.
Next, I used Poly objects to draw the mountain resembling an elephant.
After the svg is loaded successfully, extract the vertices of the polygon in the svg, and then put it into the vertices collection of the Poly instance object.

window.onload = function() {
    const dom = embed.getSVGDocument();
    const mount = dom.querySelector('# mount');
    backImg = dom.querySelector('# back');
    ctx.drawImage(backImg, 0,0);
    poly.vertices = parsePoints(mount);
    poly.draw(ctx);
   /* Mouse movement * /
    canvas.addEventListener('mousemove', mousemoveFn);
};

parsePoints(mount) parses the points attribute of the polygon tag below

<polygon id = "mount" fill-rule = "evenodd" clip-rule = "evenodd" fill = "none" stroke = "# 080102" stroke-miterlimit = "10" points = "
   211.7, 260.8 234.6, 236.6 241.2, 190.3 245.6, 165.2 255.7, 145.4 309.5, 95.2 358.4, 74.9 381.7, 115.9 388.8, 130.4 385.7, 137.9
   398,174.5 406.4,176.2 433.3,205.3 443.8,236.6 468.9,263 288.8,264.8 294.5,239.2 276,243.6 265.9,262.6 "/>

parsePoints(mount) function:

function parsePoints(dom) {
    const points = [];
    let pointsAttr = dom.getAttribute('points'). split('');
    for(let ele of pointsAttr) {
        if(ele) {
            const arr = ele.split(',');
            const [x, y]= [
                Math.round(arr [0]),
                Math.round(arr [1]),
           ];
            points.push(new Vector2(x, y));
        }
    }
    return points;
}

Page effect:

image.png

Draw other graphics

Rewriting the crtPath() method of the Poly object, we can also draw other graphics than polygons, such as drawing a love with two cubic beziers:

image.png

const poly = new Poly({
    position:new Vector2(300,400),
    stroke:true,
    close:true,
    crtPath:function(ctx) {
        ctx.beginPath();
        ctx.moveTo(0,0);
        ctx.bezierCurveTo(-200, -50, -180, -300,0, -200);
        ctx.bezierCurveTo(180, -300,200, -50,0,0);
    }
});
poly.draw(ctx);

In fact, there is still a problem, that is, after the graphic has been displaced, rotated or scaled through the transformation attribute, the point of the mouse relative to the canvas can no longer make the correct selection of the graphic.
For the reason and solution of this problem, we will explain in detail in the next chapter:[Graphic Selection-Substance is not easy]

Source Address