This commit is contained in:
Kai Vogelgesang 2020-03-21 12:59:31 +01:00
commit a368f3074f
2 changed files with 326 additions and 0 deletions

325
index.html Normal file
View File

@ -0,0 +1,325 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>wallpaper lol</title>
<link rel="stylesheet" href="mini-default.min.css">
<style>
body {
background-color: #363636;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Cg fill-rule='evenodd'%3E%3Cg fill='%238d8d8d' fill-opacity='0.18'%3E%3Cpath opacity='.5' d='M96 95h4v1h-4v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9zm-1 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9z'/%3E%3Cpath d='M6 5V0H5v5H0v1h5v94h1V6h94V5H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
}
</style>
<body>
<div class="row">
<main class="col-lg-2">
<div class="card">
<h4 class="section double-padded">Settings</h4>
<form>
<div class="input-group vertical">
<label for="global_width_cm">Global width (cm):</label>
<input type="number" id="global_width_cm" value="300" step="0.1"
autocomplete="off">
<label for="global_height_cm">Global height (cm):</label>
<input type="number" id="global_height_cm" value="200" step="0.1"
autocomplete="off">
<label for="img_upload">Image File:</label>
<input id="img_upload" type="file" accept=".jpg, .jpeg, .png">
<label for="img_width_cm">Image width (cm):</label>
<input type="number" id="img_width_cm" step="0.1" autocomplete="off">
<label for="img_height_cm">Image height (cm):</label>
<input type="number" id="img_height_cm" step="0.1" autocomplete="off">
<label for="img_offset_top">Image top offset (cm):</label>
<input type="number" id="img_offset_top" step="0.1" autocomplete="off">
<label for="img_offset_left">Image left offset (cm):</label>
<input type="number" id="img_offset_left" step="0.1" autocomplete="off">
</div>
</form>
</div>
</main>
<main class="col-lg-8">
<div class="card fluid">
<h2 class="section double-padded">Multimonitor Wallpaper Memes</h2>
<div class="section">
<div style="position: relative">
<canvas id="canvas" style="width: 100%" width="3000" height="2000"></canvas>
<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.8)"></div>
<div id="monitor_images">
</div>
</div>
</div>
</div>
</main>
<main class="col-lg-2" style="height: 100vh">
<template id="monitor_settings_template">
<div class="card fluid">
<h2 class="section">Monitor</h2>
<form>
<div class="input-group vertical">
<label>
Aspect Ratio:
<input type="search" name="aspect_ratio" autocomplete="off" value="16:9"
list="ratio_suggestions">
</label>
<datalist id="ratio_suggestions">
<option value="16:9"></option>
<option value="16:10"></option>
<option value="2560:1080"></option>
<option value="4:3"></option>
<option value="5:4"></option>
</datalist>
<label>
Screen diagonal (cm):
<input type="number" name="diagonal_cm" step="0.1" value="61" autocomplete="off">
</label>
<label>
Offset top (cm):
<input type="number" name="offset_top_cm" step="0.1" value="0" autocomplete="off">
</label>
<label>
Offset left (cm):
<input type="number" name="offset_left_cm" step="0.1" value="0" autocomplete="off">
</label>
<button type="button" name="get_image_button">Get Image</button>
</div>
</form>
</div>
</template>
<div id="monitors_column" style="max-height: 90%; overflow: auto"></div>
<div style="text-align: center">
<button id="add_monitor_button">Add Monitor</button>
</div>
</main>
</div>
<script>
let pixel_per_cm = 1,
img,
monitors = [];
let canvas = document.getElementById("canvas"),
global_width_cm = document.getElementById("global_width_cm"),
global_height_cm = document.getElementById("global_height_cm"),
img_width_cm = document.getElementById("img_width_cm"),
img_height_cm = document.getElementById("img_height_cm"),
img_offset_top = document.getElementById("img_offset_top"),
img_offset_left = document.getElementById("img_offset_left"),
img_upload = document.getElementById("img_upload");
canvas.getContext("2d").fillRect(0, 0, canvas.width, canvas.height);
const update_hash = () => {
location.hash = global_width_cm.value + "x" + global_height_cm.value;
for (let monitor of monitors) {
location.hash += ","
+ monitor.aspect_ratio.value
+ "/" + monitor.diagonal_cm.value
+ "/" + monitor.offset_top_cm.value
+ "/" + monitor.offset_left_cm.value
}
};
const redraw = () => {
canvas.width = global_width_cm.value * pixel_per_cm;
canvas.height = global_height_cm.value * pixel_per_cm;
let ctx = canvas.getContext("2d");
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (img) {
ctx.drawImage(
img,
img_offset_left.value * pixel_per_cm, img_offset_top.value * pixel_per_cm,
img_width_cm.value * pixel_per_cm, img_height_cm.value * pixel_per_cm
);
}
for (let monitor of monitors) monitor.redraw();
};
const round_to_float = (x) => {
return Math.round(x * 10) / 10;
};
const upload_image = (e) => {
let reader = new FileReader(),
f = e.target.files.item(0);
reader.onload = (e) => {
let new_img = new Image();
new_img.setAttribute("src", e.target.result.toString());
new_img.addEventListener("load", () => {
img = new_img;
if ((img.width / img.height < global_width_cm.value / global_height_cm.value)) {
// image is wider
img_width_cm.value = global_width_cm.value;
img_height_cm.value = round_to_float(img.height / img.width * img_width_cm.value);
img_offset_left.value = 0;
img_offset_top.value = round_to_float((global_height_cm.value - img_height_cm.value) / 2);
} else {
// canvas is wider
img_height_cm.value = global_height_cm.value;
img_width_cm.value = round_to_float(img.width / img.height * img_height_cm.value);
img_offset_top.value = 0;
img_offset_left.value = round_to_float((global_width_cm.value - img_width_cm.value) / 2);
}
pixel_per_cm = new_img.width / img_width_cm.value;
redraw();
});
};
reader.readAsDataURL(f);
};
img_upload.addEventListener("change", upload_image);
global_width_cm.addEventListener("input", () => {
redraw();
update_hash();
});
global_height_cm.addEventListener("input", () => {
redraw();
update_hash();
});
img_width_cm.addEventListener("input", () => {
img_height_cm.value = round_to_float(img.height / img.width * img_width_cm.value);
redraw();
});
img_height_cm.addEventListener("input", () => {
img_width_cm.value = round_to_float(img.width / img.height * img_height_cm.value);
redraw();
});
img_offset_top.addEventListener("input", redraw);
img_offset_left.addEventListener("input", redraw);
let add_monitor_button = document.getElementById("add_monitor_button"),
monitor_setings_template = document.getElementById("monitor_settings_template"),
monitors_column = document.getElementById("monitors_column"),
monitor_images = document.getElementById("monitor_images");
const add_monitor = () => {
let monitor_id = monitors.length + 1;
let settings_clone = document.importNode(monitor_setings_template.content, true);
let monitor_canvas = document.createElement("canvas");
monitor_canvas.style.position = "absolute";
monitor_canvas.style.top = "0";
monitor_canvas.style.left = "0";
monitor_canvas.draggable = true;
monitor_canvas.style.outline = "2px solid grey";
let aspect_ratio = settings_clone.querySelector('[name="aspect_ratio"]'),
diagonal_cm = settings_clone.querySelector('[name="diagonal_cm"]'),
offset_top_cm = settings_clone.querySelector('[name="offset_top_cm"]'),
offset_left_cm = settings_clone.querySelector('[name="offset_left_cm"]'),
get_image_button = settings_clone.querySelector('[name="get_image_button"]');
settings_clone.querySelector('h2').innerText += ' ' + monitor_id;
let redraw_monitor = () => {
let [width, height] = aspect_ratio.value.split(":");
let alpha = Math.atan2(height, width);
let width_cm = diagonal_cm.value * Math.cos(alpha),
height_cm = diagonal_cm.value * Math.sin(alpha);
let width_percent = round_to_float(100 * width_cm / global_width_cm.value),
height_percent = round_to_float(100 * height_cm / global_height_cm.value),
offset_top_percent = round_to_float(100 * offset_top_cm.value / global_height_cm.value),
offset_left_percent = round_to_float(100 * offset_left_cm.value / global_width_cm.value);
monitor_canvas.width = width_cm * pixel_per_cm;
monitor_canvas.height = height_cm * pixel_per_cm;
monitor_canvas.getContext("2d").drawImage(
canvas,
offset_left_cm.value * pixel_per_cm, offset_top_cm.value * pixel_per_cm,
width_cm * pixel_per_cm, height_cm * pixel_per_cm,
0, 0,
monitor_canvas.width, monitor_canvas.height
);
monitor_canvas.style.width = width_percent + "%";
monitor_canvas.style.height = height_percent + "%";
monitor_canvas.style.top = offset_top_percent + "%";
monitor_canvas.style.left = offset_left_percent + "%";
};
redraw_monitor();
const input_handler = () => {
redraw_monitor();
update_hash();
};
aspect_ratio.addEventListener('change', input_handler);
diagonal_cm.addEventListener('input', input_handler);
offset_top_cm.addEventListener('input', input_handler);
offset_left_cm.addEventListener('input', input_handler);
get_image_button.addEventListener('click', () => {
console.log(monitor_canvas.toDataURL());
let temp_a = document.createElement('a');
temp_a.setAttribute('href', monitor_canvas.toDataURL());
temp_a.setAttribute('download', 'monitor_' + monitor_id + '.png');
temp_a.style.display = 'none';
document.body.appendChild(temp_a);
temp_a.click();
document.body.removeChild(temp_a);
});
let current_monitor = {
aspect_ratio: aspect_ratio,
diagonal_cm: diagonal_cm,
offset_top_cm: offset_top_cm,
offset_left_cm: offset_left_cm,
canvas: monitor_canvas,
redraw: redraw_monitor,
};
monitors.push(current_monitor);
monitors_column.appendChild(settings_clone);
monitor_images.appendChild(monitor_canvas);
return current_monitor;
};
add_monitor_button.addEventListener("click", add_monitor);
if (location.hash !== "") {
let [global_hash, ...monitor_hashes] = location.hash.substr(1).split(",");
[global_width_cm.value, global_height_cm.value] = global_hash.split("x");
for (let monitor_hash of monitor_hashes) {
let monitor = add_monitor();
[monitor.aspect_ratio.value, monitor.diagonal_cm.value, monitor.offset_top_cm.value, monitor.offset_left_cm.value] = monitor_hash.split("/");
}
redraw();
}
</script>
</body>
</html>

1
mini-default.min.css vendored Normal file

File diff suppressed because one or more lines are too long