cut only on defocus
This commit is contained in:
parent
0856204579
commit
3bcd80b32a
|
|
@ -9,8 +9,14 @@ export class FolkEventPropagator extends FolkRope {
|
|||
static styles = [
|
||||
...FolkRope.styles,
|
||||
css`
|
||||
textarea {
|
||||
.input-container {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
translate: -50% -50%;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: auto;
|
||||
min-width: 3ch;
|
||||
height: auto;
|
||||
|
|
@ -21,8 +27,17 @@ export class FolkEventPropagator extends FolkRope {
|
|||
pointer-events: auto;
|
||||
overflow: hidden;
|
||||
field-sizing: content;
|
||||
translate: -50% -50%;
|
||||
border-radius: 5px;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.trigger {
|
||||
border-radius: 5px 5px 0 0;
|
||||
border-bottom: none;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.expression {
|
||||
border-radius: 0 5px 5px 5px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
|
@ -33,10 +48,16 @@ export class FolkEventPropagator extends FolkRope {
|
|||
#triggerTextarea = document.createElement('textarea');
|
||||
#expressionTextarea = document.createElement('textarea');
|
||||
#propagator: Propagator | null = null;
|
||||
#container = document.createElement('div');
|
||||
#hasError = false;
|
||||
|
||||
override firstUpdated(changedProperties: PropertyValues<this>): void {
|
||||
super.firstUpdated(changedProperties);
|
||||
|
||||
this.#container.className = 'input-container';
|
||||
this.#triggerTextarea.className = 'trigger';
|
||||
this.#expressionTextarea.className = 'expression';
|
||||
|
||||
this.#triggerTextarea.addEventListener('change', () => {
|
||||
this.trigger = this.#triggerTextarea.value;
|
||||
});
|
||||
|
|
@ -45,10 +66,17 @@ export class FolkEventPropagator extends FolkRope {
|
|||
this.expression = this.#expressionTextarea.value;
|
||||
});
|
||||
|
||||
this.#expressionTextarea.addEventListener('focusout', () => {
|
||||
if (this.#hasError) {
|
||||
this.cut();
|
||||
}
|
||||
});
|
||||
|
||||
this.#triggerTextarea.value = this.trigger;
|
||||
this.#expressionTextarea.value = this.expression;
|
||||
|
||||
this.renderRoot.append(this.#triggerTextarea, this.#expressionTextarea);
|
||||
this.#container.append(this.#triggerTextarea, this.#expressionTextarea);
|
||||
this.renderRoot.append(this.#container);
|
||||
|
||||
this.#initializePropagator();
|
||||
}
|
||||
|
|
@ -73,24 +101,23 @@ export class FolkEventPropagator extends FolkRope {
|
|||
target: this.targetElement,
|
||||
event: this.trigger,
|
||||
handler: this.expression,
|
||||
onParseError: () => this.cut(),
|
||||
onParseSuccess: () => this.mend(),
|
||||
onParseError: () => {
|
||||
this.#hasError = true;
|
||||
},
|
||||
onParseSuccess: () => {
|
||||
this.#hasError = false;
|
||||
this.mend();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
override render() {
|
||||
super.render();
|
||||
|
||||
const triggerPoint = this.points[Math.floor(this.points.length / 5)];
|
||||
if (triggerPoint) {
|
||||
this.#triggerTextarea.style.left = `${triggerPoint.pos.x}px`;
|
||||
this.#triggerTextarea.style.top = `${triggerPoint.pos.y}px`;
|
||||
}
|
||||
|
||||
const expressionPoint = this.points[Math.floor(this.points.length / 2)];
|
||||
if (expressionPoint) {
|
||||
this.#expressionTextarea.style.left = `${expressionPoint.pos.x}px`;
|
||||
this.#expressionTextarea.style.top = `${expressionPoint.pos.y}px`;
|
||||
const point = this.getPointAt(0.5);
|
||||
if (point) {
|
||||
this.#container.style.left = `${point.pos.x}px`;
|
||||
this.#container.style.top = `${point.pos.y}px`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ import { css, PropertyValues } from '@lit/reactive-element';
|
|||
import { AnimationFrameController, AnimationFrameControllerHost } from './common/animation-frame-controller.ts';
|
||||
import { property } from '@lit/reactive-element/decorators.js';
|
||||
|
||||
const lerp = (first: number, second: number, percentage: number) => first + (second - first) * percentage;
|
||||
|
||||
// Each rope part is one of these uses a high precision variant of Störmer–Verlet integration to keep the simulation consistent otherwise it would "explode"!
|
||||
interface RopePoint {
|
||||
pos: Point;
|
||||
|
|
@ -103,20 +101,20 @@ export class FolkRope extends FolkBaseConnection implements AnimationFrameContro
|
|||
let target: Point;
|
||||
|
||||
if (sourceRect instanceof DOMRectTransform) {
|
||||
source = sourceRect.toParentSpace({ x: sourceRect.width / 2, y: sourceRect.height });
|
||||
source = sourceRect.center;
|
||||
} else {
|
||||
source = {
|
||||
x: sourceRect.x + sourceRect.width / 2,
|
||||
y: sourceRect.y + sourceRect.height,
|
||||
y: sourceRect.y + sourceRect.height / 2,
|
||||
};
|
||||
}
|
||||
|
||||
if (targetRect instanceof DOMRectTransform) {
|
||||
target = targetRect.toParentSpace({ x: targetRect.width / 2, y: targetRect.height });
|
||||
target = targetRect.center;
|
||||
} else {
|
||||
target = {
|
||||
x: targetRect.x + targetRect.width / 2,
|
||||
y: targetRect.y + targetRect.height,
|
||||
y: targetRect.y + targetRect.height / 2,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -173,10 +171,7 @@ export class FolkRope extends FolkBaseConnection implements AnimationFrameContro
|
|||
|
||||
for (let i = 0; i < pointsLen; i++) {
|
||||
const percentage = i / (pointsLen - 1);
|
||||
const pos = {
|
||||
x: lerp(start.x, end.x, percentage),
|
||||
y: lerp(start.y, end.y, percentage),
|
||||
};
|
||||
const pos = Vector.lerp(start, end, percentage);
|
||||
|
||||
points.push({
|
||||
pos,
|
||||
|
|
@ -226,19 +221,28 @@ export class FolkRope extends FolkBaseConnection implements AnimationFrameContro
|
|||
if (point.prev) applyConstraint(point, point.prev);
|
||||
}
|
||||
|
||||
cut(index = Math.floor(this.#points.length / 2)) {
|
||||
if (index < 0 || index >= this.#points.length - 1) return;
|
||||
cut(atPercentage = 0.5) {
|
||||
const index = this.#getPointIndexAt(atPercentage);
|
||||
|
||||
this.#points[index].next = null;
|
||||
this.#points[index + 1].prev = null;
|
||||
}
|
||||
|
||||
mend(index = Math.floor(this.#points.length / 2)) {
|
||||
if (index < 0 || index >= this.#points.length - 1) return;
|
||||
mend(atPercentage = 0.5) {
|
||||
const index = this.#getPointIndexAt(atPercentage);
|
||||
|
||||
this.#points[index].next = this.#points[index + 1];
|
||||
this.#points[index + 1].prev = this.#points[index];
|
||||
}
|
||||
|
||||
getPointAt(percentage: number) {
|
||||
return this.#points[this.#getPointIndexAt(percentage)];
|
||||
}
|
||||
|
||||
#getPointIndexAt(percentage: number) {
|
||||
const clamped = Math.min(Math.max(percentage, 0), 1);
|
||||
return Math.floor(this.#points.length * clamped);
|
||||
}
|
||||
}
|
||||
|
||||
function applyConstraint(p1: RopePoint, p2: RopePoint) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue