This chapter shares how to use Konva to draw basic shapes: rectangles, straight lines, and polylines, and I hope you will continue to follow and support it haha!
Please give me a free Star!
If you find any bugs, please feel free to raise an Issue!
github source code
gitee source code
sample address (computing)
rectangles
First on the effect!
The realization is basically the same asFront-end Visual Designer with Konva (21) - Drawing Graphs (Ellipses)is consistent, the main difference between the size of the rectangle and the size of the ellipse is not the same way to set the size of the rectangle, in particular, do not need to set the offset. other than not to go into detail ha.
Straight lines, polylines
First on the effect!
Briefly describe the above interaction:
First, draw a straight line, fade out to draw a straight line or relatively simple, according to the record of the position of the mouse down and mouse release position, it is easy to get the points should be set to the value.
Then, along the lines of an ellipse or rectangle, there are only 2 specific "adjustment points" which represent the start and end points.
// src/Render/graphs/
// omitted
/**
* Straight lines, polylines
*/
export class Line extends BaseGraph {
// Omit
constructor(render: , dropPoint: Konva.Vector2d) {
super(render, dropPoint, {
type: ,
// Defines 2 adjustment points
anchors: [{ adjustType: 'start' }, { adjustType: 'end' }].map((o) => ({
adjustType: // Adjustment Point Type Definition
})), { adjustType: 'start' }
linkAnchors: [
{ x: 0, y: 0, alias: 'start' }, { x: 0, y: 0, alias: 'end' }
{ x: 0, y: 0, alias: 'end' }
] as []
})
// Create new straight and polyline
= new ({
name: 'graph',
x: 0,
y: 0,
stroke: 'black',
strokeWidth: 1,
hitStrokeWidth: (5)
})
// Give 1 pixel to prevent the exported image toDataURL from failing
({
width: 1,
height: 1
})
// Add
()
// Mouse down position as starting point
()
}
// Implementation: Drag-and-drop
override drawMove(point: Konva.Vector2d): void {
// Mouse drag offset
const offsetX = - ,
offsetY = -
// Start and end points
const linkPoints = [
[(), ()], [() + offsetX, () + offsetY]
[() + offsetX, () + offsetY]
]
// Straight, polyline paths
(_.flatten(linkPoints))
// Update the anchor position of the graphic's flatten points.
(, , )
// Update the anchor position of the graph's link points.
(, , )
// Redraw
([, , ])
}
// Implementation: drag end
override drawEnd(): void {
if (() <= 1 && () <= 1) {
// Add click only, no drag
// Default size
const width = ,
height = width
// Start and end points
const linkPoints = [
[(), ()], [() + width, () + height]
[() + width, () + height]
]
// Straight lines, polylines, position sizes
(_.flatten(linkPoints))
}
// Update Adjustment points (inflection points)
(, )
// Update the anchor position of the graphic's adjustment point.
(, , )
// Update the anchor position of the graph's join point.
(, , )
// Alignment line clear
()
// Update history
()
// Redraw
([, , ])
}
// Omit
}
Adjustment points allow you to change the start and end points of a straight line or polyline.
// summarize
/**
* straightness、broken line (continuous figure made up of straight line segments)
*/
export class Line extends BaseGraph {
// realization:update depiction (used form a nominal expression) adjustment point (used form a nominal expression) anchor location
static override updateAnchorShadows(
graph: ,
anchorShadows: [],
shape?:
): void {
if (shape) {
const points = ()
//
for (const shadow of anchorShadows) {
switch () {
case 'start':
({
x: points[0],
y: points[1]
})
break
case 'end':
({
x: points[ - 2],
y: points[ - 1]
})
break
}
}
}
}
// summarize
// realization:generating adjustment point
static override createAnchorShapes(
render: ,
graph: ,
anchorAndShadows: {
anchor:
anchorShadow:
shape?:
}[],
adjustAnchor?:
): {
anchorAndShadows: {
anchor:
anchorShadow:
shape?: | undefined
}[]
} {
// stage state of affairs
const stageState = ()
const graphShape = ('.graph') as
if (graphShape) {
const points = ()
for (const anchorAndShadow of anchorAndShadows) {
let rotate = 0
const { anchor, anchorShadow } = anchorAndShadow
const x = (().x - ),
y = (().y - )
if ( === 'manual') {
// summarize
} else {
if ( === 'start') {
rotate = (points[2] - points[0], points[3] - points[1])
} else if ( === 'end') {
rotate = (
points[ - 2] - points[ - 4],
points[ - 1] - points[ - 3]
)
}
const cos = ((rotate * ) / 180)
const sin = ((rotate * ) / 180)
const offset = ( + 5)
const offsetX = offset * sin
const offsetY = offset * cos
const anchorShape = new ({
name: 'anchor',
anchor: anchor,
//
fill:
adjustAnchor?.adjustType === && adjustAnchor?.groupId === ()
? 'rgba(0,0,255,0.8)'
: 'rgba(0,0,255,0.2)',
radius: (3),
strokeWidth: 0,
// placement
x: x,
y: y,
offsetX:
=== 'start' ? offsetX : === 'end' ? -offsetX : 0,
offsetY:
=== 'start' ? offsetY : === 'end' ? -offsetY : 0,
// angle of rotation
rotation: ()
})
('mouseenter', () => {
('rgba(0,0,255,0.8)')
= 'move'
})
('mouseleave', () => {
(
? 'rgba(0,0,255,0.8)' : 'rgba(0,0,255,0.2)'
)
= ? 'move' : 'default'
})
= anchorShape
}
}
}
return { anchorAndShadows }
}
// summarize
// realization:align depiction
static override adjust(
render: ,
graph: ,
graphSnap: ,
adjustShape: ,
anchorAndShadows: {
anchor:
anchorShadow:
shape?: | undefined
}[],
startPoint: Konva.Vector2d,
endPoint: Konva.Vector2d
) {
// goal straightness、broken line (continuous figure made up of straight line segments)
const line = ('.graph') as
// mirroring
const lineSnap = ('.graph') as
// adjustment point anchor point
const anchors = (('.anchor') ?? []) as []
// mirroring
const anchorsSnap = (('.anchor') ?? []) as []
// connection point anchor point
const linkAnchors = (('.link-anchor') ?? []) as []
if (line && lineSnap) {
// stage state of affairs
const stageState = ()
{
const [graphRotation, adjustType, ex, ey] = [
(()),
?.adjustType,
,
]
const { x: cx, y: cy, width: cw, height: ch } = ()
const { x, y } = ()
const [centerX, centerY] = [cx + cw / 2, cy + ch / 2]
const { x: sx, y: sy } = (ex, ey, centerX, centerY, -graphRotation)
const { x: rx, y: ry } = (x, y, centerX, centerY, -graphRotation)
const points = ()
const manualPoints = ( ?? []) as []
if (adjustType === 'manual') {
// summarize
} else {
const anchor = ((o) => === adjustType)
const anchorShadow = ((o) => === adjustType)
if (anchor && anchorShadow) {
{
const linkPoints = [
[points[0], points[1]],
...((a, b) => - ).map((o) => [, ]),
[points[ - 2], points[ - 1]]
]
switch (adjustType) {
case 'start':
{
linkPoints[0] = [sx - rx, sy - ry]
(_.flatten(linkPoints))
}
break
case 'end':
{
linkPoints[ - 1] = [sx - rx, sy - ry]
(_.flatten(linkPoints))
}
break
}
}
}
}
}
// update adjustment point(inflexion point (math., a point of a curve at which the curvature changes sign))
(render, graph)
// update adjustment point (used form a nominal expression) anchor point placement
(graph, anchors, line)
// update depiction (used form a nominal expression) connection point (used form a nominal expression) anchor location
(graph, linkAnchors, line)
// update adjustment point placement
for (const anchor of anchors) {
for (const { shape } of anchorAndShadows) {
if (shape) {
if (?.adjustType === ) {
const anchorShadow = graph
.find(`.anchor`)
.find((o) => === )
if (anchorShadow) {
({
x: (().x - ),
y: (().y - )
})
(())
}
}
}
}
}
// repaint
([, , ])
}
}
// summarize
}
broken line (continuous figure made up of straight line segments)
Unlike drawing ellipses and rectangles, where the "adjustment points" are fixed, for bisectors, 2 new adjustment points are added for each new point of inflection, and the overall interaction is similar to that of a manually connected line.
// src/Render/draws/
// summarize
export interface GraphDrawState {
// summarize
/**
* in the pipeline adjustment point
*/
adjustAnchor?:
/**
* Mouse over adjustment point placement
*/
startPointCurrent: Konva.Vector2d
/**
* depiction group
*/
graphCurrent?:
/**
* depiction group mirroring,用于计算placement、Offset in size
*/
graphCurrentSnap?:
}
// summarize
export class GraphDraw extends implements {
// summarize
state: GraphDrawState = {
adjusting: false,
adjustGroupId: '',
startPointCurrent: { x: 0, y: 0 }
}
// summarize
override draw() {
()
// 所有depiction
const graphs =
.find('.asset')
.filter((o) => === ) as []
for (const graph of graphs) {
// Displayed only when not selected adjustment point
if (!) {
// summarize
for (const anchorAndShadow of anchorAndShadows) {
const { shape } = anchorAndShadow
if (shape) {
// Mouse over
('mousedown', () => {
const pos = ()
if (pos) {
= true
=
= ()
= pos
= graph
= ()
('adjusting', true)
if () {
switch (?.type) {
case :
// utilization straightness、broken line (continuous figure made up of straight line segments) Static processing methods
(, graph, , pos)
break
}
}
}
})
// summarize
// End of adjustment
('mouseup', () => {
// summarize
= false
= undefined
= ''
// Restore the display of all adjustment point
for (const { shape } of anchorAndShadows) {
if (shape) {
(1)
('adjusting', false)
if (?.type === ) {
if () {
('rgba(0,0,0,0.4)')
} else {
('rgba(0,0,255,0.2)')
}
} else {
('rgba(0,0,255,0.2)')
}
}
// summarize
}
// summarize
})
// summarize
}
}
}
}
}
}
In addition to the need for more state logging adjustment information, the Line-specific adjustStart method needs to be defined:
// src/Render/graphs/
// summarize
/**
* straightness、broken line (continuous figure made up of straight line segments)
*/
export class Line extends BaseGraph {
// summarize
/**
* Before adjustment
*/
static adjustStart(
render: ,
graph: ,
adjustAnchor: & { manualIndex?: number; adjusted?: boolean },
endPoint: Konva.Vector2d
) {
const { x: gx, y: gy } = ()
const shape = ('.graph') as
if (shape && typeof === 'number') {
const manualPoints = ( ?? []) as []
if () {
//
} else {
({
x: - gx,
y: - gy,
index:
})
('manualPoints', manualPoints)
}
// update adjustment point(inflexion point (math., a point of a curve at which the curvature changes sign))
(render, graph)
}
}
}
// summarize
Dynamic adjustment points are recorded in the line's attrs manualPoints, each time a point of inflection is adjusted for the first time, a new point of inflection is added, which is mainly used:
// summarize
/**
* straightness、broken line (continuous figure made up of straight line segments)
*/
export class Line extends BaseGraph {
// summarize
// realization:align depiction
static override adjust(
render: ,
graph: ,
graphSnap: ,
adjustShape: ,
anchorAndShadows: {
anchor:
anchorShadow:
shape?: | undefined
}[],
startPoint: Konva.Vector2d,
endPoint: Konva.Vector2d
) {
// goal straightness、broken line (continuous figure made up of straight line segments)
const line = ('.graph') as
// mirroring
const lineSnap = ('.graph') as
// align点 anchor point
const anchors = (('.anchor') ?? []) as []
// mirroring
const anchorsSnap = (('.anchor') ?? []) as []
// connection point anchor point
const linkAnchors = (('.link-anchor') ?? []) as []
if (line && lineSnap) {
// stage state of affairs
const stageState = ()
{
const [graphRotation, adjustType, ex, ey] = [
(()),
?.adjustType,
,
]
const { x: cx, y: cy, width: cw, height: ch } = ()
const { x, y } = ()
const [centerX, centerY] = [cx + cw / 2, cy + ch / 2]
const { x: sx, y: sy } = (ex, ey, centerX, centerY, -graphRotation)
const { x: rx, y: ry } = (x, y, centerX, centerY, -graphRotation)
const points = ()
const manualPoints = ( ?? []) as []
if (adjustType === 'manual') {
if (?.manualIndex !== void 0) {
const index = ?.adjusted
? ?.manualIndex
: ?.manualIndex + 1
const manualPointIndex = ((o) => === index)
if (manualPointIndex > -1) {
manualPoints[manualPointIndex].x = sx - rx
manualPoints[manualPointIndex].y = sy - ry
}
const linkPoints = [
[points[0], points[1]],
...((a, b) => - ).map((o) => [, ]),
[points[ - 2], points[ - 1]]
]
('manualPoints', manualPoints)
(_.flatten(linkPoints))
//
const adjustAnchorShadow = (
(o) => === 'manual' && === index
)
if (adjustAnchorShadow) {
({
x: sx - rx,
y: sy - ry
})
}
}
} else {
// summarize
}
}
// summarize
}
}
// summarize
/**
* update align点(inflexion point (math., a point of a curve at which the curvature changes sign))
* @param render
* @param graph
*/
static updateAnchor(render: , graph: ) {
const anchors = ?? []
const anchorShadows = ('.anchor') ?? []
const shape = ('.graph') as
if (shape) {
// abducted
let manualPoints = ( ?? []) as []
const points = ()
// align点 + inflexion point (math., a point of a curve at which the curvature changes sign)
const linkPoints = [
[points[0], points[1]],
...((a, b) => - ).map((o) => [, ]),
[points[ - 2], points[ - 1]]
]
// empty align点(inflexion point (math., a point of a curve at which the curvature changes sign)),reservations start end
(2)
const shadows = (2)
for (const shadow of shadows) {
()
()
}
manualPoints = []
for (let i = - 1; i > 0; i--) {
(i, 0, [])
}
// align点(inflexion point (math., a point of a curve at which the curvature changes sign))
for (let i = 1; i < - 1; i++) {
const anchor = {
type: ,
adjustType: 'manual',
//
name: 'anchor',
groupId: (),
//
manualIndex: i,
adjusted: false
}
if (linkPoints[i].length === 0) {
= false
// additional
const prev = linkPoints[i - 1]
const next = linkPoints[i + 1]
const circle = new ({
adjustType: ,
anchorType: ,
name: ,
manualIndex: ,
radius: 0,
// radius: (2),
// fill: 'red',
//
x: (prev[0] + next[0]) / 2,
y: (prev[1] + next[1]) / 2,
anchor
})
(circle)
} else {
= true
// abducted
const circle = new ({
adjustType: ,
anchorType: ,
name: ,
manualIndex: ,
adjusted: true,
radius: 0,
// radius: (2),
// fill: 'red',
//
x: linkPoints[i][0],
y: linkPoints[i][1],
anchor
})
(circle)
({
x: linkPoints[i][0],
y: linkPoints[i][1],
index:
})
}
(anchor)
}
('manualPoints', manualPoints)
('anchors', anchors)
}
}
// summarize
}
The above is simply the algorithm that handles manualPoints, which controls the addition of new points of inflection, then inserts the "points" between the start and end points, and finally handles the value of the points.
Incidentally. The distinction between start, end, and inflection points is made by the adjustType field in attrs; the distinction between whether or not an inflection point has already been manipulated is made by the adjusted field in attrs; and there is a definite order to the inflection points, which is recorded in the manualIndex field in attrs.
Personally, I think, at present, the code structure and variable naming of drawing graphics are easy to produce ambiguity, try to take the time to refactor it later, everyone support support 👇!
Thanks watching~
More Stars please!
source code (computing)
gitee source code
sample address (computing)