summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKyle Gunger <kgunger12@gmail.com>2024-11-13 02:23:34 -0500
committerKyle Gunger <kgunger12@gmail.com>2024-11-13 02:23:34 -0500
commitff981cd5b7ab9274630a1417593af96e36b99e7a (patch)
treeaea5306f3c1f046893c7160b8350fd9808773f91
parent53c95ab94cab5163424646d4a798a7ea7fb13ec7 (diff)
Color Wheel Widget
-rw-r--r--index.html6
-rw-r--r--scripts/gui-common/color.js53
-rw-r--r--scripts/gui-common/widgets.js244
-rw-r--r--scripts/main.js2
-rw-r--r--styles/widgets.css37
5 files changed, 264 insertions, 78 deletions
diff --git a/index.html b/index.html
index 23578ff..f5fd475 100644
--- a/index.html
+++ b/index.html
@@ -33,12 +33,6 @@
</nav>
<content>
- <div class="widget button toggle">Lamp 1</div>
- <div class="widget slider" style="--percent: 0.3;"><div class="detail">10%</div></div>
- <div class="widget button checkbox"></div>
- <div class="widget color-wheel" style="--pos-x: 0.3; --pos-y: 0.3;"></div>
- <div class="widget color-temp" style="--percent: 0.3;"></div>
- <div class="widget color-light" style="--percent: 0.3;"></div>
<div class="widget thermostat" style="--percent: 0.3; --arch-color: #0084ff;">
<arch></arch>
<gague>68</gague>
diff --git a/scripts/gui-common/color.js b/scripts/gui-common/color.js
index 90eac14..0b25f68 100644
--- a/scripts/gui-common/color.js
+++ b/scripts/gui-common/color.js
@@ -52,8 +52,61 @@ class Color
{
return new Color(r / 255, g / 255, b / 255, a);
}
+
+ static d(h)
+ {
+ let d = Math.floor((h - (h % PI_THIRDS)) / PI_THIRDS);
+ return (DIVS.length - 1 + d) % (DIVS.length - 1);
+ }
+
+ static from_hsv(h, s, v)
+ {
+ h = (((h % TWO_PI)) + TWO_PI) % TWO_PI;
+
+ let d = Color.d(h);
+
+ h = (h % PI_THIRDS) / PI_THIRDS;
+
+ let out = DIVS[d].interpolate(DIVS[d + 1], h);
+ out = WHITE.interpolate(out, s);
+
+ return BLACK.interpolate(out, v);
+ }
+
+ static from_hsl(h, s, l)
+ {
+ h = (((h % TWO_PI)) + TWO_PI) % TWO_PI;
+
+ let d = Color.d(h);
+
+ h = (h % PI_THIRDS) / PI_THIRDS;
+
+ let out = DIVS[d].interpolate(DIVS[d + 1], h);
+ let L = BLACK.interpolate(WHITE, l);
+
+ if (l < 0.5)
+ out = BLACK.interpolate(out, l / 0.5);
+ else
+ out = out.interpolate(WHITE, (l - 0.5) / 0.5);
+
+ return L.interpolate(out, s);
+ }
}
+const RED = new Color(1, 0, 0, 1);
+const YELLOW = new Color(1, 1, 0, 1);
+const GREEN = new Color(0, 1, 0, 1);
+const CYAN = new Color(0, 1, 1, 1);
+const BLUE = new Color(0, 0, 1, 1);
+const MAGENTA = new Color(1, 0, 1, 1);
+const WHITE = new Color(1, 1, 1, 1);
+const BLACK = new Color(0, 0, 0, 1);
+
+const PI_THIRDS = Math.PI / 3;
+const TWO_PI = Math.PI * 2;
+
+const DIVS = [RED, YELLOW, GREEN, CYAN, BLUE, MAGENTA, RED];
+
/**
* Interpolate between two colors
* @param {Color} a
diff --git a/scripts/gui-common/widgets.js b/scripts/gui-common/widgets.js
index 88f1db8..9ec1712 100644
--- a/scripts/gui-common/widgets.js
+++ b/scripts/gui-common/widgets.js
@@ -28,6 +28,8 @@ class Widget extends EventTarget{
this.element.addEventListener("mouseleave", this.#emitMouseEvent.bind(this));
this.element.addEventListener("mouseenter", this.#emitMouseEvent.bind(this));
+ this.element.addEventListener("click", this.#emitMouseEvent.bind(this));
+
this.element.addEventListener("touchstart", this.#emitTouchEvent.bind(this));
this.element.addEventListener("touchend", this.#emitTouchEvent.bind(this));
this.element.addEventListener("touchmove", this.#emitTouchEvent.bind(this));
@@ -236,7 +238,7 @@ class WidgetCheckbox extends WidgetToggle {
}
}
-class WidgetSlider extends Widget
+class WidgetDragable extends Widget
{
/** @type {number} */
#primed = 0;
@@ -244,13 +246,92 @@ class WidgetSlider extends Widget
/** @type {number} */
#gone = 0;
- #boundUp = null;
- #boundMove = null;
+ /** @type {(e: MouseEvent) => void} */
+ #m_up = null;
+ /** @type {(e: MouseEvent, b: number) => void} */
+ #m_move = null;
+
+ /** @type {(e: TouchEvent) => void} */
+ #t_up = null;
+ /** @type {(e: TouchEvent) => void} */
+ #t_move = null;
+
+
+ /**
+ * Constructor
+ */
+ constructor ()
+ {
+ super();
+
+ this.#m_up = this.#unpress.bind(this);
+ this.#m_move = this.#move.bind(this);
+
+ this.addEventListener("mousedown", this.#press);
+ this.addEventListener("mousemove", this.#move);
+ this.addEventListener("mouseup", this.#unpress);
+ this.addEventListener("mouseleave", this.#leave);
+ this.addEventListener("mouseenter", this.#enter);
+ }
+
+ /** @param {MouseEvent} event */
+ #press(event)
+ {
+ this.#primed |= (1 << event.button);
+ this.#move(event);
+ }
+
+ /** @param {MouseEvent} event */
+ #unpress(event)
+ {
+ if (((1 << event.button) & this.#primed) == 0)
+ return;
+ this.#primed -= (1 << event.button);
+ this.#move(event);
+ if (this.#primed == 0)
+ {
+ if (this.#gone)
+ {
+ this.#gone = 0;
+ window.removeEventListener("mouseup", this.#m_up);
+ window.removeEventListener("mousemove", this.#m_move);
+ }
+ }
+ }
+
+ /** @param {MouseEvent} event */
+ #move(event)
+ {
+ this.move(event, this.#primed);
+ }
+
+ /** @param {MouseEvent} event */
+ #leave(event)
+ {
+ if (this.#primed == 0)
+ return;
+ this.#gone = 1;
+ window.addEventListener("mouseup", this.#m_up);
+ window.addEventListener("mousemove", this.#m_move);
+ }
+
+ /** @param {MouseEvent} event */
+ #enter(event)
+ {
+ if (this.#primed == 0)
+ return;
+ this.#gone = 0;
+ window.removeEventListener("mouseup", this.#m_up);
+ window.removeEventListener("mousemove", this.#m_move);
+ }
+}
+class WidgetSlider extends WidgetDragable
+{
/** @type {HTMLElement} */
#detail = null;
/** @type {(e: HTMLElement, v: number, p: number) => void} */
- #detailUpdater = null;
+ #u_detail = null;
/** @type {number} */
#tmpNum = 0;
@@ -286,37 +367,29 @@ class WidgetSlider extends Widget
this.#detail = document.createElement("div");
this.#detail.classList.add("detail");
this.element.appendChild(this.#detail);
-
- this.addEventListener("mousedown", this.#press);
- this.addEventListener("mousemove", this.#move);
- this.addEventListener("mouseup", this.#unpress);
- this.addEventListener("mouseleave", this.#leave);
- this.addEventListener("mouseenter", this.#enter);
+
this.addEventListener("change", this.#change);
-
- this.#boundUp = this.#unpress.bind(this);
- this.#boundMove = this.#move.bind(this);
+
this.#max = max;
this.#min = min;
this.#step = step;
this.#precision = trunc;
this.#percent = percent;
- this.#detailUpdater = this.update_detail.bind(this);
- }
-
- /** @param {MouseEvent} event */
- #press(event)
- {
- this.#primed |= (1 << event.button);
- this.#move(event);
+ this.#u_detail = this.update_detail.bind(this);
}
- /** @param {MouseEvent} event */
- #move(event)
+ /**
+ * @param {MouseEvent} event
+ * @param {number} btns
+ */
+ move(event, btns)
{
- if (this.#primed == 0)
+ if (btns == 0)
+ {
+ this.set(this.#tmpNum);
return;
+ }
let rect = this.element.getBoundingClientRect();
let top = 0, bot = 0, point = 0;
@@ -361,48 +434,10 @@ class WidgetSlider extends Widget
this.#tmpNum = Math.min(Math.max(this.#tmpNum, this.#min), this.#max);
let percent = (this.#tmpNum - this.#min) / (this.#max - this.#min);
- this.#detailUpdater(this.#detail, this.#tmpNum, percent, this.#percent);
+ this.#u_detail(this.#detail, this.#tmpNum, percent, this.#percent);
this.element.style.setProperty("--percent", percent);
}
- /** @param {MouseEvent} event */
- #unpress(event)
- {
- if (((1 << event.button) & this.#primed) == 0)
- return;
- this.#primed -= (1 << event.button);
- if (this.#primed == 0)
- {
- this.set(this.#tmpNum);
- if (this.#gone)
- {
- this.#gone = 0;
- window.removeEventListener("mouseup", this.#boundUp);
- window.removeEventListener("mousemove", this.#boundMove);
- }
- }
- }
-
- /** @param {MouseEvent} event */
- #leave(event)
- {
- if (this.#primed == 0)
- return;
- this.#gone = 1;
- window.addEventListener("mouseup", this.#boundUp);
- window.addEventListener("mousemove", this.#boundMove);
- }
-
- /** @param {MouseEvent} event */
- #enter(event)
- {
- if (this.#primed == 0)
- return;
- this.#gone = 0;
- window.removeEventListener("mouseup", this.#boundUp);
- window.removeEventListener("mousemove", this.#boundMove);
- }
-
update_detail(el, val, percent)
{
if (this.#percent)
@@ -413,7 +448,7 @@ class WidgetSlider extends Widget
setDetailUpdater(updater)
{
- this.#detailUpdater = updater;
+ this.#u_detail = updater;
}
/** @param {number} m */
@@ -514,10 +549,83 @@ class WidgetColorLight extends WidgetSlider
}
}
-/** @typedef {Widget<number>} WidgetSlider */
-/** @typedef {Widget<string>} WidgetColorWheel */
-/** @typedef {Widget<number>} WidgetColorTemp */
-/** @typedef {Widget<number>} WidgetColorLight */
+class WidgetColorWheel extends WidgetDragable
+{
+ /** @type {Color} */
+ #tmpColor = null;
+ /** @type {HTMLElement} */
+ #detail = null;
+
+ /**
+ * Constructor
+ */
+ constructor ()
+ {
+ super();
+ this.element.classList.add("color-wheel");
+
+ this.#detail = document.createElement("div");
+ this.#detail.classList.add("detail");
+ this.element.appendChild(this.#detail);
+ }
+
+ /**
+ * @param {MouseEvent} event
+ * @param {number} btns
+ */
+ move(event, btns)
+ {
+ if (btns == 0)
+ {
+ this.set(this.#tmpColor);
+ return;
+ }
+
+ let rect = this.element.getBoundingClientRect();
+
+ // Points
+ let tmpX = event.clientX;
+ let tmpY = rect.bottom - event.clientY + rect.top;
+
+ // Percents
+ tmpX = (tmpX - rect.left) / (rect.right - rect.left);
+ tmpY = (tmpY - rect.top) / (rect.bottom - rect.top);
+
+ // Vecs
+ tmpX = (Math.min(Math.max(0.0, tmpX), 1.0) - 0.5) * 2;
+ tmpY = (Math.min(Math.max(0.0, tmpY), 1.0) - 0.5) * 2;
+
+ // Normalized
+ let mag = Math.sqrt(tmpX * tmpX + tmpY * tmpY);
+ if (mag > 1)
+ {
+ tmpX /= mag;
+ tmpY /= mag;
+ }
+
+ this.element.style.setProperty("--pos-x", tmpX);
+ this.element.style.setProperty("--pos-y", tmpY);
+ this.update_detail(tmpX, tmpY, mag);
+ }
+
+ update_detail(x, y, mag)
+ {
+ if (x == 0)
+ {
+ if (y > 0)
+ this.#tmpColor = Color.from_hsv(Math.PI / 2, mag, 1);
+ else
+ this.#tmpColor = Color.from_hsv(-Math.PI / 2, mag, 1);
+ }
+ else if (x < 0)
+ this.#tmpColor = Color.from_hsv(Math.atan(y / x) + Math.PI, mag, 1);
+ else
+ this.#tmpColor = Color.from_hsv(Math.atan(y / x), mag, 1);
+
+ this.element.style.setProperty("--detail", this.#tmpColor.rgb());
+ }
+}
+
/** @typedef {Widget<number> & {getGague: () => number, setGague: (value: number) => void}} WidgetThermostat */
/** @typedef {Widget<any> & {addSelection: (name: string, value: any) => void, setSelection: (name: string) => boolean, removeSelection: (name: string) => void, getSelection: () => string}} WidgetSelectButton */
diff --git a/scripts/main.js b/scripts/main.js
index ac6dbe9..3cdbe12 100644
--- a/scripts/main.js
+++ b/scripts/main.js
@@ -16,6 +16,8 @@ class Client {
content.appendChild(this.temp.element);
this.light = new WidgetColorLight();
content.appendChild(this.light.element);
+ this.wheel = new WidgetColorWheel();
+ content.appendChild(this.wheel.element);
// content.appendChild(Widget("button").el);
// content.appendChild(Widget("checkbox").el);
// content.appendChild(Widget("slider").el);
diff --git a/styles/widgets.css b/styles/widgets.css
index 5a29507..5a7728e 100644
--- a/styles/widgets.css
+++ b/styles/widgets.css
@@ -147,7 +147,7 @@ input {
height: calc(100% * var(--percent));
width: 100%;
background-color: var(--w-sl-fill);
-
+ transition-duration: 0.15s;
content: '';
}
@@ -162,6 +162,7 @@ input {
.slider:active > .fill::before {
background-color: var(--w-sl-fill-active);
+ transition-duration: 0s;
}
.slider::after {
@@ -185,7 +186,7 @@ input {
border-bottom: var(--w-sl-dots);
}
-.slider > .detail, .color-light > .detail, .color-temp > .detail {
+.slider > .detail, .color-light > .detail, .color-temp > .detail, .color-wheel > .detail {
display: block;
position: absolute;
@@ -203,10 +204,13 @@ input {
width: calc(1.2 * var(--base-unit));
height: var(--base-unit);
pointer-events: none;
+ user-select: none;
align-content: center;
text-align: center;
padding: 2px;
+
+ transition-duration: 0.15s;
}
.slider.h > .detail, .color-light.h > .detail, .color-temp.h > .detail {
@@ -217,10 +221,12 @@ input {
.slider:active > .detail {
background-color: rgba(0, 132, 255, 1);
color: rgba(255, 255, 255, 1);
+ transition-duration: 0s;
}
-.color-light:active > .detail, .color-temp:active > .detail {
+.color-light:active > .detail, .color-temp:active > .detail, .color-wheel:active > .detail {
background-color: var(--detail);
+ transition-duration: 0s;
}
.inactive > .detail {
@@ -334,7 +340,7 @@ input {
*/
.color-wheel {
- background: radial-gradient(white, transparent calc(1.5 * var(--base-unit))), conic-gradient(#f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00);
+ background: radial-gradient(white, transparent calc(1.5 * var(--base-unit))), conic-gradient(from 90deg, #f00, #f0f, #00f, #0ff, #0f0, #ff0, #f00);
border-radius: 50%;
width: calc(3 * var(--base-unit));
height: calc(3 * var(--base-unit));
@@ -356,6 +362,10 @@ input {
box-sizing: border-box;
}
+.color-wheel:active::after {
+ transition-duration: 0s;
+}
+
.color-wheel:hover::after {
border-color: #888;
}
@@ -368,6 +378,19 @@ input {
border-color: rgb(68, 68, 68, 0);
}
+.color-wheel > .detail {
+ width: calc(var(--base-unit) * 0.5);
+ height: calc(var(--base-unit) * 0.5);
+ border-radius: 5px;
+ box-shadow: none;
+ top: 0;
+ left: calc(100% + 7px);
+}
+
+.color-wheel:active > .detail {
+ box-shadow: 1px 1px var(--w-shadow), 3px 3px var(--w-shadow), 5px 5px var(--w-shadow);
+}
+
.color-temp {
width: var(--base-unit);
height: calc(3 * var(--base-unit));
@@ -396,6 +419,12 @@ input {
box-sizing: border-box;
border-radius: 7px;
+
+ transition-duration: 0.15s;
+}
+
+.color-temp:active::after, .color-light:active::after {
+ transition-duration: 0s;
}
.color-temp:hover::after, .color-light:hover::after {