1
0
mirror of https://github.com/Pomax/BezierInfo-2.git synced 2025-08-27 18:20:24 +02:00

much better sketch resetting using DOM rebuild

This commit is contained in:
Pomax
2020-09-16 22:43:16 -07:00
parent 4861c3a63b
commit ab4dd877ee
8 changed files with 292 additions and 194 deletions

View File

@@ -1,5 +1,4 @@
import { enrich } from "../lib/enrich.js";
import { create } from "../lib/create.js";
import { Bezier } from "./types/bezier.js";
import { BSpline } from "./types/bspline.js";
import { Vector } from "./types/vector.js";
@@ -7,6 +6,7 @@ import { Matrix } from "./types/matrix.js";
import { Shape } from "./util/shape.js";
import binomial from "./util/binomial.js";
import { BaseAPI } from "./base-api.js";
import impartSliderLogic from "./impart-slider-logic.js";
const MOUSE_PRECISION_ZONE = 5;
const TOUCH_PRECISION_ZONE = 30;
@@ -182,154 +182,6 @@ class GraphicsAPI extends BaseAPI {
this.line(0, 0, 0, this.height);
}
/**
* Dynamically add a slider
*/
addSlider(classes, propname, min, max, step, value, transform) {
if (this.element) {
let slider = create(`input`);
slider.type = `range`;
slider.min = min;
slider.max = max;
slider.step = step;
slider.setAttribute(`value`, value);
slider.setAttribute(`class`, classes);
this.element.append(slider);
this.setSlider(slider, propname, value, transform);
}
}
/**
* Update a slider with new min/max/step parameters, and value.
*
* @param {*} propname
* @param {*} min
* @param {*} max
* @param {*} step
* @param {*} value
*/
updateSlider(propname, min, max, step, value) {
let slider = this._sliders[propname];
if (!slider) {
throw new Error(`this.${propname} has no associated slider.`);
}
slider.setAttribute(`min`, min);
slider.setAttribute(`max`, max);
slider.setAttribute(`step`, step);
slider.setAttribute(`value`, value);
slider.updateProperty(value);
}
/**
* Set up a slider to control a named, numerical property in the sketch.
*
* @param {String} local query selector for the type=range element.
* @param {String} propname the name of the property to control.
* @param {float} initial the initial value for this property.
* @param {boolean} redraw whether or not to redraw after updating the value from the slider.
*/
setSlider(qs, propname, initial, transform) {
if (propname !== false && typeof this[propname] !== `undefined`) {
throw new Error(`this.${propname} already exists: cannot bind slider.`);
}
this._sliders = this._sliders || {};
let propLabel = propname.replace(`!`, ``);
propname = propLabel === propname ? propname : false;
let slider = typeof qs === `string` ? this.find(qs) : qs;
// create a slider row in the table of sliders
let ui = (() => {
if (!this.element) {
return { update: (v) => {} };
}
let table = find(`table.slider-wrapper`);
if (!table) {
table = slider.parentNode.querySelector(`table.slider-wrapper`);
if (!table) {
table = create(`table`);
table.classList.add(`slider-wrapper`);
slider.parentNode.replaceChild(table, slider);
}
}
let tr = create(`tr`);
let td = create(`td`);
let label = create(`label`);
label.classList.add(`slider-label`);
label.innerHTML = propLabel;
td.append(label);
tr.append(td);
td = create(`td`);
slider.classList.add(`slider`);
this._sliders[propname] = slider;
td.append(slider);
tr.append(td);
td = create(`td`);
let valueField = create(`label`);
valueField.classList.add(`slider-value`);
valueField.textContent;
td.append(valueField);
tr.append(td);
table.append(tr);
return { update: (v) => (valueField.textContent = v) };
})();
if (!slider) {
console.warn(`Warning: no slider found for query selector "${qs}"`);
if (propname) this[propname] = initial;
return undefined;
}
let step = slider.getAttribute(`step`) || "1";
let res = !step.includes(`.`)
? 0
: step.substring(step.indexOf(`.`) + 1).length;
slider.updateProperty = (evt) => {
let value = parseFloat(slider.value);
ui.update(value.toFixed(res));
try {
let checked = transform ? transform(value) ?? value : value;
if (propname) this[propname] = checked;
} catch (e) {
if (evt instanceof Event) {
evt.preventDefault();
evt.stopPropagation();
}
ui.update(e.value.toFixed(res));
slider.value = e.value;
slider.setAttribute(`value`, e.value);
}
if (!this.redrawing) this.redraw();
};
slider.value = initial;
slider.updateProperty({ target: { value: initial } });
slider.listen(`input`, (evt) => slider.updateProperty(evt));
return slider;
}
/**
* remove all sliders from this element
*/
removeSliders() {
this.findAll(`.slider-wrapper`).forEach((s) => {
s.parentNode.removeChild(s);
});
}
/**
* Convert the canvas to an image
*/
@@ -936,4 +788,12 @@ class GraphicsAPI extends BaseAPI {
}
}
/**
* Multiple inheritance is probably a good thing to not have in JS,
* but traits would have been nice, because a 1000+ LoC class is kind
* of silly, so to keep things easy to maintain, you end up writing
* custom trait-adding functions like this...
*/
impartSliderLogic(GraphicsAPI);
export { GraphicsAPI, Bezier, BSpline, Vector, Matrix, Shape };

View File

@@ -0,0 +1,172 @@
import { create } from "../lib/create.js";
export default function impartSliderLogic(GraphicsAPI) {
/**
* Dynamically add a slider
*/
GraphicsAPI.prototype.addSlider = function addSlider(
classes,
propname,
min,
max,
step,
value,
transform
) {
if (this.element) {
let slider = create(`input`);
slider.type = `range`;
slider.min = min;
slider.max = max;
slider.step = step;
slider.setAttribute(`value`, value);
slider.setAttribute(`class`, classes);
this.element.append(slider);
this.setSlider(slider, propname, value, transform);
}
};
/**
* Update a slider with new min/max/step parameters, and value.
*
* @param {*} propname
* @param {*} min
* @param {*} max
* @param {*} step
* @param {*} value
*/
GraphicsAPI.prototype.updateSlider = function updateSlider(
propname,
min,
max,
step,
value
) {
let slider = this._sliders[propname];
if (!slider) {
throw new Error(`this.${propname} has no associated slider.`);
}
slider.setAttribute(`min`, min);
slider.setAttribute(`max`, max);
slider.setAttribute(`step`, step);
slider.setAttribute(`value`, value);
slider.updateProperty(value);
};
/**
* Set up a slider to control a named, numerical property in the sketch.
*
* @param {String} local query selector for the type=range element.
* @param {String} propname the name of the property to control.
* @param {float} initial the initial value for this property.
* @param {boolean} redraw whether or not to redraw after updating the value from the slider.
*/
GraphicsAPI.prototype.setSlider = function setSlider(
qs,
propname,
initial,
transform
) {
if (propname !== false && typeof this[propname] !== `undefined`) {
throw new Error(`this.${propname} already exists: cannot bind slider.`);
}
this._sliders = this._sliders || {};
let propLabel = propname.replace(`!`, ``);
propname = propLabel === propname ? propname : false;
let slider = typeof qs === `string` ? this.find(qs) : qs;
// create a slider row in the table of sliders
let ui = (() => {
if (!this.element) {
return { update: (v) => {} };
}
let table = find(`table.slider-wrapper`);
if (!table) {
table = slider.parentNode.querySelector(`table.slider-wrapper`);
if (!table) {
table = create(`table`);
table.classList.add(`slider-wrapper`);
slider.parentNode.replaceChild(table, slider);
}
}
let tr = create(`tr`);
let td = create(`td`);
let label = create(`label`);
label.classList.add(`slider-label`);
label.innerHTML = propLabel;
td.append(label);
tr.append(td);
td = create(`td`);
slider.classList.add(`slider`);
this._sliders[propname] = slider;
td.append(slider);
tr.append(td);
td = create(`td`);
let valueField = create(`label`);
valueField.classList.add(`slider-value`);
valueField.textContent;
td.append(valueField);
tr.append(td);
table.append(tr);
return { update: (v) => (valueField.textContent = v) };
})();
if (!slider) {
console.warn(`Warning: no slider found for query selector "${qs}"`);
if (propname) this[propname] = initial;
return undefined;
}
let step = slider.getAttribute(`step`) || "1";
let res = !step.includes(`.`)
? 0
: step.substring(step.indexOf(`.`) + 1).length;
slider.updateProperty = (evt) => {
let value = parseFloat(slider.value);
ui.update(value.toFixed(res));
try {
let checked = transform ? transform(value) ?? value : value;
if (propname) this[propname] = checked;
} catch (e) {
if (evt instanceof Event) {
evt.preventDefault();
evt.stopPropagation();
}
ui.update(e.value.toFixed(res));
slider.value = e.value;
slider.setAttribute(`value`, e.value);
}
if (!this.redrawing) this.redraw();
};
slider.value = initial;
slider.updateProperty({ target: { value: initial } });
slider.listen(`input`, (evt) => slider.updateProperty(evt));
return slider;
};
/**
* remove all sliders from this element
*/
GraphicsAPI.prototype.removeSliders = function removeSliders() {
this.findAll(`.slider-wrapper`).forEach((s) => {
s.parentNode.removeChild(s);
});
};
return GraphicsAPI;
}

View File

@@ -256,7 +256,15 @@ class GraphicsElement extends CustomElement {
* Reload this graphics element the brute force way.
*/
async reset() {
this.outerHTML = this.originalHTML;
const parent = this.parentNode;
const offDOM = document.createElement(`div`);
offDOM.innerHTML = this.originalHTML;
const newElement = offDOM.querySelector(`graphics-element`);
offDOM.style.display = `none`;
document.body.append(offDOM);
newElement.addEventListener(`loaded`, () =>
parent.replaceChild(newElement, this)
);
}
/**
@@ -284,6 +292,7 @@ class GraphicsElement extends CustomElement {
// If we get here, there were no source code errors: undo the scheduled error print.
clearTimeout(this.errorPrintTimeout);
this.render();
this.dispatchEvent(new CustomEvent(`loaded`));
}
/**

View File

@@ -10,17 +10,15 @@
* grabs your document.referrer value, which (unless Do
* Not Track is enabled) will contain the location of
* the page you were on before you clicked a link to this
* page, and GETs that to my logger. That GET operation
* comes from your computer, so will have your IP as part
* of the HTTP headers.
* page, and GETs that to my logger.
*
* And that's all I really care about, because I want to
* know how many people visit this page, and roughly where
* they're from (gasp! IPs can be turned into rough
* geographical location O_O).
* If you want to know what gets logged, have a look
* at the ./src/logger/logger.php file on github.
*
* If you want to know what logger.php looks like, hit up
* github. It's in referrer/logger.php
* If that's too much effort:
* - the request URL
* - the referrer URL (if there is one)
* - the user agent
*
*/
(function referrer(l) {
@@ -32,20 +30,7 @@
if (loc.indexOf("localhost") !== -1) return;
// right, continue
var url = "https://pomax.nihongoresources.com/pages/bezierinfo/logger.php";
var xhr = new XMLHttpRequest();
xhr.open(
"GET",
url +
"?" +
"referrer=" +
encodeURIComponent(document.referrer) +
"&for=" +
page,
true
);
try {
xhr.send(null);
} catch (e) {
/* you don't care about this error, and I can't see it, so why would we do anything with it? */
}
fetch(`${url}?referrer=${encodeURIComponent(document.referrer)}&for=${page}`)
.then((_) => {})
.catch((_) => {});
})(window.location.toString());