create mod and rename original
This commit is contained in:
parent
f220413017
commit
1a861f81ce
839
mods/zoom.js
839
mods/zoom.js
|
|
@ -1,322 +1,567 @@
|
||||||
const zoom_levels = [
|
// zoom.js
|
||||||
0.5,
|
"use strict";
|
||||||
1,
|
(() => {
|
||||||
2,
|
// src/custom_setting_types.ts
|
||||||
3,
|
var def_classes = () => {
|
||||||
6,
|
class Numlist2 extends Setting {
|
||||||
12
|
step;
|
||||||
]
|
input_container = null;
|
||||||
window.zoom_data_div = null
|
push_btn = null;
|
||||||
window.zoom_level = 1
|
pop_btn = null;
|
||||||
window.zoom_panning = [0,0]
|
constructor(name, storage_name, default_values = [], desc = "", step = 1, custom_validator = () => true, disabled = false) {
|
||||||
|
super(
|
||||||
|
name,
|
||||||
|
storage_name,
|
||||||
|
[5, 0],
|
||||||
|
disabled,
|
||||||
|
default_values ?? [],
|
||||||
|
desc,
|
||||||
|
custom_validator
|
||||||
|
);
|
||||||
|
this.step = step;
|
||||||
|
}
|
||||||
|
#new_input(value, i) {
|
||||||
|
const elem = document.createElement("input");
|
||||||
|
elem.type = "number";
|
||||||
|
elem.value = value.toString();
|
||||||
|
elem.step = this.step.toString();
|
||||||
|
elem.onchange = (ev) => {
|
||||||
|
const parsed = Number.parseFloat(ev.target.value);
|
||||||
|
if (!Number.isNaN(parsed)) {
|
||||||
|
this.value[i] = parsed;
|
||||||
|
this.set(this.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
#push_pop_btns() {
|
||||||
|
this.push_btn = document.createElement("button");
|
||||||
|
this.push_btn.style.color = "#0F0";
|
||||||
|
this.push_btn.innerText = "+";
|
||||||
|
this.pop_btn = document.createElement("button");
|
||||||
|
this.pop_btn.style.color = "#F00";
|
||||||
|
this.pop_btn.innerText = "-";
|
||||||
|
this.push_btn.onclick = () => {
|
||||||
|
this.value.push(1);
|
||||||
|
this.input_container.append(this.#new_input(1, this.value.length));
|
||||||
|
};
|
||||||
|
this.pop_btn.onclick = () => {
|
||||||
|
this.value.pop();
|
||||||
|
if (this.input_container.lastChild) {
|
||||||
|
this.input_container.removeChild(this.input_container.lastChild);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return [this.push_btn, this.pop_btn];
|
||||||
|
}
|
||||||
|
disable() {
|
||||||
|
this.push_btn?.setAttribute("disabled", "true");
|
||||||
|
this.pop_btn?.setAttribute("disabled", "true");
|
||||||
|
}
|
||||||
|
enable() {
|
||||||
|
this.push_btn?.removeAttribute("disabled");
|
||||||
|
this.pop_btn?.removeAttribute("disabled");
|
||||||
|
}
|
||||||
|
build() {
|
||||||
|
const value = this.get();
|
||||||
|
const container = document.createElement("span");
|
||||||
|
container.classList.add("setting-span", "zm_nml_setting");
|
||||||
|
const l_container = document.createElement("span");
|
||||||
|
const label = document.createElement("span");
|
||||||
|
label.innerText = this.name;
|
||||||
|
const btn_container = document.createElement("span");
|
||||||
|
btn_container.classList.add("zm_nml_btn_container");
|
||||||
|
btn_container.append(...this.#push_pop_btns());
|
||||||
|
l_container.append(label, document.createElement("br"), btn_container);
|
||||||
|
this.input_container = document.createElement("span");
|
||||||
|
this.input_container.classList.add("zm_nml_icontainer");
|
||||||
|
const elems = [];
|
||||||
|
value.forEach((x, i) => {
|
||||||
|
elems.push(this.#new_input(x, i));
|
||||||
|
});
|
||||||
|
this.input_container.append(...elems);
|
||||||
|
container.append(l_container, this.input_container);
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class MultiSetting extends Setting {
|
||||||
|
settings;
|
||||||
|
elements = [];
|
||||||
|
multi_input_name;
|
||||||
|
rows = [];
|
||||||
|
constructor(name, storage_name, extra_opts, ...settings) {
|
||||||
|
super(
|
||||||
|
name,
|
||||||
|
storage_name,
|
||||||
|
[255],
|
||||||
|
extra_opts.disabled,
|
||||||
|
extra_opts.default_value ?? 0,
|
||||||
|
extra_opts.desc,
|
||||||
|
void 0
|
||||||
|
);
|
||||||
|
this.settings = settings;
|
||||||
|
this.multi_input_name = crypto.randomUUID();
|
||||||
|
}
|
||||||
|
build() {
|
||||||
|
const container = document.createElement("span");
|
||||||
|
this.settings.forEach((setting, i) => {
|
||||||
|
const row_container = document.createElement("div");
|
||||||
|
row_container.classList.add("zm_ms_row");
|
||||||
|
this.rows.push(row_container);
|
||||||
|
const select_btn = document.createElement("button");
|
||||||
|
select_btn.classList.add("zm_ms_selbtn");
|
||||||
|
select_btn.innerText = "#";
|
||||||
|
const built_item = setting.build();
|
||||||
|
built_item.classList.add("zm_ms_item");
|
||||||
|
built_item.dataset.index = i.toString();
|
||||||
|
row_container.dataset.current = i == this.value ? "true" : "false";
|
||||||
|
select_btn.onclick = () => {
|
||||||
|
this.set(i);
|
||||||
|
setting.enable();
|
||||||
|
for (const setting2 of this.settings) setting2.disable();
|
||||||
|
for (const row of this.rows) {
|
||||||
|
row.dataset.current = "false";
|
||||||
|
row.querySelectorAll(".zm_ms_item input").forEach((x) => x.setAttribute("disabled", "true"));
|
||||||
|
}
|
||||||
|
built_item.querySelectorAll("input").forEach((x) => x.removeAttribute("disabled"));
|
||||||
|
row_container.dataset.current = "true";
|
||||||
|
};
|
||||||
|
row_container.append(select_btn, built_item);
|
||||||
|
container.appendChild(row_container);
|
||||||
|
});
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class SettingGroup extends Setting {
|
||||||
|
settings;
|
||||||
|
constructor(settings) {
|
||||||
|
super(
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
[2763],
|
||||||
|
false
|
||||||
|
);
|
||||||
|
this.settings = settings;
|
||||||
|
}
|
||||||
|
enable() {
|
||||||
|
for (const x of Object.values(this.settings)) {
|
||||||
|
x.enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
disable() {
|
||||||
|
for (const x of Object.values(this.settings)) {
|
||||||
|
x.disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
build() {
|
||||||
|
const container = document.createElement("div");
|
||||||
|
for (const x of Object.values(this.settings)) {
|
||||||
|
container.appendChild(x.build());
|
||||||
|
}
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
get() {
|
||||||
|
return this.settings;
|
||||||
|
}
|
||||||
|
// Override these so the defaults don't do anything
|
||||||
|
set() {
|
||||||
|
}
|
||||||
|
update() {
|
||||||
|
}
|
||||||
|
onUpdate() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
Numlist: Numlist2,
|
||||||
|
MultiSetting,
|
||||||
|
SettingGroup
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
let colour_setting;
|
// src/custom_settings.ts
|
||||||
|
var CustomSettingsManager = class {
|
||||||
dependOn("betterSettings.js", () => {
|
canvas_bkg;
|
||||||
|
zoom;
|
||||||
|
unl_zoom;
|
||||||
|
show_floater;
|
||||||
|
fpan_speed;
|
||||||
|
cpan_speed;
|
||||||
|
upan_speed;
|
||||||
|
constructor(on_edit) {
|
||||||
|
const { Numlist: Numlist2, MultiSetting, SettingGroup } = def_classes();
|
||||||
const settings_tab = new SettingsTab("zoom.js");
|
const settings_tab = new SettingsTab("zoom.js");
|
||||||
colour_setting = new Setting(
|
const validator = () => {
|
||||||
|
on_edit.cb(this);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
this.canvas_bkg = new Setting(
|
||||||
"Canvas background",
|
"Canvas background",
|
||||||
"canvas_bkg",
|
"canvas_bkg",
|
||||||
settingType.COLOR,
|
settingType.COLOR,
|
||||||
false,
|
false,
|
||||||
defaultValue="#252525"
|
"#252525",
|
||||||
|
"The colour for the area around the canvas",
|
||||||
|
validator
|
||||||
);
|
);
|
||||||
|
this.cpan_speed = new Setting(
|
||||||
settings_tab.registerSettings(undefined, colour_setting)
|
"Coarse pan speed",
|
||||||
settingsManager.registerTab(settings_tab)
|
"cpan_speed",
|
||||||
})
|
settingType.NUMBER,
|
||||||
|
false,
|
||||||
function handle_zoom(direction){
|
10,
|
||||||
switch (direction){
|
"The default pan speed",
|
||||||
case "in":
|
validator
|
||||||
if (!(zoom_level+1 in zoom_levels)) { break; }
|
);
|
||||||
window.zoom_level += 1
|
this.fpan_speed = new Setting(
|
||||||
break;
|
"Fine pan speed",
|
||||||
case "out":
|
"fpan_speed",
|
||||||
if (!(zoom_level-1 in zoom_levels)) { break; }
|
settingType.NUMBER,
|
||||||
window.zoom_level -= 1
|
false,
|
||||||
break;
|
3,
|
||||||
}
|
"The pan speed when holding shift (F in the floater)",
|
||||||
rescale()
|
validator
|
||||||
}
|
);
|
||||||
|
this.upan_speed = new Setting(
|
||||||
function handle_pan(direction, speed){
|
"Ultrafine pan speed",
|
||||||
switch (direction){
|
"upan_speed",
|
||||||
case "right":
|
settingType.NUMBER,
|
||||||
zoom_panning[0] -= speed
|
false,
|
||||||
break;
|
1,
|
||||||
case "left":
|
"The pan speed when holding alt (U in the floater)",
|
||||||
zoom_panning[0] += speed
|
validator
|
||||||
break;
|
);
|
||||||
case "up":
|
this.show_floater = new Setting(
|
||||||
zoom_panning[1] += speed
|
"Show floater",
|
||||||
break;
|
"show_floater",
|
||||||
case "down":
|
settingType.BOOLEAN,
|
||||||
zoom_panning[1] -= speed
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
rescale()
|
|
||||||
}
|
|
||||||
|
|
||||||
function gen_button(row, col, html, click, nopos, id){
|
|
||||||
const elem = document.createElement("button")
|
|
||||||
|
|
||||||
|
|
||||||
if (!nopos){
|
|
||||||
elem.style.gridColumn = row
|
|
||||||
elem.style.gridRow = col
|
|
||||||
}
|
|
||||||
if (id) { elem.id = id }
|
|
||||||
|
|
||||||
// Table for the data-pos to assign (row major). If null, don't add.
|
|
||||||
const data_pos_map = [
|
|
||||||
["tl", null, "tr"],
|
|
||||||
[null, null, null],
|
|
||||||
["bl", null, "br"]
|
|
||||||
]
|
|
||||||
|
|
||||||
elem.innerHTML = html
|
|
||||||
elem.onclick = click
|
|
||||||
|
|
||||||
if (data_pos_map[row-1][col-1] !== null) {
|
|
||||||
elem.dataset.pos = data_pos_map[row-1][col-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
return elem
|
|
||||||
}
|
|
||||||
|
|
||||||
function add_css(){
|
|
||||||
const CSS = `
|
|
||||||
#zm_data_div { margin-bottom: 10px }
|
|
||||||
#canvasDiv { overflow: hidden; background-color: var(--opac-85) }
|
|
||||||
|
|
||||||
@media(pointer=coarse){
|
|
||||||
#zm_floater_container#zm_floater_container {
|
|
||||||
width: 40%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
#zm_floater_container:has(#zm_collapse[data-collapsed="true"]){
|
|
||||||
width: calc(40% / 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media(pointer:coarse) and (orientation:landscape){
|
|
||||||
#zm_floater_container#zm_floater_container {
|
|
||||||
width: auto;
|
|
||||||
top: 5px;
|
|
||||||
}
|
|
||||||
#zm_floater_container:has(#zm_collapse[data-collapsed="true"]){
|
|
||||||
width: calc(40% / 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#colorSelector { z-index: 1; right: 5px }
|
|
||||||
#zm_floater_container {
|
|
||||||
position: absolute;
|
|
||||||
display: grid;
|
|
||||||
|
|
||||||
right: 5px;
|
|
||||||
bottom: 5px;
|
|
||||||
height: 100px;
|
|
||||||
aspect-ratio: 1;
|
|
||||||
|
|
||||||
max-width: 200px;
|
|
||||||
max-height: 200px;
|
|
||||||
|
|
||||||
border: 2px solid white;
|
|
||||||
background-color: black;
|
|
||||||
font-size: 120%;
|
|
||||||
|
|
||||||
button { text-align: center; border: 0px solid white }
|
|
||||||
|
|
||||||
button:where([data-pos="tl"]) { border-width: 0px 2px 2px 0px };
|
|
||||||
button:where([data-pos="tr"]) { border-width: 2px 2px 0px 0px };
|
|
||||||
button:where([data-pos="bl"]) { border-width: 0px 0px 2px 2px };
|
|
||||||
button:where([data-pos="br"]) { border-width: 2px 0px 0px 2px };
|
|
||||||
}
|
|
||||||
#zm_floater_container:has(#zm_collapse[data-collapsed="true"]) {
|
|
||||||
height: 50px;
|
|
||||||
|
|
||||||
button:not(#zm_collapse) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#canvasDiv:has(#colorSelector[style *= "block"]) #zm_floater_container {
|
|
||||||
bottom: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zm_corner { border: 2px solid white; }
|
|
||||||
|
|
||||||
#zm_collapse {
|
|
||||||
grid-row: 3;
|
|
||||||
grid-column: 3;
|
|
||||||
}
|
|
||||||
#zm_collapse[data-collapsed="true"] {
|
|
||||||
grid-row: 1;
|
|
||||||
grid-column: 1;
|
|
||||||
border-width: 0px;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const style_div = document.createElement("style")
|
|
||||||
style_div.innerHTML = CSS
|
|
||||||
|
|
||||||
document.head.appendChild(style_div)
|
|
||||||
}
|
|
||||||
|
|
||||||
function add_zoom_floaters(){
|
|
||||||
const container = document.createElement("div")
|
|
||||||
container.id = "zm_floater_container"
|
|
||||||
|
|
||||||
// Pan mode selector (C: Coarse F: Fine)
|
|
||||||
const pan_mode_sel = gen_button(
|
|
||||||
1,3, "C",
|
|
||||||
(evt) => {
|
|
||||||
evt.target.dataset.mode = evt.target.dataset.mode == "F" ? "C" : "F"
|
|
||||||
evt.target.innerText = evt.target.dataset.mode
|
|
||||||
},
|
|
||||||
false,
|
false,
|
||||||
"zm_panmode_sel"
|
|
||||||
)
|
|
||||||
|
|
||||||
const speed = () =>
|
|
||||||
(window.zoom_level > 3 ? 5 : 10) * // More granular at higher zoom levels
|
|
||||||
(pan_mode_sel.dataset.mode == "F" ? 0.25 : 1) // Increase granularity in fine mode
|
|
||||||
|
|
||||||
container.append(
|
|
||||||
// Direction buttons
|
|
||||||
gen_button(2,1, "↑", () => handle_pan("up" ,speed())),
|
|
||||||
gen_button(1,2, "←", () => handle_pan("left" ,speed())),
|
|
||||||
gen_button(3,2, "→", () => handle_pan("right" ,speed())),
|
|
||||||
gen_button(2,3, "↓", () => handle_pan("down" ,speed())),
|
|
||||||
|
|
||||||
// Zoom buttons
|
|
||||||
gen_button(1,1, "+", () => handle_zoom("in")),
|
|
||||||
gen_button(3,1, "-", () => handle_zoom("out")),
|
|
||||||
|
|
||||||
// Collapse button
|
|
||||||
gen_button(
|
|
||||||
3,3, "#",
|
|
||||||
(evt) => {
|
|
||||||
evt.target.dataset.collapsed = evt.target.dataset.collapsed == "true"
|
|
||||||
? "false"
|
|
||||||
: "true"
|
|
||||||
},
|
|
||||||
true,
|
true,
|
||||||
"zm_collapse"
|
"Whether to show the floater or not",
|
||||||
|
validator
|
||||||
|
);
|
||||||
|
const zoom_levels = new Numlist2(
|
||||||
|
"Zoom levels",
|
||||||
|
"zoom_levels",
|
||||||
|
[0.5, 1, 2, 3, 6, 12],
|
||||||
|
"Zoom levels",
|
||||||
|
1,
|
||||||
|
validator
|
||||||
|
);
|
||||||
|
this.unl_zoom = new SettingGroup({
|
||||||
|
speed: new Setting(
|
||||||
|
"Zoom speed",
|
||||||
|
"unl_zoom_speed",
|
||||||
|
settingType.NUMBER,
|
||||||
|
false,
|
||||||
|
2,
|
||||||
|
"The zoom magnitude (as the multiplier to the zoom level every time zoom is used)",
|
||||||
|
validator
|
||||||
),
|
),
|
||||||
pan_mode_sel
|
min: new Setting(
|
||||||
|
"Zoom limit (min)",
|
||||||
|
"unl_zlim_min",
|
||||||
|
settingType.NUMBER,
|
||||||
|
false,
|
||||||
|
0.25,
|
||||||
|
"The lower zoom limit (reducing may lead to rounding error coming back from very low levels)",
|
||||||
|
validator
|
||||||
|
),
|
||||||
|
max: new Setting(
|
||||||
|
"Zoom limit (max)",
|
||||||
|
"unl_zlim_max",
|
||||||
|
settingType.NUMBER,
|
||||||
|
false,
|
||||||
|
25,
|
||||||
|
"The upper zoom limit (reducing may lead to rounding error coming back from very high levels)",
|
||||||
|
validator
|
||||||
)
|
)
|
||||||
|
});
|
||||||
const canvas_div = document.getElementById("canvasDiv")
|
this.zoom = new MultiSetting(
|
||||||
canvas_div.style.backgroundColor = colour_setting?.value ?? "#252525"
|
"Zoom mode",
|
||||||
canvas_div.appendChild(container)
|
"zoom_mode",
|
||||||
|
{},
|
||||||
|
zoom_levels,
|
||||||
|
this.unl_zoom
|
||||||
|
);
|
||||||
|
settings_tab.registerSettings(
|
||||||
|
void 0,
|
||||||
|
this.canvas_bkg,
|
||||||
|
this.show_floater,
|
||||||
|
this.zoom
|
||||||
|
);
|
||||||
|
settings_tab.registerSettings(
|
||||||
|
"Panning",
|
||||||
|
this.cpan_speed,
|
||||||
|
this.fpan_speed,
|
||||||
|
this.upan_speed
|
||||||
|
);
|
||||||
|
settingsManager.registerTab(settings_tab);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function rescale(){
|
// src/handler.ts
|
||||||
log_info()
|
var Handler = class {
|
||||||
|
settings;
|
||||||
const scale = zoom_levels[zoom_level]
|
patcher;
|
||||||
const x = zoom_panning[0] * (pixelSize * scale)
|
zoom_panning = [0, 0];
|
||||||
const y = zoom_panning[1] * (pixelSize * scale)
|
zoom_level;
|
||||||
|
constructor(settings, patcher) {
|
||||||
gameCanvas.style.transform = `translate(${x}px, ${y}px) translateX(-50%) scale(${scale})`
|
this.settings = settings;
|
||||||
}
|
this.patcher = patcher;
|
||||||
|
this.zoom_level = 1;
|
||||||
function log_info(){
|
this.patch_keybinds();
|
||||||
// Values are negated to make them more intuitive
|
this.patch_floater();
|
||||||
const x_pan = (-zoom_panning[0]).toString().padEnd(4)
|
window.getMousePos = (canvas2, evt) => {
|
||||||
const y_pan = (-zoom_panning[1]).toString().padEnd(4)
|
|
||||||
|
|
||||||
if (zoom_data_div === null){ return; }
|
|
||||||
|
|
||||||
zoom_data_div.innerText = ""
|
|
||||||
zoom_data_div.innerText += `Scale: ${zoom_levels[zoom_level]}x\n`
|
|
||||||
zoom_data_div.innerText += `Pan : ${x_pan}, ${y_pan}`
|
|
||||||
}
|
|
||||||
|
|
||||||
function patch_keybinds(){
|
|
||||||
// Be more granular at higher zoom levels
|
|
||||||
const speed_a = () => zoom_level > 3 ? 5 : 10
|
|
||||||
const speed_b = () => zoom_level > 3 ? 10 : 20
|
|
||||||
|
|
||||||
keybinds["9"] = () => handle_zoom("in")
|
|
||||||
keybinds["0"] = () => handle_zoom("out")
|
|
||||||
|
|
||||||
keybinds["w"] = () => handle_pan("up", speed_a())
|
|
||||||
keybinds["a"] = () => handle_pan("left", speed_a())
|
|
||||||
keybinds["s"] = () => handle_pan("down", speed_a())
|
|
||||||
keybinds["d"] = () => handle_pan("right", speed_a())
|
|
||||||
|
|
||||||
keybinds["W"] = () => handle_pan("up", speed_b())
|
|
||||||
keybinds["A"] = () => handle_pan("left", speed_b())
|
|
||||||
keybinds["S"] = () => handle_pan("down", speed_b())
|
|
||||||
keybinds["D"] = () => handle_pan("right", speed_b())
|
|
||||||
}
|
|
||||||
|
|
||||||
function patch_ui(){
|
|
||||||
add_css()
|
|
||||||
add_zoom_floaters()
|
|
||||||
|
|
||||||
zoom_data_div = document.createElement("div")
|
|
||||||
zoom_data_div.id = "zm_data_div"
|
|
||||||
document.getElementById("logDiv").prepend(zoom_data_div)
|
|
||||||
|
|
||||||
const controls_table = document.getElementById("controlsTable").lastElementChild
|
|
||||||
controls_table.insertAdjacentHTML("beforeBegin",`
|
|
||||||
<tr>
|
|
||||||
<td>Zoom in/out</td>
|
|
||||||
<td>
|
|
||||||
<kbd>9</kbd>/
|
|
||||||
<kbd>0</kbd>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Pan</td>
|
|
||||||
<td>
|
|
||||||
<kbd>W</kbd>
|
|
||||||
<kbd>A</kbd>
|
|
||||||
<kbd>S</kbd>
|
|
||||||
<kbd>D</kbd>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Pan (fast)</td>
|
|
||||||
<td>
|
|
||||||
<kbd>Shift</kbd> +
|
|
||||||
<kbd>W</kbd>
|
|
||||||
<kbd>A</kbd>
|
|
||||||
<kbd>S</kbd>
|
|
||||||
<kbd>D</kbd>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Redefine to give correct numbers when zoomed
|
|
||||||
window.getMousePos = (canvas, evt) => {
|
|
||||||
if (evt.touches) {
|
if (evt.touches) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
evt = evt.touches[0];
|
evt = evt.touches[0];
|
||||||
isMobile = true;
|
isMobile = true;
|
||||||
}
|
}
|
||||||
const rect = canvas.getBoundingClientRect();
|
const rect = canvas2.getBoundingClientRect();
|
||||||
|
const clx = evt.clientX;
|
||||||
|
const cly = evt.clientY;
|
||||||
|
let x = (clx - rect.left) / this.scale();
|
||||||
|
let y = (cly - rect.top) / this.scale();
|
||||||
|
x = Math.floor(x / canvas2.clientWidth * (width + 1));
|
||||||
|
y = Math.floor(y / canvas2.clientHeight * (height + 1));
|
||||||
|
return { x, y };
|
||||||
|
};
|
||||||
|
runAfterReset(() => {
|
||||||
|
this.zoom_level = 1;
|
||||||
|
this.zoom_panning = [0, 0];
|
||||||
|
this.rescale();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
handle_zoom(direction) {
|
||||||
|
if (this.settings.zoom.value == 0) {
|
||||||
|
switch (direction) {
|
||||||
|
case "in":
|
||||||
|
if (!(this.zoom_level + 1 in this.settings.zoom.settings[0].value)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.zoom_level += 1;
|
||||||
|
break;
|
||||||
|
case "out":
|
||||||
|
if (!(this.zoom_level - 1 in this.settings.zoom.settings[0].value)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.zoom_level -= 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const settings = this.settings.zoom.settings[1].settings;
|
||||||
|
const speed = settings.speed.value;
|
||||||
|
const min = settings.min.value;
|
||||||
|
const max = settings.max.value;
|
||||||
|
switch (direction) {
|
||||||
|
case "in":
|
||||||
|
if (this.zoom_level * speed > max) break;
|
||||||
|
this.zoom_level *= speed;
|
||||||
|
break;
|
||||||
|
case "out":
|
||||||
|
if (this.zoom_level / speed < min) break;
|
||||||
|
this.zoom_level /= speed;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.zoom_level = Number(this.zoom_level.toPrecision(3));
|
||||||
|
}
|
||||||
|
this.rescale();
|
||||||
|
}
|
||||||
|
handle_pan(direction, speed) {
|
||||||
|
switch (direction) {
|
||||||
|
case "right":
|
||||||
|
this.zoom_panning[0] -= speed;
|
||||||
|
break;
|
||||||
|
case "left":
|
||||||
|
this.zoom_panning[0] += speed;
|
||||||
|
break;
|
||||||
|
case "up":
|
||||||
|
this.zoom_panning[1] += speed;
|
||||||
|
break;
|
||||||
|
case "down":
|
||||||
|
this.zoom_panning[1] -= speed;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.rescale();
|
||||||
|
}
|
||||||
|
scale() {
|
||||||
|
return this.settings.zoom.value == 0 ? this.settings.zoom.settings[0].value[this.zoom_level] : this.zoom_level;
|
||||||
|
}
|
||||||
|
rescale() {
|
||||||
|
this.log_info();
|
||||||
|
const x = this.zoom_panning[0] * (pixelSize * this.scale());
|
||||||
|
const y = this.zoom_panning[1] * (pixelSize * this.scale());
|
||||||
|
canvas.style.transform = `translate(${x}px, ${y}px) translateX(-50%) scale(${this.scale()})`;
|
||||||
|
}
|
||||||
|
log_info() {
|
||||||
|
const x_pan = (-this.zoom_panning[0]).toString().padEnd(4);
|
||||||
|
const y_pan = (-this.zoom_panning[1]).toString().padEnd(4);
|
||||||
|
this.patcher.zoom_data_div.innerText = "";
|
||||||
|
this.patcher.zoom_data_div.innerText += `Scale: ${this.scale()}x
|
||||||
|
`;
|
||||||
|
this.patcher.zoom_data_div.innerText += `Pan : ${x_pan}, ${y_pan}`;
|
||||||
|
}
|
||||||
|
patch_keybinds() {
|
||||||
|
keybinds["9"] = () => this.handle_zoom("in");
|
||||||
|
keybinds["0"] = () => this.handle_zoom("out");
|
||||||
|
keybinds["w"] = (ev) => this.handle_pan("up", ev.altKey ? this.settings.upan_speed.value : this.settings.cpan_speed.value);
|
||||||
|
keybinds["a"] = (ev) => this.handle_pan("left", ev.altKey ? this.settings.upan_speed.value : this.settings.cpan_speed.value);
|
||||||
|
keybinds["s"] = (ev) => this.handle_pan("down", ev.altKey ? this.settings.upan_speed.value : this.settings.cpan_speed.value);
|
||||||
|
keybinds["d"] = (ev) => this.handle_pan("right", ev.altKey ? this.settings.upan_speed.value : this.settings.cpan_speed.value);
|
||||||
|
keybinds["W"] = () => this.handle_pan("up", this.settings.fpan_speed.value);
|
||||||
|
keybinds["A"] = () => this.handle_pan("left", this.settings.fpan_speed.value);
|
||||||
|
keybinds["S"] = () => this.handle_pan("down", this.settings.fpan_speed.value);
|
||||||
|
keybinds["D"] = () => this.handle_pan("right", this.settings.fpan_speed.value);
|
||||||
|
}
|
||||||
|
speed() {
|
||||||
|
switch (this.patcher.panmode_sel.innerText) {
|
||||||
|
case "C":
|
||||||
|
return this.settings.cpan_speed.value;
|
||||||
|
case "F":
|
||||||
|
return this.settings.fpan_speed.value;
|
||||||
|
case "U":
|
||||||
|
return this.settings.upan_speed.value;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
patch_floater() {
|
||||||
|
document.getElementById("zm_floater_u").onclick = () => this.handle_pan("up", this.speed());
|
||||||
|
document.getElementById("zm_floater_d").onclick = () => this.handle_pan("down", this.speed());
|
||||||
|
document.getElementById("zm_floater_l").onclick = () => this.handle_pan("left", this.speed());
|
||||||
|
document.getElementById("zm_floater_r").onclick = () => this.handle_pan("right", this.speed());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let x = (evt.clientX - rect.left) / zoom_levels[zoom_level];
|
// assets/ctrl_info.html
|
||||||
let y = (evt.clientY - rect.top) / zoom_levels[zoom_level];
|
var ctrl_info_default = "<tr>\r\n <td>Zoom in/out</td>\r\n <td>\r\n <kbd>9</kbd>/\r\n <kbd>0</kbd>\r\n </td>\r\n</tr>\r\n<tr>\r\n <td>Pan</td>\r\n <td>\r\n <kbd>W</kbd>\r\n <kbd>A</kbd>\r\n <kbd>S</kbd>\r\n <kbd>D</kbd>\r\n </td>\r\n</tr>\r\n<tr>\r\n <td>Pan (fast)</td>\r\n <td>\r\n <kbd>Shift</kbd> + \r\n <kbd>W</kbd>\r\n <kbd>A</kbd>\r\n <kbd>S</kbd>\r\n <kbd>D</kbd>\r\n </td>\r\n</tr>";
|
||||||
|
|
||||||
x = Math.floor((x / canvas.clientWidth) * (width+1));
|
// assets/floater.html
|
||||||
y = Math.floor((y / canvas.clientHeight) * (height+1));
|
var floater_default = '<div id="zm_floater_container">\r\n <button id="zm_floater_u" style="grid-area: 1 / 2;">↑</button>\r\n <button id="zm_floater_d" style="grid-area: 3 / 2;">↓</button>\r\n <button id="zm_floater_l" style="grid-area: 2 / 1;">←</button>\r\n <button id="zm_floater_r" style="grid-area: 2 / 3;">→</button>\r\n \r\n <button id="zm_floater_zi" data-pos="tl" style="grid-area: 1 / 1;">+</button>\r\n <button id="zm_floater_zo" data-pos="bl" style="grid-area: 1 / 3;">-</button>\r\n\r\n <button id="zm_collapse" data-pos="br">#</button>\r\n <button id="zm_panmode_sel" data-pos="tr" style="grid-area: 3 / 1;">C</button>\r\n</div>';
|
||||||
|
|
||||||
return {x:x, y:y};
|
// assets/main.css
|
||||||
|
var main_default = '#zm_data_div { margin-bottom: 10px }\r\n#canvasDiv { overflow: hidden; background-color: var(--opac-85) }\r\n\r\n@media(pointer=coarse){\r\n#zm_floater_container#zm_floater_container { \r\n width: 40%;\r\n height: auto;\r\n}\r\n#zm_floater_container:has(#zm_collapse[data-collapsed="true"]){\r\n width: calc(40% / 3);\r\n}\r\n}\r\n\r\n@media(pointer:coarse) and (orientation:landscape){\r\n#zm_floater_container#zm_floater_container {\r\n width: auto;\r\n top: 5px;\r\n}\r\n#zm_floater_container:has(#zm_collapse[data-collapsed="true"]){\r\n width: calc(40% / 3);\r\n}\r\n}\r\n\r\n#colorSelector { z-index: 1; right: 5px }\r\n#zm_floater_container {\r\nposition: absolute;\r\ndisplay: grid;\r\n\r\nright: 5px;\r\nbottom: 5px;\r\nheight: 100px;\r\naspect-ratio: 1;\r\n\r\nmax-width: 200px;\r\nmax-height: 200px;\r\n\r\nborder: 2px solid white;\r\nbackground-color: black;\r\nfont-size: 120%;\r\n\r\nbutton { text-align: center; border: 0px solid white }\r\n\r\nbutton:where([data-pos="tl"]) { border-width: 0px 2px 2px 0px };\r\nbutton:where([data-pos="tr"]) { border-width: 2px 2px 0px 0px };\r\nbutton:where([data-pos="bl"]) { border-width: 0px 0px 2px 2px };\r\nbutton:where([data-pos="br"]) { border-width: 2px 0px 0px 2px };\r\n}\r\n#zm_floater_container:has(#zm_collapse[data-collapsed="true"]) {\r\nheight: 50px;\r\n\r\nbutton:not(#zm_collapse) {\r\n display: none;\r\n}\r\n}\r\n#canvasDiv:has(#colorSelector[style *= "block"]) #zm_floater_container {\r\nbottom: 50px;\r\n}\r\n\r\n.zm_corner { border: 2px solid white; }\r\n\r\n#zm_collapse {\r\ngrid-row: 3;\r\ngrid-column: 3;\r\n}\r\n#zm_collapse[data-collapsed="true"] {\r\ngrid-row: 1;\r\ngrid-column: 1;\r\nborder-width: 0px;\r\n}';
|
||||||
|
|
||||||
|
// assets/nlist_spinner.png
|
||||||
|
var nlist_spinner_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAYCAYAAADH2bwQAAAAcklEQVQokcWSXQrAIAyDv8oOoPe/49wJzB7coIg/jA2WPhQ1TdOiATsThNmjJ8QV4XjdokIkRHqiEAF2NAgoSw8GlCuD3K3zYEzgFdSQBbA15LaYQN1i9lU+3y16CgDqjSl/MKBoMIk5DyNk46sf9SfhBITwI86iGhy9AAAAAElFTkSuQmCC";
|
||||||
|
|
||||||
|
// assets/numlist.css.ts
|
||||||
|
var CSS = `
|
||||||
|
#settingsMenu .zm_nml_btn_container button {
|
||||||
|
font-size: 2em;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
runAfterReset(() => {
|
#settingsMenu .zm_nml_icontainer {
|
||||||
window.zoom_level = 1
|
align-self: center
|
||||||
rescale()
|
}
|
||||||
})
|
|
||||||
|
|
||||||
|
#settingsMenu .zm_nml_setting {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 7em 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#settingsMenu .zm_nml_setting span {
|
||||||
|
input {
|
||||||
|
width: 2em;
|
||||||
|
margin-right: 4px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
|
border-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sorry, firefox users */
|
||||||
|
input::-webkit-inner-spin-button,
|
||||||
|
input::-webkit-outer-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 0.75em;
|
||||||
|
|
||||||
|
background: #000 url(${nlist_spinner_default}) no-repeat center center;
|
||||||
|
background-size: 100%;
|
||||||
|
border-left: 2px solid var(--theme);
|
||||||
|
|
||||||
|
image-rendering: pixelated;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
input::-webkit-inner-spin-button:hover,
|
||||||
|
input::-webkit-outer-spin-button:active {
|
||||||
|
border-left: 2px solid white;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
var numlist_css_default = CSS;
|
||||||
|
|
||||||
|
// assets/multisetting.css
|
||||||
|
var multisetting_default = '.zm_ms_row {\r\n display: grid;\r\n grid-template-columns: 2.2em 1fr; \r\n}\r\n\r\n.zm_ms_row[data-current="false"] {\r\n .zm_ms_selbtn { color: transparent }\r\n}\r\n\r\n.zm_ms_selbtn.zm_ms_selbtn:not(#_) {\r\n height: 100%;\r\n width: calc(100% - 10px);\r\n\r\n margin-right: 2px;\r\n padding: 0px;\r\n\r\n border: 2px solid var(--theme);\r\n font-size: 1.5em;\r\n}';
|
||||||
|
|
||||||
|
// src/patcher.ts
|
||||||
|
var Patcher = class {
|
||||||
|
zoom_data_div;
|
||||||
|
floater_div;
|
||||||
|
canvas_div;
|
||||||
|
settings;
|
||||||
|
panmode_sel;
|
||||||
|
constructor(settings) {
|
||||||
|
this.settings = settings;
|
||||||
|
const style_div = document.createElement("style");
|
||||||
|
style_div.innerHTML = main_default;
|
||||||
|
document.head.appendChild(style_div);
|
||||||
|
dependOn("betterSettings.js", () => {
|
||||||
|
const style_div2 = document.createElement("style");
|
||||||
|
style_div2.innerHTML = numlist_css_default + multisetting_default;
|
||||||
|
document.head.appendChild(style_div2);
|
||||||
|
});
|
||||||
|
this.canvas_div = document.getElementById("canvasDiv");
|
||||||
|
this.canvas_div.insertAdjacentHTML("beforeend", floater_default);
|
||||||
|
this.floater_div = document.getElementById("zm_floater_container");
|
||||||
|
this.panmode_sel = document.getElementById("zm_panmode_sel");
|
||||||
|
this.panmode_sel.onclick = () => {
|
||||||
|
switch (this.panmode_sel.innerText) {
|
||||||
|
case "C":
|
||||||
|
this.panmode_sel.innerText = "F";
|
||||||
|
break;
|
||||||
|
case "F":
|
||||||
|
this.panmode_sel.innerText = "U";
|
||||||
|
break;
|
||||||
|
case "U":
|
||||||
|
this.panmode_sel.innerText = "C";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.zoom_data_div = document.createElement("div");
|
||||||
|
this.zoom_data_div.id = "zm_data_div";
|
||||||
|
document.getElementById("logDiv")?.prepend(this.zoom_data_div);
|
||||||
|
document.getElementById("controlsTable")?.lastElementChild?.insertAdjacentHTML("beforebegin", ctrl_info_default);
|
||||||
|
this.update_from_settings();
|
||||||
runAfterLoad(() => {
|
runAfterLoad(() => {
|
||||||
patch_keybinds()
|
const cb = this.update_from_settings.bind(this);
|
||||||
patch_ui()
|
for (const elem of document.querySelectorAll("#betterSettings\\/div\\/zoom\\.js span.setting-span input")) {
|
||||||
})
|
elem.addEventListener(elem.classList.contains("toggleInput") ? "click" : "change", cb);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
update_from_settings() {
|
||||||
|
this.floater_div.style.display = this.settings.show_floater.value ? "grid" : "none";
|
||||||
|
this.canvas_div.style.backgroundColor = this.settings.canvas_bkg.value ?? "#252525";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// src/main.ts
|
||||||
|
dependOn("betterSettings.js", () => {
|
||||||
|
const on_change = { cb: () => {
|
||||||
|
} };
|
||||||
|
const settings_manager = new CustomSettingsManager(on_change);
|
||||||
|
const patcher = new Patcher(settings_manager);
|
||||||
|
const handler = new Handler(settings_manager, patcher);
|
||||||
|
}, true);
|
||||||
|
})();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,322 @@
|
||||||
|
const zoom_levels = [
|
||||||
|
0.5,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
6,
|
||||||
|
12
|
||||||
|
]
|
||||||
|
window.zoom_data_div = null
|
||||||
|
window.zoom_level = 1
|
||||||
|
window.zoom_panning = [0,0]
|
||||||
|
|
||||||
|
let colour_setting;
|
||||||
|
|
||||||
|
dependOn("betterSettings.js", () => {
|
||||||
|
const settings_tab = new SettingsTab("zoom.js");
|
||||||
|
colour_setting = new Setting(
|
||||||
|
"Canvas background",
|
||||||
|
"canvas_bkg",
|
||||||
|
settingType.COLOR,
|
||||||
|
false,
|
||||||
|
defaultValue="#252525"
|
||||||
|
);
|
||||||
|
|
||||||
|
settings_tab.registerSettings(undefined, colour_setting)
|
||||||
|
settingsManager.registerTab(settings_tab)
|
||||||
|
})
|
||||||
|
|
||||||
|
function handle_zoom(direction){
|
||||||
|
switch (direction){
|
||||||
|
case "in":
|
||||||
|
if (!(zoom_level+1 in zoom_levels)) { break; }
|
||||||
|
window.zoom_level += 1
|
||||||
|
break;
|
||||||
|
case "out":
|
||||||
|
if (!(zoom_level-1 in zoom_levels)) { break; }
|
||||||
|
window.zoom_level -= 1
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rescale()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_pan(direction, speed){
|
||||||
|
switch (direction){
|
||||||
|
case "right":
|
||||||
|
zoom_panning[0] -= speed
|
||||||
|
break;
|
||||||
|
case "left":
|
||||||
|
zoom_panning[0] += speed
|
||||||
|
break;
|
||||||
|
case "up":
|
||||||
|
zoom_panning[1] += speed
|
||||||
|
break;
|
||||||
|
case "down":
|
||||||
|
zoom_panning[1] -= speed
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rescale()
|
||||||
|
}
|
||||||
|
|
||||||
|
function gen_button(row, col, html, click, nopos, id){
|
||||||
|
const elem = document.createElement("button")
|
||||||
|
|
||||||
|
|
||||||
|
if (!nopos){
|
||||||
|
elem.style.gridColumn = row
|
||||||
|
elem.style.gridRow = col
|
||||||
|
}
|
||||||
|
if (id) { elem.id = id }
|
||||||
|
|
||||||
|
// Table for the data-pos to assign (row major). If null, don't add.
|
||||||
|
const data_pos_map = [
|
||||||
|
["tl", null, "tr"],
|
||||||
|
[null, null, null],
|
||||||
|
["bl", null, "br"]
|
||||||
|
]
|
||||||
|
|
||||||
|
elem.innerHTML = html
|
||||||
|
elem.onclick = click
|
||||||
|
|
||||||
|
if (data_pos_map[row-1][col-1] !== null) {
|
||||||
|
elem.dataset.pos = data_pos_map[row-1][col-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return elem
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_css(){
|
||||||
|
const CSS = `
|
||||||
|
#zm_data_div { margin-bottom: 10px }
|
||||||
|
#canvasDiv { overflow: hidden; background-color: var(--opac-85) }
|
||||||
|
|
||||||
|
@media(pointer=coarse){
|
||||||
|
#zm_floater_container#zm_floater_container {
|
||||||
|
width: 40%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
#zm_floater_container:has(#zm_collapse[data-collapsed="true"]){
|
||||||
|
width: calc(40% / 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(pointer:coarse) and (orientation:landscape){
|
||||||
|
#zm_floater_container#zm_floater_container {
|
||||||
|
width: auto;
|
||||||
|
top: 5px;
|
||||||
|
}
|
||||||
|
#zm_floater_container:has(#zm_collapse[data-collapsed="true"]){
|
||||||
|
width: calc(40% / 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#colorSelector { z-index: 1; right: 5px }
|
||||||
|
#zm_floater_container {
|
||||||
|
position: absolute;
|
||||||
|
display: grid;
|
||||||
|
|
||||||
|
right: 5px;
|
||||||
|
bottom: 5px;
|
||||||
|
height: 100px;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
|
||||||
|
max-width: 200px;
|
||||||
|
max-height: 200px;
|
||||||
|
|
||||||
|
border: 2px solid white;
|
||||||
|
background-color: black;
|
||||||
|
font-size: 120%;
|
||||||
|
|
||||||
|
button { text-align: center; border: 0px solid white }
|
||||||
|
|
||||||
|
button:where([data-pos="tl"]) { border-width: 0px 2px 2px 0px };
|
||||||
|
button:where([data-pos="tr"]) { border-width: 2px 2px 0px 0px };
|
||||||
|
button:where([data-pos="bl"]) { border-width: 0px 0px 2px 2px };
|
||||||
|
button:where([data-pos="br"]) { border-width: 2px 0px 0px 2px };
|
||||||
|
}
|
||||||
|
#zm_floater_container:has(#zm_collapse[data-collapsed="true"]) {
|
||||||
|
height: 50px;
|
||||||
|
|
||||||
|
button:not(#zm_collapse) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#canvasDiv:has(#colorSelector[style *= "block"]) #zm_floater_container {
|
||||||
|
bottom: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.zm_corner { border: 2px solid white; }
|
||||||
|
|
||||||
|
#zm_collapse {
|
||||||
|
grid-row: 3;
|
||||||
|
grid-column: 3;
|
||||||
|
}
|
||||||
|
#zm_collapse[data-collapsed="true"] {
|
||||||
|
grid-row: 1;
|
||||||
|
grid-column: 1;
|
||||||
|
border-width: 0px;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const style_div = document.createElement("style")
|
||||||
|
style_div.innerHTML = CSS
|
||||||
|
|
||||||
|
document.head.appendChild(style_div)
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_zoom_floaters(){
|
||||||
|
const container = document.createElement("div")
|
||||||
|
container.id = "zm_floater_container"
|
||||||
|
|
||||||
|
// Pan mode selector (C: Coarse F: Fine)
|
||||||
|
const pan_mode_sel = gen_button(
|
||||||
|
1,3, "C",
|
||||||
|
(evt) => {
|
||||||
|
evt.target.dataset.mode = evt.target.dataset.mode == "F" ? "C" : "F"
|
||||||
|
evt.target.innerText = evt.target.dataset.mode
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
"zm_panmode_sel"
|
||||||
|
)
|
||||||
|
|
||||||
|
const speed = () =>
|
||||||
|
(window.zoom_level > 3 ? 5 : 10) * // More granular at higher zoom levels
|
||||||
|
(pan_mode_sel.dataset.mode == "F" ? 0.25 : 1) // Increase granularity in fine mode
|
||||||
|
|
||||||
|
container.append(
|
||||||
|
// Direction buttons
|
||||||
|
gen_button(2,1, "↑", () => handle_pan("up" ,speed())),
|
||||||
|
gen_button(1,2, "←", () => handle_pan("left" ,speed())),
|
||||||
|
gen_button(3,2, "→", () => handle_pan("right" ,speed())),
|
||||||
|
gen_button(2,3, "↓", () => handle_pan("down" ,speed())),
|
||||||
|
|
||||||
|
// Zoom buttons
|
||||||
|
gen_button(1,1, "+", () => handle_zoom("in")),
|
||||||
|
gen_button(3,1, "-", () => handle_zoom("out")),
|
||||||
|
|
||||||
|
// Collapse button
|
||||||
|
gen_button(
|
||||||
|
3,3, "#",
|
||||||
|
(evt) => {
|
||||||
|
evt.target.dataset.collapsed = evt.target.dataset.collapsed == "true"
|
||||||
|
? "false"
|
||||||
|
: "true"
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
"zm_collapse"
|
||||||
|
),
|
||||||
|
pan_mode_sel
|
||||||
|
)
|
||||||
|
|
||||||
|
const canvas_div = document.getElementById("canvasDiv")
|
||||||
|
canvas_div.style.backgroundColor = colour_setting?.value ?? "#252525"
|
||||||
|
canvas_div.appendChild(container)
|
||||||
|
}
|
||||||
|
|
||||||
|
function rescale(){
|
||||||
|
log_info()
|
||||||
|
|
||||||
|
const scale = zoom_levels[zoom_level]
|
||||||
|
const x = zoom_panning[0] * (pixelSize * scale)
|
||||||
|
const y = zoom_panning[1] * (pixelSize * scale)
|
||||||
|
|
||||||
|
gameCanvas.style.transform = `translate(${x}px, ${y}px) translateX(-50%) scale(${scale})`
|
||||||
|
}
|
||||||
|
|
||||||
|
function log_info(){
|
||||||
|
// Values are negated to make them more intuitive
|
||||||
|
const x_pan = (-zoom_panning[0]).toString().padEnd(4)
|
||||||
|
const y_pan = (-zoom_panning[1]).toString().padEnd(4)
|
||||||
|
|
||||||
|
if (zoom_data_div === null){ return; }
|
||||||
|
|
||||||
|
zoom_data_div.innerText = ""
|
||||||
|
zoom_data_div.innerText += `Scale: ${zoom_levels[zoom_level]}x\n`
|
||||||
|
zoom_data_div.innerText += `Pan : ${x_pan}, ${y_pan}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function patch_keybinds(){
|
||||||
|
// Be more granular at higher zoom levels
|
||||||
|
const speed_a = () => zoom_level > 3 ? 5 : 10
|
||||||
|
const speed_b = () => zoom_level > 3 ? 10 : 20
|
||||||
|
|
||||||
|
keybinds["9"] = () => handle_zoom("in")
|
||||||
|
keybinds["0"] = () => handle_zoom("out")
|
||||||
|
|
||||||
|
keybinds["w"] = () => handle_pan("up", speed_a())
|
||||||
|
keybinds["a"] = () => handle_pan("left", speed_a())
|
||||||
|
keybinds["s"] = () => handle_pan("down", speed_a())
|
||||||
|
keybinds["d"] = () => handle_pan("right", speed_a())
|
||||||
|
|
||||||
|
keybinds["W"] = () => handle_pan("up", speed_b())
|
||||||
|
keybinds["A"] = () => handle_pan("left", speed_b())
|
||||||
|
keybinds["S"] = () => handle_pan("down", speed_b())
|
||||||
|
keybinds["D"] = () => handle_pan("right", speed_b())
|
||||||
|
}
|
||||||
|
|
||||||
|
function patch_ui(){
|
||||||
|
add_css()
|
||||||
|
add_zoom_floaters()
|
||||||
|
|
||||||
|
zoom_data_div = document.createElement("div")
|
||||||
|
zoom_data_div.id = "zm_data_div"
|
||||||
|
document.getElementById("logDiv").prepend(zoom_data_div)
|
||||||
|
|
||||||
|
const controls_table = document.getElementById("controlsTable").lastElementChild
|
||||||
|
controls_table.insertAdjacentHTML("beforeBegin",`
|
||||||
|
<tr>
|
||||||
|
<td>Zoom in/out</td>
|
||||||
|
<td>
|
||||||
|
<kbd>9</kbd>/
|
||||||
|
<kbd>0</kbd>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Pan</td>
|
||||||
|
<td>
|
||||||
|
<kbd>W</kbd>
|
||||||
|
<kbd>A</kbd>
|
||||||
|
<kbd>S</kbd>
|
||||||
|
<kbd>D</kbd>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Pan (fast)</td>
|
||||||
|
<td>
|
||||||
|
<kbd>Shift</kbd> +
|
||||||
|
<kbd>W</kbd>
|
||||||
|
<kbd>A</kbd>
|
||||||
|
<kbd>S</kbd>
|
||||||
|
<kbd>D</kbd>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redefine to give correct numbers when zoomed
|
||||||
|
window.getMousePos = (canvas, evt) => {
|
||||||
|
if (evt.touches) {
|
||||||
|
evt.preventDefault();
|
||||||
|
evt = evt.touches[0];
|
||||||
|
isMobile = true;
|
||||||
|
}
|
||||||
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
|
||||||
|
let x = (evt.clientX - rect.left) / zoom_levels[zoom_level];
|
||||||
|
let y = (evt.clientY - rect.top) / zoom_levels[zoom_level];
|
||||||
|
|
||||||
|
x = Math.floor((x / canvas.clientWidth) * (width+1));
|
||||||
|
y = Math.floor((y / canvas.clientHeight) * (height+1));
|
||||||
|
|
||||||
|
return {x:x, y:y};
|
||||||
|
}
|
||||||
|
|
||||||
|
runAfterReset(() => {
|
||||||
|
window.zoom_level = 1
|
||||||
|
rescale()
|
||||||
|
})
|
||||||
|
|
||||||
|
runAfterLoad(() => {
|
||||||
|
patch_keybinds()
|
||||||
|
patch_ui()
|
||||||
|
})
|
||||||
Loading…
Reference in New Issue