-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathpaintworklet.js
135 lines (126 loc) · 5.84 KB
/
paintworklet.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
registerPaint('tooltip', class {
static get inputProperties() {
return [
'--round-radius', // optional, number, default to 5
'--background-color', // color string, default '#ddd'
'--triangle-size', // number, default 20. The long edge length.
'--position', // string | number, default 'center' (50), the percentage of the center point on the edge
'--direction', // top|bottom|left|right, default 'left'
'--border-color', // color string, default no border
'--border-width', // number, default 0
];
}
paint(ctx, geom, properties) {
// default values
const DEFAULT_RADIUS = 5;
const DEFAULT_BACKGROUND = '#fff';
const DEFAULT_TRIANGLE_SIZE = 16;
const DEFAULT_DIRECTION = 'left';
const DEFAULT_POSITION = 50;
const DEFAULT_BORDER_WIDTH = 0;
const DEFAULT_BORDER_COLOR = '#000';
// get shape vars
const radius = isNaN(parseFloat(properties.get('--round-radius').toString())) ? DEFAULT_RADIUS : parseFloat(properties.get('--round-radius').toString());
const bgColor = properties.get('--background-color').toString() || DEFAULT_BACKGROUND;
// get border vars
const borderColor = properties.get('--border-color').toString() || DEFAULT_BORDER_COLOR;
const borderWidth = parseFloat(properties.get('--border-width').toString() || DEFAULT_BORDER_WIDTH);
// get position vars
const direction = properties.get('--direction').toString().trim() || DEFAULT_DIRECTION;
const triangleSize = parseFloat(properties.get('--triangle-size').toString()) || DEFAULT_TRIANGLE_SIZE;
const positionValue = properties.get('--position').toString().trim() || DEFAULT_POSITION;
// calculate positions
const trianglePosition = (positionValue === 'center' ? 50 : parseFloat(positionValue)) / 100 *
(direction === 'top' || direction === 'bottom' ? geom.width: geom.height);
const triangle = [];
const rect = {};
switch(direction) {
case 'top':
rect.width = geom.width - 2 * borderWidth;
rect.height = geom.height - triangleSize * .6 - 2 * borderWidth;
rect.x = borderWidth;
rect.y = triangleSize * .6 + borderWidth;
triangle[0] = {x: trianglePosition - triangleSize / 2, y: triangleSize * .6 + borderWidth};
triangle[1] = {x: trianglePosition, y: 0};
triangle[2] = {x: trianglePosition + triangleSize / 2, y: triangleSize * .6 + borderWidth};
break;
case 'bottom':
rect.width = geom.width - 2 * borderWidth;
rect.height = geom.height - triangleSize * .6 - 2 * borderWidth;
rect.x = borderWidth;
rect.y = borderWidth;
triangle[0] = {x: trianglePosition - triangleSize / 2, y: geom.height - triangleSize * .6 - borderWidth};
triangle[1] = {x: trianglePosition, y: geom.height};
triangle[2] = {x: trianglePosition + triangleSize / 2, y: geom.height - triangleSize * .6 - borderWidth};
break;
case 'right':
rect.width = geom.width - triangleSize * .6 - 2 * borderWidth;
rect.height = geom.height - 2 * borderWidth;
rect.x = borderWidth;
rect.y = borderWidth;
triangle[0] = {x: geom.width - triangleSize * .6 - borderWidth, y: trianglePosition - triangleSize / 2};
triangle[1] = {x: geom.width, y: trianglePosition};
triangle[2] = {x: geom.width - triangleSize * .6 - borderWidth, y: trianglePosition + triangleSize / 2};
break;
default:
rect.width = geom.width - triangleSize * .6 - 2 * borderWidth;
rect.height = geom.height - 2 * borderWidth;
rect.x = triangleSize * .6 + borderWidth;
rect.y = borderWidth;
triangle[0] = {x: triangleSize * .6 + borderWidth, y: trianglePosition - triangleSize / 2};
triangle[1] = {x: 0, y: trianglePosition};
triangle[2] = {x: triangleSize * .6 + borderWidth, y: trianglePosition + triangleSize / 2};
}
// setup canvas context
ctx.fillStyle = bgColor;
ctx.strokeStyle = borderColor;
ctx.lineWidth = borderWidth;
// draw
this.drawRoundRect(ctx, rect, radius);
ctx.fill();
if (borderWidth) {
ctx.stroke();
this.coverOverlappingBorder(ctx, triangle, borderWidth, bgColor, borderColor);
}
ctx.lineCap = 'round';
this.drawTriangle(ctx, triangle);
ctx.fill();
if (borderWidth) {ctx.stroke();}
}
// rounded corner rect inspired by http://js-bits.blogspot.com/2010/07/canvas-rounded-corner-rectangles.html
drawRoundRect(ctx, rect, radius) {
if (typeof radius === "undefined") {
radius = 5;
}
ctx.beginPath();
ctx.moveTo(rect.x + radius, rect.y);
ctx.lineTo(rect.x + rect.width - radius, rect.y);
ctx.quadraticCurveTo(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + radius);
ctx.lineTo(rect.x + rect.width, rect.y + rect.height - radius);
ctx.quadraticCurveTo(rect.x + rect.width, rect.y + rect.height, rect.x + rect.width - radius, rect.y + rect.height);
ctx.lineTo(rect.x + radius, rect.y + rect.height);
ctx.quadraticCurveTo(rect.x, rect.y + rect.height, rect.x, rect.y + rect.height - radius);
ctx.lineTo(rect.x, rect.y + radius);
ctx.quadraticCurveTo(rect.x, rect.y, rect.x + radius, rect.y);
ctx.closePath();
}
drawTriangle(ctx, triangle) {
ctx.beginPath();
ctx.moveTo(triangle[0].x, triangle[0].y);
ctx.lineTo(triangle[1].x, triangle[1].y);
ctx.lineTo(triangle[2].x, triangle[2].y);
}
coverOverlappingBorder(ctx, triangle, borderWidth, bgColor, borderColor) {
// Set it to covering color
ctx.strokeStyle = bgColor;
// Set it slightly wider than the border to totally cover it.
ctx.lineWidth = borderWidth + 1;
ctx.beginPath();
ctx.moveTo(triangle[0].x, triangle[0].y);
ctx.lineTo(triangle[2].x, triangle[2].y);
ctx.stroke();
// Reset it back
ctx.lineWidth = borderWidth;
ctx.strokeStyle = borderColor;
}
});