@usman404/crowjs 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +1 -0
- package/Core/Component.js +426 -0
- package/Core/GUIEvent/GUIEvent.js +23 -0
- package/Core/GUIEvent/KeyboardEvent.js +14 -0
- package/Core/GUIEvent/MouseEvent.js +22 -0
- package/Core/Root.js +558 -0
- package/Frames/DummyFrame.js +185 -0
- package/Frames/Frame.js +531 -0
- package/Frames/FrameComponent.js +54 -0
- package/Frames/GridFrame.js +574 -0
- package/Frames/ScrollFrame.js +764 -0
- package/LICENSE +21 -0
- package/README.md +130 -0
- package/UIComponents/Input.js +78 -0
- package/UIComponents/Label.js +234 -0
- package/UIComponents/TextField.js +551 -0
- package/UIComponents/UIComponent.js +97 -0
- package/crowjs-01-01.png +0 -0
- package/index.html +15 -0
- package/package.json +23 -0
- package/sketch.js +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Usman Ali
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# CrowJS 🐦⬛
|
|
2
|
+
**CrowJS** is a lightweight and extensible GUI library built on top of [p5.js](https://p5js.org/), designed for creative coders and simulation builders who need intuitive, canvas-native UI components like buttons, sliders, input fields, and more — all rendered directly inside the p5.js canvas.
|
|
3
|
+
|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
## ✨ Features
|
|
7
|
+
- Minimal and canvas-native UI for p5.js sketches
|
|
8
|
+
- Frames, nested layouts, scrollable & grid containers
|
|
9
|
+
- Customizable UI components: buttons, sliders, toggles, input fields, etc.
|
|
10
|
+
- Event system inspired by the web (click, hover, focus, blur)
|
|
11
|
+
- Modular design with support for custom components
|
|
12
|
+
- Fully drawn inside p5.js (no HTML overlays or DOM interference)
|
|
13
|
+
- Ideal for creative coding, simulations, visual experiments, and tools
|
|
14
|
+
|
|
15
|
+
## 🚀 Getting Started
|
|
16
|
+
|
|
17
|
+
### 1. Install
|
|
18
|
+
|
|
19
|
+
To use **CrowJS**, begin by downloading this repository and placing the `CrowJS` folder inside your project's source code.
|
|
20
|
+
Once included, open the `index.html` file in your browser; it already contains the required setup for p5.js and for loading CrowJS modules.
|
|
21
|
+
|
|
22
|
+
All GUI development should be done inside **`sketch.js`**, which serves as the main entry point for both your p5.js sketch and all CrowJS components.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
### ⚠ Important Notes
|
|
27
|
+
- **Do not rename** the `sketch.js` file. CrowJS depends on this filename for consistent module loading.
|
|
28
|
+
- **Do not alter** the structure or script tags inside `index.html`. Modifying them may prevent CrowJS from running correctly.
|
|
29
|
+
- A **CDN-based installation** method will be added in the future to make setup even easier.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
### 2. Basic Example
|
|
34
|
+
```javascript
|
|
35
|
+
import { Root } from "./Core/Root.js";
|
|
36
|
+
import { Label } from "./UIComponents/Label.js";
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
/** @type {Root} */
|
|
40
|
+
let root;
|
|
41
|
+
let clickTimes=1;
|
|
42
|
+
|
|
43
|
+
window.setup = function(){
|
|
44
|
+
createCanvas(windowWidth, windowHeight);
|
|
45
|
+
root = new Root();
|
|
46
|
+
|
|
47
|
+
let btnWidth = 200;
|
|
48
|
+
let btnHeight = 100;
|
|
49
|
+
|
|
50
|
+
let btn = new Label((windowWidth/2)-(btnWidth/2), (windowHeight/2)-(btnHeight/2), 200, 100, "Hello! 👋",
|
|
51
|
+
{cornerRadius: 20,
|
|
52
|
+
backgroundColor: "rgba(0, 0, 0, 1)",
|
|
53
|
+
textColor: "rgba(255, 255, 255, 1)",
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
btn.addEventListener("click", (event)=>{
|
|
57
|
+
clickTimes+=1;
|
|
58
|
+
event.target.setText(`You clicked ${clickTimes} \ntimes!`);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
root.add(btn);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
window.draw = function () {
|
|
65
|
+
background('rgba(255,255,255,255)');
|
|
66
|
+
root.show();
|
|
67
|
+
root.mouseEnterEventListeners(mouseX, mouseY);
|
|
68
|
+
root.hoverEventListeners(mouseX, mouseY);
|
|
69
|
+
root.mouseLeaveEventListeners(mouseX, mouseY);
|
|
70
|
+
root.keyDownEventListeners(mouseX, mouseY);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
window.mouseDragged = function (){
|
|
74
|
+
root.mouseDraggedEventListeners(mouseX, mouseY);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
window.mouseClicked = function(){
|
|
78
|
+
root.mouseClickedEventListeners(mouseX, mouseY);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
window.mousePressed = function(){
|
|
82
|
+
root.mousePressedEventListeners(mouseX, mouseY);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
window.mouseReleased = function(){
|
|
86
|
+
root.mouseReleasedEventListeners(mouseX, mouseY);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
window.mouseWheel = function(event){
|
|
90
|
+
root.mouseWheelEventListeners(mouseX, mouseY, event);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
window.keyPressed = function(event){
|
|
94
|
+
if (keyCode === UP_ARROW || keyCode === DOWN_ARROW || keyCode === LEFT_ARROW || keyCode === RIGHT_ARROW) {
|
|
95
|
+
event.preventDefault();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
root.keyPressedEventListeners(mouseX, mouseY);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## 🧰 Components Overview
|
|
103
|
+
- `Label(x, y, width, height, text, {options})`
|
|
104
|
+
- `ScrollFrame`, `GridFrame`, `Label`, etc.
|
|
105
|
+
|
|
106
|
+
More components and documentation coming soon.
|
|
107
|
+
|
|
108
|
+
## 📚 Documentation & Tutorials
|
|
109
|
+
A full **video series** and documentation site are in the works!
|
|
110
|
+
Stay tuned for:
|
|
111
|
+
- Getting started tutorials
|
|
112
|
+
- Custom component creation
|
|
113
|
+
- Internals of CrowJS for contributors
|
|
114
|
+
|
|
115
|
+
## 💡 Why CrowJS?
|
|
116
|
+
While p5.js is amazing for generative art and simulations, there's a noticeable gap when it comes to in-sketch UI. CrowJS fills that gap by giving you a canvas-first GUI framework that blends seamlessly with your visuals.
|
|
117
|
+
|
|
118
|
+
## 🤝 Contributing
|
|
119
|
+
CrowJS is open-source! Contributions, bug reports, and ideas are welcome.
|
|
120
|
+
See the `CONTRIBUTING.md` file for details.
|
|
121
|
+
|
|
122
|
+
## 📄 License
|
|
123
|
+
This project is licensed under the MIT License.
|
|
124
|
+
See the `LICENSE` file for details.
|
|
125
|
+
|
|
126
|
+
## 🌐 Links
|
|
127
|
+
- My sketches: [https://editor.p5js.org/Usman_Ali/sketches/](p5js.org/Usman_Ali/sketches/)
|
|
128
|
+
- p5.js: [https://p5js.org](https://p5js.org)
|
|
129
|
+
|
|
130
|
+
Created with ❤️ by **Usman**
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { GUIEvent } from "../Core/GUIEvent/GUIEvent.js";
|
|
2
|
+
import { UIComponent } from "./UIComponent.js";
|
|
3
|
+
import { Component } from "../Core/Component.js";
|
|
4
|
+
|
|
5
|
+
export class Input extends UIComponent{
|
|
6
|
+
/**
|
|
7
|
+
* Creates a base input component
|
|
8
|
+
* @param {number} x - The x-coordinate
|
|
9
|
+
* @param {number} y - The y-coordinate
|
|
10
|
+
* @param {number} width - The width
|
|
11
|
+
* @param {number} height - The height
|
|
12
|
+
* @param {p5.Color} backgroundColor - Background color
|
|
13
|
+
* @param {boolean} borderFlag - Whether to show border
|
|
14
|
+
* @param {p5.Color} borderColor - Border color
|
|
15
|
+
* @param {number} borderWidth - Border width
|
|
16
|
+
* @param {number} cornerRadius - Corner radius
|
|
17
|
+
* @param {boolean} enableShadow - Enable shadow
|
|
18
|
+
* @param {string} shadowColor - Shadow color
|
|
19
|
+
* @param {number} shadowIntensity - Shadow opacity
|
|
20
|
+
* @param {number} shadowSpread - Shadow spread
|
|
21
|
+
* @param {number} shadowDetail - Shadow layers
|
|
22
|
+
* @param {Object} options - Additional options
|
|
23
|
+
* @param {Component|null} options.parent - Parent component
|
|
24
|
+
* @param {string} options.type - Component type
|
|
25
|
+
* @param {string|null} options.id - Component ID
|
|
26
|
+
*/
|
|
27
|
+
constructor(x, y, width, height, backgroundColor, borderFlag, borderColor, borderWidth,
|
|
28
|
+
cornerRadius, enableShadow, shadowColor, shadowIntensity, shadowSpread, shadowDetail,
|
|
29
|
+
{parent=null, type="", id=null} = {}
|
|
30
|
+
){
|
|
31
|
+
super(x, y, width, height, backgroundColor, borderFlag, borderColor,
|
|
32
|
+
borderWidth, cornerRadius, enableShadow, shadowColor, shadowIntensity,
|
|
33
|
+
shadowSpread, shadowDetail, {parent: parent, type: type, id: id});
|
|
34
|
+
this.isFocused = false;
|
|
35
|
+
// this.addEventListener("focus", (event)=>this.onFocus());
|
|
36
|
+
// this.addEventListener("blur", (event)=>this.onBlur());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Handles focus event
|
|
41
|
+
* @param {GUIEvent} event - The focus event
|
|
42
|
+
*/
|
|
43
|
+
onFocus(event){
|
|
44
|
+
console.log("focused...");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Handles blur event
|
|
49
|
+
* @param {GUIEvent} event - The blur event
|
|
50
|
+
*/
|
|
51
|
+
onBlur(event){
|
|
52
|
+
console.log("blurred...");
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Sets focus on the input field with visual feedback
|
|
56
|
+
*/
|
|
57
|
+
focus(){
|
|
58
|
+
// console.log("Focus!");
|
|
59
|
+
if(!this.isFocused){
|
|
60
|
+
this.isFocused = true;
|
|
61
|
+
this.borderWidth += 3;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.dispatchEventOnlyOnSelf(new GUIEvent(0, 0, "focus", this));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Removes focus from the input field
|
|
68
|
+
*/
|
|
69
|
+
blur(){
|
|
70
|
+
// console.log("Blur!");
|
|
71
|
+
if(this.isFocused){
|
|
72
|
+
this.isFocused = false;
|
|
73
|
+
this.borderWidth -= 3;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this.dispatchEventOnlyOnSelf(new GUIEvent(0, 0, "blur", this));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import {UIComponent} from './UIComponent.js';
|
|
2
|
+
import { Component } from '../Core/Component.js';
|
|
3
|
+
|
|
4
|
+
export class Label extends UIComponent{
|
|
5
|
+
/**
|
|
6
|
+
* Creates a text label component
|
|
7
|
+
* @param {number} x - The x-coordinate
|
|
8
|
+
* @param {number} y - The y-coordinate
|
|
9
|
+
* @param {number} width - The width
|
|
10
|
+
* @param {number} height - The height
|
|
11
|
+
* @param {string} label - The text to display
|
|
12
|
+
* @param {Object} options - Configuration options
|
|
13
|
+
* @param {string|null} options.id - Component ID
|
|
14
|
+
* @param {Component|null} options.parent - Parent component
|
|
15
|
+
* @param {p5.Color} options.backgroundColor - Background color
|
|
16
|
+
* @param {p5.Color} options.textColor - Text color
|
|
17
|
+
* @param {boolean} options.borderFlag - Whether to show border
|
|
18
|
+
* @param {p5.Color} options.borderColor - Border color
|
|
19
|
+
* @param {number} options.borderWidth - Border width
|
|
20
|
+
* @param {number} options.cornerRadius - Corner radius
|
|
21
|
+
* @param {boolean} options.enableShadow - Enable shadow rendering
|
|
22
|
+
* @param {string} options.shadowColor - Shadow color
|
|
23
|
+
* @param {number} options.shadowIntensity - Shadow opacity
|
|
24
|
+
* @param {number} options.shadowSpread - Shadow spread
|
|
25
|
+
* @param {number} options.shadowDetail - Shadow layers
|
|
26
|
+
* @param {string} options.HTextAlign - Horizontal text alignment
|
|
27
|
+
* @param {string} options.VTextAlign - Vertical text alignment
|
|
28
|
+
* @param {number} options.pad - General padding
|
|
29
|
+
* @param {number} options.padx - Horizontal padding
|
|
30
|
+
* @param {number} options.pady - Vertical padding
|
|
31
|
+
* @param {number} options.padl - Left padding
|
|
32
|
+
* @param {number} options.padr - Right padding
|
|
33
|
+
* @param {number} options.padt - Top padding
|
|
34
|
+
* @param {number} options.padb - Bottom padding
|
|
35
|
+
*/
|
|
36
|
+
constructor(x, y, width, height, label,
|
|
37
|
+
{id=null,
|
|
38
|
+
parent = null,
|
|
39
|
+
backgroundColor = color(200),
|
|
40
|
+
textColor = color(0),
|
|
41
|
+
borderFlag = true,
|
|
42
|
+
borderColor = color(0),
|
|
43
|
+
borderWidth = 1,
|
|
44
|
+
cornerRadius = 0,
|
|
45
|
+
enableShadow=false,
|
|
46
|
+
shadowColor= 'rgb(0,0,0)',
|
|
47
|
+
shadowIntensity= 0.4,
|
|
48
|
+
shadowSpread= 3,
|
|
49
|
+
shadowDetail=5,
|
|
50
|
+
HTextAlign="center",
|
|
51
|
+
VTextAlign="center",
|
|
52
|
+
pad = 5,
|
|
53
|
+
padx = 0,
|
|
54
|
+
pady = 0,
|
|
55
|
+
padl = 0,
|
|
56
|
+
padr = 0,
|
|
57
|
+
padt = 0,
|
|
58
|
+
padb = 0,
|
|
59
|
+
} = {}) {
|
|
60
|
+
super(x, y, width, height, backgroundColor, borderFlag, borderColor,
|
|
61
|
+
borderWidth, cornerRadius, enableShadow, shadowColor, shadowIntensity,
|
|
62
|
+
shadowSpread, shadowDetail, {parent: parent, type: "UIComponent", id: id});
|
|
63
|
+
|
|
64
|
+
this.text = label;
|
|
65
|
+
this.labelSize = 20;
|
|
66
|
+
this.textColor = textColor;
|
|
67
|
+
|
|
68
|
+
this.HTextAlign = HTextAlign;
|
|
69
|
+
this.VTextAlign = VTextAlign;
|
|
70
|
+
|
|
71
|
+
this.pad = pad;
|
|
72
|
+
this.padx = padx;
|
|
73
|
+
this.pady = pady;
|
|
74
|
+
this.padl = padl;
|
|
75
|
+
this.padr = padr;
|
|
76
|
+
this.padt = padt;
|
|
77
|
+
this.padb = padb;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// if(this.enableShadow){
|
|
81
|
+
// this.shadowColor = shadowColor;//rgb value
|
|
82
|
+
// this.shadowIntensity = shadowIntensity;//opacity value between 0 and 1
|
|
83
|
+
// this.shadowSpread = shadowSpread;//stroke width of each of those rectangles
|
|
84
|
+
// this.shadowDetail = shadowDetail;//number of rectangles that will be drawn around the component
|
|
85
|
+
// }
|
|
86
|
+
/**
|
|
87
|
+
* Renders the label with text, background, and border
|
|
88
|
+
*/
|
|
89
|
+
show() {
|
|
90
|
+
if(this.enableShadow){
|
|
91
|
+
this.drawShadow();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
push();
|
|
95
|
+
beginClip();
|
|
96
|
+
rect(this.x, this.y, this.width, this.height, this.cornerRadius);
|
|
97
|
+
endClip();
|
|
98
|
+
translate(this.x, this.y);
|
|
99
|
+
fill(this.backgroundColor);
|
|
100
|
+
|
|
101
|
+
// Background rectangle
|
|
102
|
+
rect(0, 0, this.width, this.height, this.cornerRadius);
|
|
103
|
+
|
|
104
|
+
// Text
|
|
105
|
+
fill(this.textColor);
|
|
106
|
+
// textAlign(CENTER, CENTER);
|
|
107
|
+
textSize(this.labelSize);
|
|
108
|
+
|
|
109
|
+
let x;
|
|
110
|
+
if(this.HTextAlign === "left"){
|
|
111
|
+
textAlign(LEFT, CENTER);
|
|
112
|
+
x = this.pad;
|
|
113
|
+
} else if(this.HTextAlign === "right"){
|
|
114
|
+
textAlign(RIGHT, CENTER);
|
|
115
|
+
x = this.width - this.pad;
|
|
116
|
+
} else{
|
|
117
|
+
//center
|
|
118
|
+
textAlign(CENTER, CENTER);
|
|
119
|
+
x = this.width / 2;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let y;
|
|
123
|
+
if(this.VTextAlign === "top"){
|
|
124
|
+
textAlign(this.getHTextAlign(), BOTTOM);
|
|
125
|
+
y = this.labelSize + this.pad;
|
|
126
|
+
} else if(this.VTextAlign === "bottom"){
|
|
127
|
+
textAlign(this.getHTextAlign(), TOP);
|
|
128
|
+
y = this.height - this.labelSize - this.pad;
|
|
129
|
+
} else {
|
|
130
|
+
//center
|
|
131
|
+
textAlign(this.getHTextAlign(), CENTER);
|
|
132
|
+
y = this.height / 2;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
text(this.text, x, y);
|
|
136
|
+
// rect(x, y, this.labelSize, this.labelSize);
|
|
137
|
+
|
|
138
|
+
// Border
|
|
139
|
+
if (this.borderFlag) {
|
|
140
|
+
noFill(); // Only for the border
|
|
141
|
+
stroke(this.borderColor);
|
|
142
|
+
strokeWeight(this.borderWidth);
|
|
143
|
+
rect(0, 0, this.width, this.height, this.cornerRadius); // Fix here
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
pop();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Converts horizontal alignment string to P5 constant
|
|
151
|
+
* @returns {number} P5 alignment constant
|
|
152
|
+
*/
|
|
153
|
+
getHTextAlign(){
|
|
154
|
+
switch(this.HTextAlign){
|
|
155
|
+
case "left":
|
|
156
|
+
return LEFT;
|
|
157
|
+
case "right":
|
|
158
|
+
return RIGHT;
|
|
159
|
+
default:
|
|
160
|
+
return CENTER;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Converts vertical alignment string to P5 constant
|
|
166
|
+
* @returns {number} P5 alignment constant
|
|
167
|
+
*/
|
|
168
|
+
getVTextAlign(){
|
|
169
|
+
switch(this.VTextAlign){
|
|
170
|
+
case "top":
|
|
171
|
+
return TOP;
|
|
172
|
+
case "bottom":
|
|
173
|
+
return BOTTOM;
|
|
174
|
+
default:
|
|
175
|
+
return CENTER;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Updates the label text and recalculates text size
|
|
181
|
+
* @param {string} text - The new text to display
|
|
182
|
+
*/
|
|
183
|
+
setText(text){
|
|
184
|
+
this.text = text;
|
|
185
|
+
this.updateLabelSize();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
//needs heavy optimizations
|
|
189
|
+
/**
|
|
190
|
+
* Dynamically calculates the optimal text size to fit the container
|
|
191
|
+
* Uses binary search for efficient size calculation
|
|
192
|
+
*/
|
|
193
|
+
updateLabelSize() {
|
|
194
|
+
let maxSize = min(this.width * 0.9, this.height * 0.8);
|
|
195
|
+
let minSize = 1;
|
|
196
|
+
let low = minSize;
|
|
197
|
+
let high = maxSize;
|
|
198
|
+
let bestSize = minSize;
|
|
199
|
+
|
|
200
|
+
const maxLabelWidth = this.width * 0.9;
|
|
201
|
+
const maxLabelHeight = this.height * 0.8;
|
|
202
|
+
|
|
203
|
+
while (low <= high) {
|
|
204
|
+
let mid = Math.floor((low + high) / 2);
|
|
205
|
+
textSize(mid);
|
|
206
|
+
let labelWidth = textWidth(this.text);
|
|
207
|
+
let labelHeight = textAscent() + textDescent();
|
|
208
|
+
|
|
209
|
+
if (labelWidth <= maxLabelWidth && labelHeight <= maxLabelHeight) {
|
|
210
|
+
bestSize = mid;
|
|
211
|
+
low = mid + 1;
|
|
212
|
+
} else {
|
|
213
|
+
high = mid - 1;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
this.labelSize = bestSize;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Handles width changes and updates text size accordingly
|
|
222
|
+
*/
|
|
223
|
+
updateWidth(){
|
|
224
|
+
this.updateLabelSize();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Handles height changes and updates text size accordingly
|
|
229
|
+
*/
|
|
230
|
+
updateHeight(){
|
|
231
|
+
this.updateLabelSize();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|